import Foundation extension RDEPUBParser { public func readingProfile() -> RDEPUBReadingProfile { if metadata.layout == .fixed { return .webFixedLayout } return hasInteractiveContent() ? .webInteractive : .textReflowable } public func hasInteractiveContent() -> Bool { let interactiveMediaTypes = [ "application/javascript", "text/javascript", "application/ecmascript" ] if manifest.values.contains(where: { item in interactiveMediaTypes.contains(item.mediaType.lowercased()) || item.properties.contains("scripted") }) { return true } for item in spine where item.linear && (item.mediaType.contains("html") || item.mediaType.contains("xhtml")) { guard let html = htmlString(forRelativePath: item.href) else { continue } if containsInteractiveMarkup(html) { return true } } return false } private func containsInteractiveMarkup(_ html: String) -> Bool { let interactivePattern = #"<(script|iframe|video|audio|canvas|object|embed)\b|\bon(load|click|touchstart|touchend|mouseover|submit|change|input)=|hype_generated_script|swiper|webview"# if html.range(of: interactivePattern, options: [.regularExpression, .caseInsensitive]) != nil { return true } let dynamicSVGPattern = #"]*>[\s\S]*?(<(script|foreignObject|animate|animateMotion|animateTransform|set)\b|\bon(load|click|touchstart|touchend|mouseover)=)"# return html.range(of: dynamicSVGPattern, options: [.regularExpression, .caseInsensitive]) != nil } }