{- Copyright (C) 2013-2015 Dr. Alistair Ward This file is part of WeekDaze. WeekDaze is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. WeekDaze is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with WeekDaze. If not, see . -} {- | [@AUTHOR@] Dr. Alistair Ward [@DESCRIPTION@] Exports labels used to name CSS-classes, & functions to encode arbitrary CSS-identifiers. -} module WeekDaze.Text.CSS( -- * Types -- ** Type-synonyms CSSIdentifier, -- * Constants boxCSSIdentifier, cssSuffix, consoleOutputCSSIdentifier, dataCSSIdentifier, numericDataCSSIdentifier, infoCSSIdentifier, warningCSSIdentifier, observerSummaryCSSIdentifier, timetableViewCSSIdentifier, -- validIdentifierCharacterSet, -- * Functions -- ** Constructor mkIdentifier ) where import qualified Data.Char import qualified Data.Set import qualified System.FilePath -- | The suffix used for CSS-files. cssSuffix :: System.FilePath.FilePath cssSuffix = "css" -- | Merely aids self-documentation. type CSSIdentifier = String -- | A CSS class-label, for an HTML /box/. boxCSSIdentifier :: CSSIdentifier boxCSSIdentifier = "box" -- | A CSS class-label, denoting console-output. consoleOutputCSSIdentifier :: CSSIdentifier consoleOutputCSSIdentifier = "consoleOutput" -- | A CSS class-label, for arbitrary data. dataCSSIdentifier :: CSSIdentifier dataCSSIdentifier = "data" -- | A CSS class-label, for arbitrary data. numericDataCSSIdentifier :: CSSIdentifier numericDataCSSIdentifier = "numericData" -- | A CSS class-label, denoting some form of informative message. infoCSSIdentifier :: CSSIdentifier infoCSSIdentifier = "info" -- | A CSS class-label, denoting some form of warning message. warningCSSIdentifier :: CSSIdentifier warningCSSIdentifier = "warning" -- | A CSS class-label, for the individual /timetable/ for a week, as seen by some observer. observerSummaryCSSIdentifier :: CSSIdentifier observerSummaryCSSIdentifier = "observerSummary" -- | A CSS class-label, for a view of a /timetable/ for the week. timetableViewCSSIdentifier :: CSSIdentifier timetableViewCSSIdentifier = "timetableView" {- | * An identifier in CSS, can only be composed from a restricted character-set. * CAVEAT: valid HTML-identifiers also include ':' & '.'. -} validIdentifierCharacterSet :: Data.Set.Set Char validIdentifierCharacterSet = Data.Set.unions [ Data.Set.fromDistinctAscList ['a' .. 'z'], Data.Set.fromDistinctAscList ['A' .. 'Z'], Data.Set.fromDistinctAscList ['0' .. '9'], Data.Set.fromList "-_" ] {- | * Normalises an arbitrary string into a valid CSS-identifier. * -} mkIdentifier :: String -> CSSIdentifier mkIdentifier s = removeIllegalPrefix $ filter (`Data.Set.member` validIdentifierCharacterSet) s where removeIllegalPrefix :: String -> String removeIllegalPrefix ('-' : s'@('-' : _)) = removeIllegalPrefix {-recurse-} s' -- It can't start with a double hyphen. removeIllegalPrefix s'@('-' : (x : xs)) | Data.Char.isDigit x = removeIllegalPrefix {-recurse-} xs -- It can't start with either, a hyphen followed by a digit, or with a digit. | otherwise = s' -- Terminate. removeIllegalPrefix s'@(x : xs) | Data.Char.isDigit x = removeIllegalPrefix {-recurse-} xs -- It can't start with a digit. | otherwise = s' -- Terminate. removeIllegalPrefix [] = error $ "WeekDaze.Text.CSS.mkIdentifier:\tnormalisation of '" ++ s ++ "', resulted in a null string."