gitit (empty) → 0.1.0.1
raw patch · 21 files changed
+2662/−0 lines, 21 filesdep +Cryptodep +HAppS-Datadep +HAppS-Serversetup-changedbinary-added
Dependencies added: Crypto, HAppS-Data, HAppS-Server, HAppS-State, HTTP, base, bytestring, cgi, containers, directory, filepath, highlighting-kate, mtl, network, old-time, pandoc, parsec, pretty, process, utf8-string, xhtml
Files
- Gitit.hs +812/−0
- Gitit/Git.hs +207/−0
- Gitit/State.hs +177/−0
- LICENSE +340/−0
- README.markdown +207/−0
- Setup.lhs +3/−0
- data/FrontPage.page +32/−0
- data/Help.page +273/−0
- data/SampleConfig.hs +14/−0
- data/post-update +85/−0
- gitit.cabal +43/−0
- javascripts/dragdiff.js +21/−0
- javascripts/folding.js +28/−0
- javascripts/jquery-ui-personalized-1.6rc2.min.js +79/−0
- javascripts/jquery.min.js +32/−0
- javascripts/preview.js +8/−0
- javascripts/search.js +13/−0
- stylesheets/folder.png binary
- stylesheets/gitit.css +268/−0
- stylesheets/hk-pyg.css +20/−0
- stylesheets/page.png binary
+ Gitit.hs view
@@ -0,0 +1,812 @@+{-# LANGUAGE CPP #-}+{-+Copyright (C) 2008 John MacFarlane <jgm@berkeley.edu>++This program 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 2 of the License, or+(at your option) any later version.++This program 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 this program; if not, write to the Free Software+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA+-}++module Main where++import HAppS.Server+import HAppS.State hiding (Method)+import System.Environment+import System.IO.UTF8+import System.IO (stderr)+import Prelude hiding (writeFile, readFile, putStrLn, putStr)+import System.Process+import System.Directory+import Control.Concurrent+import System.FilePath+import Gitit.Git+import Gitit.State+import Text.XHtml hiding ( (</>), dir, method, password )+import qualified Text.XHtml as X ( password, method )+import Data.List (intersect, intersperse, intercalate, sort, nub, sortBy, isSuffixOf)+import Data.Maybe (fromMaybe, fromJust, mapMaybe, isNothing, isJust)+import Data.Ord (comparing)+import qualified Data.Digest.SHA512 as SHA512 (hash)+import Paths_gitit+import Text.Pandoc+import Text.Pandoc.Definition (processPandoc)+import Text.Pandoc.Shared (HTMLMathMethod(..))+import Data.ByteString.Internal (c2w)+import Data.Char (isAlphaNum, isAlpha)+import Codec.Binary.UTF8.String (decodeString)+import Control.Monad.Reader+import qualified Data.ByteString.Lazy as B+import Network.HTTP (urlEncodeVars, urlEncode)+import System.Console.GetOpt+import System.Exit++gititVersion :: String+gititVersion = "0.1.0.1"++main :: IO ()+main = do+ argv <- getArgs+ options <- parseArgs argv+ conf <- foldM handleFlag defaultConfig options+ gitPath <- findExecutable "git"+ when (isNothing gitPath) $ error "'git' program not found in system path."+ initializeWiki (repositoryPath conf) (staticDir conf)+ control <- startSystemState entryPoint+ update $ SetConfig conf+ hPutStrLn stderr $ "Starting server on port " ++ show (portNumber conf)+ let debugger = if (debugMode conf) then debugFilter else id+ tid <- forkIO $ simpleHTTP (Conf { port = portNumber conf }) $ debugger $+ [ dir "stylesheets" [ fileServe [] $ (staticDir conf) </> "stylesheets" ]+ , dir "images" [ fileServe [] $ (staticDir conf) </> "images" ]+ , dir "javascripts" [ fileServe [] $ (staticDir conf) </> "javascripts" ]+ ] ++ wikiHandlers ++ [ fileServe [] (repositoryPath conf) ]+ waitForTermination+ putStrLn "Shutting down..."+ killThread tid+ createCheckpoint control+ shutdownSystem control+ putStrLn "Shutdown complete"++data Opt+ = Help+ | ConfigFile FilePath+ | Version+ deriving (Eq)++flags :: [OptDescr Opt]+flags =+ [ Option ['h'] [] (NoArg Help)+ "Print this help message"+ , Option ['v'] [] (NoArg Version)+ "Print version information"+ , Option ['f'] [] (ReqArg ConfigFile "FILE")+ "Specify configuration file"+ ]++parseArgs :: [String] -> IO [Opt]+parseArgs argv = do+ progname <- getProgName+ case getOpt Permute flags argv of+ (opts,_,[]) -> return opts+ (_,_,errs) -> hPutStrLn stderr (concat errs ++ usageInfo (usageHeader progname) flags) >>+ exitWith (ExitFailure 1)++usageHeader :: String -> String+usageHeader progname = "Usage: " ++ progname ++ " [opts...]"++copyrightMessage :: String+copyrightMessage = "\nCopyright (C) 2008 John MacFarlane\n" +++ "This is free software; see the source for copying conditions. There is no\n" +++ "warranty, not even for merchantability or fitness for a particular purpose."++handleFlag :: Config -> Opt -> IO Config+handleFlag _ opt = do+ progname <- getProgName+ case opt of+ Help -> hPutStrLn stderr (usageInfo (usageHeader progname) flags) >> exitWith ExitSuccess+ Version -> hPutStrLn stderr (progname ++ " version " ++ gititVersion ++ copyrightMessage) >> exitWith ExitSuccess+ ConfigFile f -> do readFile f >>= return . read++entryPoint :: Proxy AppState+entryPoint = Proxy++-- | Create repository and public directories, unless they already exist.+initializeWiki :: FilePath -> FilePath -> IO ()+initializeWiki repodir staticdir = do+ repoExists <- doesDirectoryExist repodir+ unless repoExists $ do+ postupdatepath <- getDataFileName $ "data" </> "post-update"+ postupdatecontents <- B.readFile postupdatepath+ welcomepath <- getDataFileName $ "data" </> "FrontPage.page"+ welcomecontents <- B.readFile welcomepath+ createDirectory repodir+ oldDir <- getCurrentDirectory+ setCurrentDirectory repodir+ runCommand "git init" >>= waitForProcess+ -- add welcome page+ B.writeFile "Front Page.page" welcomecontents+ runCommand "git add 'Front Page.page'; git commit -m 'Initial commit of front page.'" >>= waitForProcess+ -- set post-update hook so working directory will be updated+ -- when changes are pushed to the repo+ let postupdate = ".git" </> "hooks" </> "post-update"+ B.writeFile postupdate postupdatecontents+ perms <- getPermissions postupdate+ setPermissions postupdate (perms {executable = True})+ hPutStrLn stderr $ "Created repository " ++ repodir+ setCurrentDirectory oldDir+ staticExists <- doesDirectoryExist staticdir+ unless staticExists $ do+ createDirectoryIfMissing True $ staticdir </> "stylesheets"+ let stylesheets = map ("stylesheets" </>) ["gitit.css", "hk-pyg.css", "folder.png", "page.png"]+ stylesheetpaths <- mapM getDataFileName stylesheets+ zipWithM copyFile stylesheetpaths (map (staticdir </>) stylesheets)+ createDirectoryIfMissing True $ staticdir </> "javascripts"+ let javascripts = ["jquery.min.js", "jquery-ui-personalized-1.6rc2.min.js",+ "folding.js", "dragdiff.js", "preview.js", "search.js"]+ javascriptpaths <- mapM getDataFileName $ map ("javascripts" </>) javascripts+ zipWithM copyFile javascriptpaths $ map ((staticdir </> "javascripts") </>) javascripts+ hPutStrLn stderr $ "Created " ++ staticdir ++ " directory"+ jsMathExists <- doesDirectoryExist (staticdir </> "javascripts" </> "jsMath")+ unless jsMathExists $ do+ hPutStrLn stderr $ replicate 80 '*' +++ "\nWarning: jsMath not found.\n" +++ "If you want support for math, copy the jsMath directory into " ++ staticdir ++ "/javascripts/\n" +++ "jsMath can be obtained from http://www.math.union.edu/~dpvc/jsMath/\n" +++ replicate 80 '*'++type Handler = ServerPart Response+++wikiHandlers :: [Handler]+wikiHandlers = [ dir "_index" [ handle GET indexPage ]+ , dir "_activity" [ handle GET showActivity ]+ , dir "_help" [ handle GET helpPage ]+ , dir "_preview" [ handle POST preview ]+ , dir "_search" [ handle POST searchResults ]+ , dir "_register" [ handle GET registerUserForm,+ handle POST registerUser ]+ , dir "_login" [ handle GET loginUserForm,+ handle POST loginUser ]+ , dir "_logout" [ handle GET logoutUser ]+ , dir "_upload" [ handle GET uploadForm,+ handle POST uploadFile ]+ , handleCommand "showraw" GET showRawPage+ , handleCommand "history" GET showPageHistory+ , handleCommand "edit" GET editPage+ , handleCommand "diff" GET showDiff+ , handleCommand "cancel" POST showPage+ , handleCommand "update" POST updatePage+ , handleCommand "delete" GET confirmDelete+ , handleCommand "delete" POST deletePage+ , handle GET showPage+ ]++data Params = Params { pUsername :: String+ , pPassword :: String+ , pPassword2 :: String+ , pRevision :: String+ , pDestination :: String+ , pForUser :: String+ , pSince :: String+ , pRaw :: String+ , pLimit :: Int+ , pPatterns :: [String]+ , pEditedText :: Maybe String+ , pMessages :: [String]+ , pFrom :: String+ , pTo :: String+ , pSHA1 :: String+ , pLogMsg :: String+ , pEmail :: String+ , pAccessCode :: String+ , pWikiname :: String+ , pOverwrite :: Bool+ , pFilename :: String+ , pFileContents :: B.ByteString+ , pSessionKey :: Maybe SessionKey+ } deriving Show++instance FromData Params where+ fromData = do+ un <- look "username" `mplus` return ""+ pw <- look "password" `mplus` return ""+ p2 <- look "password2" `mplus` return ""+ rv <- look "revision" `mplus` return "HEAD"+ fu <- look "forUser" `mplus` return ""+ si <- look "since" `mplus` return ""+ ds <- look "destination" `mplus` return ""+ ra <- look "raw" `mplus` return ""+ lt <- look "limit" `mplus` return "100"+ pa <- look "patterns" `mplus` return ""+ me <- lookRead "messages" `mplus` return [] + fm <- look "from" `mplus` return "HEAD"+ to <- look "to" `mplus` return "HEAD"+ et <- (look "editedText" >>= return . Just . filter (/= '\r')) `mplus` return Nothing+ sh <- look "sha1" `mplus` return ""+ lm <- look "logMsg" `mplus` return ""+ em <- look "email" `mplus` return ""+ wn <- look "wikiname" `mplus` return ""+ ow <- (look "overwrite" >>= return . (== "yes")) `mplus` return False+ fn <- (lookInput "file" >>= return . fromMaybe "" . inputFilename) `mplus` return ""+ fc <- (lookInput "file" >>= return . inputValue) `mplus` return B.empty+ ac <- look "accessCode" `mplus` return ""+ sk <- (readCookieValue "sid" >>= return . Just) `mplus` return Nothing+ return $ Params { pUsername = un+ , pPassword = pw+ , pPassword2 = p2+ , pRevision = rv+ , pForUser = fu+ , pSince = si+ , pDestination = ds+ , pRaw = ra+ , pLimit = read lt+ , pPatterns = words pa+ , pMessages = me+ , pFrom = fm+ , pTo = to+ , pEditedText = et+ , pSHA1 = sh+ , pLogMsg = lm+ , pEmail = em+ , pWikiname = wn+ , pOverwrite = ow+ , pFilename = fn+ , pFileContents = fc+ , pAccessCode = ac+ , pSessionKey = sk }++getLoggedInUser :: MonadIO m => Params -> m (Maybe String)+getLoggedInUser params = do+ mbSd <- maybe (return Nothing) ( query . GetSession ) $ pSessionKey params+ let user = case mbSd of+ Nothing -> Nothing+ Just sd -> Just $ sessionUser sd+ return $! user++data Command = Command (Maybe String)++commandList :: [String]+commandList = ["edit", "showraw", "history", "diff", "cancel", "update", "delete"]++instance FromData Command where+ fromData = do+ pairs <- lookPairs+ return $ case (map fst pairs) `intersect` commandList of+ [] -> Command Nothing+ (c:_) -> Command $ Just c++handle :: Method -> (String -> Params -> Web Response) -> Handler+handle meth responder = uriRest $ \uri -> let uriPath = drop 1 $ takeWhile (/='?') uri+ in if isPage uriPath+ then withData $ \params ->+ [ withRequest $ \req -> if rqMethod req == meth+ then responder uriPath params+ else noHandle ]+ else anyRequest noHandle++handleCommand :: String -> Method -> (String -> Params -> Web Response) -> Handler+handleCommand command meth responder =+ withData $ \com -> case com of+ Command (Just c) | c == command -> [ handle meth responder ]+ _ -> []++orIfNull :: String -> String -> String+orIfNull str backup = if null str then backup else str++isPage :: String -> Bool+isPage ('_':_) = False+isPage s = '.' `notElem` s++urlForPage :: String -> String+urlForPage page = "/" ++ urlEncode page++pathForPage :: String -> FilePath+pathForPage page = page <.> "page"++withCommands :: Method -> [String] -> (String -> Request -> Web Response) -> Handler+withCommands meth commands page = withRequest $ \req -> do+ if rqMethod req /= meth+ then noHandle+ else if all (`elem` (map fst $ rqInputs req)) commands+ then page (intercalate "/" $ rqPaths req) req+ else noHandle++helpPage :: String -> Params -> Web Response+helpPage _ params = do+ helpText <- liftIO $ getDataFileName ("data" </> "Help.page") >>= readFile+ helpHtml <- convertToHtml helpText+ formattedPage [HidePageControls] ["jsMath/easy/load.js"] "Help" params helpHtml++showRawPage :: String -> Params -> Web Response+showRawPage page params = do+ let revision = pRevision params+ rawContents <- gitCatFile revision (pathForPage page)+ case rawContents of+ Just c -> ok $ toResponse c+ _ -> noHandle++showPage :: String -> Params -> Web Response+showPage "" params = showPage "Front Page" params >>= seeOther "/Front%20Page"+showPage page params = do+ let revision = pRevision params+ rawContents <- gitCatFile revision (pathForPage page)+ case rawContents of+ Just c -> do+ cont <- convertToHtml c+ let cont' = thediv ! [identifier "wikipage",+ strAttr "onDblClick" ("window.location = '" ++ urlForPage page ++ "?edit&revision=" ++ revision +++ (if revision == "HEAD" then "" else "&" ++ urlEncodeVars [("logMsg", "Revert to " ++ revision)]) ++ "';")] << cont+ formattedPage [] ["jsMath/easy/load.js"] page params cont'+ _ -> if revision == "HEAD"+ then editPage page params+ else error $ "Invalid revision: " ++ revision++validate :: [(Bool, String)] -- ^ list of conditions and error messages+ -> [String] -- ^ list of error messages+validate = foldl go []+ where go errs (condition, msg) = if condition then msg:errs else errs++uploadForm :: String -> Params -> Web Response+uploadForm _ params = do+ let origPath = pFilename params+ let wikiname = pWikiname params `orIfNull` takeFileName origPath+ let logMsg = pLogMsg params+ let upForm = form ! [X.method "post", enctype "multipart/form-data"] <<+ [ p << [label << "File to upload:", br, afile "file" ! [value origPath]]+ , p << [label << "Name on wiki, including extension:", br, textfield "wikiname" ! [value wikiname],+ primHtmlChar "nbsp", checkbox "overwrite" "yes", label << "Overwrite existing file"]+ , p << [label << "Description of content or changes:", br, textfield "logMsg" ! [size "60", value logMsg],+ submit "upload" "Upload"] ]+ user <- getLoggedInUser params+ if isJust user+ then formattedPage [HidePageControls] [] "File upload" params upForm+ else seeOther ("/_login?" ++ urlEncodeVars [("destination", "_upload")]) $ toResponse $ p << "You must be logged in to upload a file."++uploadFile :: String -> Params -> Web Response+uploadFile _ params = do+ let origPath = pFilename params+ let fileContents = pFileContents params+ let wikiname = pWikiname params `orIfNull` takeFileName origPath+ let logMsg = pLogMsg params+ user <- getLoggedInUser params+ if isJust user+ then do+ cfg <- query GetConfig+ let author = fromJust user+ let email = ""+ let overwrite = pOverwrite params+ exists <- liftIO $ doesFileExist (repositoryPath cfg </> wikiname)+ let errors = validate [ (null logMsg, "Description cannot be empty.")+ , (null origPath, "File not found.")+ , (not overwrite && exists, "A file named '" ++ wikiname +++ "' already exists in the repository: choose a new name " +++ "or check the box to overwrite the existing file existing file.")+ , (B.length fileContents > fromIntegral (maxUploadSize cfg),+ "File exceeds maximum upload size.")+ , (isPage wikiname,+ "Uploaded file name must have an appropriate extension.")+ ]+ if null errors+ then do+ if B.length fileContents > fromIntegral (maxUploadSize cfg)+ then error "File exceeds maximum upload size"+ else return ()+ let dir' = takeDirectory wikiname+ liftIO $ createDirectoryIfMissing True ((repositoryPath cfg) </> dir')+ liftIO $ B.writeFile ((repositoryPath cfg) </> wikiname) fileContents+ gitCommit wikiname (author, email) logMsg+ formattedPage [HidePageControls] [] "File upload" params $+ p << ("Uploaded " ++ show (B.length fileContents) ++ " bytes")+ else uploadForm "File upload" (params { pMessages = errors })+ else seeOther ("/_login?" ++ urlEncodeVars [("destination", "_upload")]) $ toResponse $ p << "You must be logged in to upload a file."++searchResults :: String -> Params -> Web Response+searchResults _ params = do+ let patterns = pPatterns params+ let limit = pLimit params+ if null patterns+ then noHandle+ else do+ matchLines <- gitGrep patterns >>= return . map parseMatchLine . take limit . lines+ let matchedFiles = nub $ filter (".page" `isSuffixOf`) $ map fst matchLines+ let matches = map (\f -> (f, mapMaybe (\(a,b) -> if a == f then Just b else Nothing) matchLines)) matchedFiles+ let preamble = if null matches+ then h3 << ["No matches found for '", unwords patterns, "':"]+ else h3 << [(show $ length matches), " matches found for '", unwords patterns, "':"]+ let htmlMatches = preamble +++ olist << map+ (\(file, contents) -> li << [anchor ! [href $ urlForPage $ takeBaseName file] << takeBaseName file,+ stringToHtml (" (" ++ show (length contents) ++ " matching lines)"),+ stringToHtml " ", anchor ! [href "#", theclass "showmatch", thestyle "display: none;"] << "[show matches]",+ pre ! [theclass "matches"] << unlines contents])+ (reverse $ sortBy (comparing (length . snd)) matches)+ formattedPage [HidePageControls] ["search.js"] "Search Results" params htmlMatches++-- Auxiliary function for searchResults+parseMatchLine :: String -> (String, String)+parseMatchLine matchLine =+ let (file, rest) = break (==':') matchLine+ contents = drop 1 rest -- strip off colon+ in (file, contents)++preview :: String -> Params -> Web Response+preview _ params = convertToHtml (pRaw params) >>= ok . toResponse++showPageHistory :: String -> Params -> Web Response+showPageHistory page params = do+ let since = pSince params `orIfNull` "1 year ago"+ hist <- gitLog since "" [pathForPage page]+ let versionToHtml entry pos = li ! [theclass "difflink", intAttr "order" pos, strAttr "revision" $ logRevision entry] <<+ [thespan ! [theclass "date"] << logDate entry, stringToHtml " (",+ thespan ! [theclass "author"] << anchor ! [href $ "/_activity?" ++ urlEncodeVars [("forUser", logAuthor entry)]] <<+ (logAuthor entry), stringToHtml ")", stringToHtml ": ",+ anchor ! [href (urlForPage page ++ "?revision=" ++ logRevision entry)] <<+ thespan ! [theclass "subject"] << logSubject entry,+ noscript << ([stringToHtml " [compare with ",+ anchor ! [href $ urlForPage page ++ "?diff&from=" ++ logRevision entry +++ "^&to=" ++ logRevision entry] << "previous"] +++ (if pos /= 1+ then [primHtmlChar "nbsp", primHtmlChar "bull",+ primHtmlChar "nbsp",+ anchor ! [href $ urlForPage page ++ "?diff&from=" +++ logRevision entry ++ "&to=HEAD"] << "current" ]+ else []) +++ [stringToHtml "]"])]+ let contents = ulist ! [theclass "history"] << zipWith versionToHtml hist [(length hist), (length hist - 1)..1]+ formattedPage [] ["dragdiff.js"] page params contents++showActivity :: String -> Params -> Web Response+showActivity _ params = do+ let since = pSince params `orIfNull` "1 month ago"+ let forUser = pForUser params+ hist <- gitLog since forUser []+ let filesFor files = intersperse (primHtmlChar "nbsp") $ map+ (\file -> anchor ! [href $ urlForPage file ++ "?history"] << file) $ map+ (\file -> if ".page" `isSuffixOf` file then dropExtension file else file) files+ let contents = ulist ! [theclass "history"] << map (\entry -> li <<+ [thespan ! [theclass "date"] << logDate entry, stringToHtml " (",+ thespan ! [theclass "author"] << anchor ! [href $ "/_activity?" ++ urlEncodeVars [("forUser", logAuthor entry)]] <<+ (logAuthor entry), stringToHtml "):",+ thespan ! [theclass "subject"] << logSubject entry, stringToHtml " (",+ thespan ! [theclass "files"] << filesFor (logFiles entry),+ stringToHtml ")"]) hist+ formattedPage [HidePageControls] [] ("Recent changes" ++ if null forUser then "" else (" by " ++ forUser)) params contents++showDiff :: String -> Params -> Web Response+showDiff page params = do+ let from = pFrom params+ let to = pTo params+ rawDiff <- gitDiff (pathForPage page) from to+ let diffLineToHtml l = case head l of+ '+' -> thespan ! [theclass "added"] << [tail l, "\n"]+ '-' -> thespan ! [theclass "deleted"] << [tail l, "\n"]+ _ -> thespan << [tail l, "\n"]+ let formattedDiff = thespan ! [theclass "detail"] << ("Changes from " ++ from) ++++ pre ! [theclass "diff"] << map diffLineToHtml (drop 5 $ lines rawDiff)+ formattedPage [] [] page (params { pRevision = to }) formattedDiff++editPage :: String -> Params -> Web Response+editPage page params = do+ user <- getLoggedInUser params+ if isJust user+ then do+ let revision = pRevision params+ let messages = pMessages params+ rawContents <- case pEditedText params of+ Nothing -> gitCatFile revision (pathForPage page)+ Just t -> return $ Just t+ let (new, contents) = case rawContents of+ Nothing -> (True, "# Title goes here\n\nContent goes here")+ Just c -> (False, c)+ let messages' = if new+ then ("This page does not yet exist. You may create it by editing the text below." : messages)+ else messages+ sha1 <- case (pSHA1 params) of+ "" -> gitGetSHA1 (pathForPage page) >>= return . fromMaybe ""+ s -> return s+ let logMsg = pLogMsg params+ let sha1Box = textfield "sha1" ! [thestyle "display: none", value sha1]+ let editForm = gui (urlForPage page) ! [identifier "editform"] <<+ [sha1Box,+ textarea ! [cols "80", name "editedText", identifier "editedText"] << contents,+ label << "Description of changes:", br,+ textfield "logMsg" ! [size "76", value logMsg],+ submit "update" "Save", primHtmlChar "nbsp",+ submit "cancel" "Discard", br,+ thediv ! [ identifier "previewpane" ] << noHtml ]+ formattedPage [HidePageControls, HideNavbar] ["preview.js"] page (params {pMessages = messages'}) editForm+ else do+ seeOther ("/_login?" ++ urlEncodeVars [("destination", page ++ "?edit")]) $ toResponse $ p << "You must be logged in to edit a page."++confirmDelete :: String -> Params -> Web Response+confirmDelete page params = do+ let confirmForm = gui "" <<+ [ p << "Are you sure you want to delete this page?"+ , submit "confirm" "Yes, delete it!"+ , stringToHtml " "+ , submit "cancel" "No, keep it!"+ , br ]+ user <- getLoggedInUser params+ if isJust user+ then formattedPage [HidePageControls] [] page params confirmForm+ else seeOther ("/_login?" ++ urlEncodeVars [("destination", page ++ "?delete")]) $ toResponse $ p << "You must be logged in to delete a page."++deletePage :: String -> Params -> Web Response+deletePage page params = do+ user <- getLoggedInUser params+ if isNothing user+ then fail "User must be logged in to delete page."+ else return ()+ let author = fromJust user+ let email = ""+ gitRemove (pathForPage page) (author, email) "Deleted from web."+ seeOther "/" $ toResponse $ p << "Page deleted"++updatePage :: String -> Params -> Web Response+updatePage page params = do+ user <- getLoggedInUser params+ if isNothing user+ then fail "User must be logged in to update page."+ else return ()+ let editedText = case pEditedText params of+ Nothing -> error "No body text in POST request"+ Just b -> b+ let author = fromJust user+ let email = ""+ let logMsg = pLogMsg params+ let oldSHA1 = pSHA1 params+ if null logMsg+ then editPage page (params { pMessages = ["Description cannot be empty."] })+ else do+ cfg <- query GetConfig+ if length editedText > fromIntegral (maxUploadSize cfg)+ then error "Page exceeds maximum size."+ else return ()+ currentSHA1 <- gitGetSHA1 (pathForPage page) >>= return . fromMaybe ""+ -- check SHA1 in case page has been modified, merge+ if currentSHA1 == oldSHA1+ then do+ let dir' = takeDirectory page+ liftIO $ createDirectoryIfMissing True ((repositoryPath cfg) </> dir')+ liftIO $ writeFile ((repositoryPath cfg) </> pathForPage page) editedText+ gitCommit (pathForPage page) (author, email) logMsg+ seeOther (urlForPage page) $ toResponse $ p << "Page updated"+ else do -- there have been conflicting changes+ original <- gitCatFile oldSHA1 (pathForPage page) >>= return . fromJust+ latest <- gitCatFile currentSHA1 (pathForPage page) >>= return . fromJust+ let pagePath = repositoryPath cfg </> pathForPage page+ let [textTmp, originalTmp, latestTmp] = map (pagePath ++) [".edited",".original",".latest"]+ let editedText' = if null editedText || last editedText == '\n' then editedText else editedText ++ "\n"+ liftIO $ writeFile textTmp editedText'+ liftIO $ writeFile originalTmp original+ liftIO $ writeFile latestTmp latest+ mergeText <- gitMergeFile (pathForPage page ++ ".edited") (pathForPage page ++ ".original") (pathForPage page ++ ".latest")+ liftIO $ mapM removeFile [textTmp, originalTmp, latestTmp]+ let mergeMsg = "The page has been edited since you checked it out. " +++ "Changes have been merged into your edits below. " +++ "Please resolve conflicts and Save."+ editPage page (params { pEditedText = Just mergeText+ , pRevision = "HEAD"+ , pSHA1 = currentSHA1+ , pMessages = [mergeMsg] })++indexPage :: String -> Params -> Web Response+indexPage _ params = do+ let revision = pRevision params+ files <- gitLsTree revision >>= return . map (unwords . drop 3 . words) . lines+ let htmlIndex = fileListToHtml "/" $ map splitPath $ sort files+ formattedPage [HidePageControls] ["folding.js"] "index" params htmlIndex++-- | Map a list of nonempty lists onto a list of pairs of list heads and list of tails.+-- e.g. [[1,2],[1],[2,1]] -> [(1,[[2],[]]), (2,[[1]])]+consolidateHeads :: Eq a => [[a]] -> [(a,[[a]])]+consolidateHeads lst =+ let heads = nub $ map head lst+ tailsFor h = map tail [l | l <- lst, head l == h]+ in map (\h -> (h, tailsFor h)) heads++-- | Create a hierarchical ordered list (with links) for a list of files+fileListToHtml :: String -> [[FilePath]] -> Html+fileListToHtml prefix lst = ulist ! [identifier "index", theclass "folding"] <<+ (map (\(h, l) -> let h' = if ".page" `isSuffixOf` h then dropExtension h else h+ in if [] `elem` l+ then li ! [theclass $ if isPage h' then "page" else "upload"] << anchor ! [href $ prefix ++ h'] << h'+ else li ! [theclass "folder"] << [stringToHtml h', fileListToHtml (prefix ++ h') l]) $+ consolidateHeads lst)++-- | Convert links with no URL to wikilinks, if their labels are all strings and spaces+convertWikiLinks :: Inline -> Inline+convertWikiLinks (Link ref ("", "")) | all isStringOrSpace ref =+ Link ref (refToUrl ref, "Go to wiki page")+convertWikiLinks x = x++isStringOrSpace :: Inline -> Bool+isStringOrSpace Space = True+isStringOrSpace (Str _) = True+isStringOrSpace _ = False++refToUrl :: [Inline] -> String+refToUrl ((Str x):xs) = x ++ refToUrl xs+refToUrl (Space:xs) = "%20" ++ refToUrl xs+refToUrl (_:_) = error "Encountered an inline other than Str or Space"+refToUrl [] = ""++-- | Converts markdown string to HTML.+convertToHtml :: MonadIO m => String -> m Html+convertToHtml text' = do+ cfg <- query GetConfig+ let pandocContents = readMarkdown (defaultParserState { stateSanitizeHTML = True, stateSmart = True }) $+ filter (/= '\r') $ decodeString $ text'+ let htmlContents = writeHtml (defaultWriterOptions { writerStandalone = False+ , writerHTMLMathMethod = JsMath (Just "/javascripts/jsMath/easy/load.js")+ , writerTableOfContents = tableOfContents cfg+ }) $ processPandoc convertWikiLinks pandocContents+ return htmlContents++data PageOption = HidePageControls | HideNavbar deriving (Eq, Show)++-- | Returns formatted page+formattedPage :: [PageOption] -> [String] -> String -> Params -> Html -> Web Response+formattedPage opts scripts page params htmlContents = do+ let revision = pRevision params+ user <- getLoggedInUser params+ cfg <- (query GetConfig)+ let stylesheetlinks = thelink ! [href "/stylesheets/gitit.css", rel "stylesheet",+ strAttr "media" "screen", thetype "text/css"] << noHtml ++++ thelink ! [href "/stylesheets/hk-pyg.css", rel "stylesheet",+ strAttr "media" "screen", thetype "text/css"] << noHtml ++++ thelink ! [href "/stylesheets/gitit-print.css", rel "stylesheet",+ strAttr "media" "print", thetype "text/css"] << noHtml+ let javascriptlinks = if null scripts+ then noHtml+ else concatHtml $ map+ (\x -> script ! [src ("/javascripts/" ++ x), thetype "text/javascript"] << noHtml)+ (["jquery.min.js", "jquery-ui-personalized-1.6rc2.min.js"] ++ scripts)+ let title' = thetitle << (wikiTitle cfg ++ " - " ++ page)+ let head' = header << [title', stylesheetlinks, javascriptlinks]+ let sitenav = thediv ! [theclass "sitenav"] <<+ gui ("/_search") ! [identifier "searchform"] <<+ [ anchor ! [href "/Front%20Page", theclass "nav_link"] << "front"+ , primHtmlChar "bull"+ , anchor ! [href "/_index", theclass "nav_link"] << "index"+ , primHtmlChar "bull"+ , anchor ! [href "/_upload", theclass "nav_link"] << "upload"+ , primHtmlChar "bull"+ , anchor ! [href "/_activity", theclass "nav_link"] << "activity"+ , primHtmlChar "bull"+ , anchor ! [href "/_help", theclass "nav_link"] << "help"+ , primHtmlChar "nbsp"+ , textfield "patterns" ! [theclass "search_field search_term"]+ , submit "search" "Search" ]+ let buttons = [ anchor ! [href $ urlForPage page ++ "?revision=" ++ revision ++ "&showraw", theclass "nav_link"] << "raw"+ , primHtmlChar "bull"+ , anchor ! [href $ urlForPage page ++ "?delete", theclass "nav_link"] << "delete"+ , primHtmlChar "bull"+ , anchor ! [href $ urlForPage page ++ "?revision=" ++ revision ++ "&history", theclass "nav_link"] << "history"+ , primHtmlChar "bull"+ , anchor ! [href $ urlForPage page ++ "?edit&revision=" ++ revision +++ if revision == "HEAD" then "" else "&" ++ urlEncodeVars [("logMsg", "Revert to " ++ revision)],+ theclass "nav_link"] << if revision == "HEAD" then "edit" else "revert" ]+ let userbox = thediv ! [identifier "userbox"] <<+ case user of+ Just u -> anchor ! [href ("/_logout?" ++ urlEncodeVars [("destination", page)]), theclass "nav_link"] << ("logout " ++ u)+ Nothing -> (anchor ! [href ("/_login?" ++ urlEncodeVars [("destination", page)]), theclass "nav_link"] << "login") ++++ primHtmlChar "bull" ++++ anchor ! [href ("/_register?" ++ urlEncodeVars [("destination", page)]), theclass "nav_link"] << "register"+ let sitenavVis = if HideNavbar `elem` opts then "hidden" else "visible"+ let sidebarVis = if HidePageControls `elem` opts then "hidden" else "visible"+ let messages = pMessages params+ let htmlMessages = if null messages+ then noHtml+ else ulist ! [theclass "messages"] << map (li <<) messages+ let body' = body << thediv ! [identifier "container"] <<+ [ thediv ! [identifier "banner"] << primHtml (wikiBanner cfg)+ , thediv ! [identifier "navbar", thestyle $ "visibility: " ++ sitenavVis] << [userbox, sitenav]+ , thediv ! [identifier "pageTitle"] << [ anchor ! [href $ urlForPage page] << (h1 << page) ]+ , thediv ! [identifier "content"] << [htmlMessages, htmlContents]+ , thediv ! [identifier "pageinfo"] << [ thediv ! [theclass "pageControls", thestyle $ "visibility: " ++ sidebarVis] << buttons+ , thediv ! [theclass "details"] << revision ]+ , thediv ! [identifier "footer"] << primHtml (wikiFooter cfg)+ ]+ ok $ toResponse $ head' +++ body'++-- user authentication+loginForm :: Params -> Html+loginForm params =+ let destination = pDestination params+ in gui "" ! [identifier "loginForm"] <<+ [ textfield "sha1" ! [thestyle "display: none", value destination]+ , label << "Username ", textfield "username" ! [size "15"], stringToHtml " "+ , label << "Password ", X.password "password" ! [size "15"], stringToHtml " "+ , submit "login" "Login"+ , p << [ stringToHtml "If you do not have an account, "+ , anchor ! [href ("/_register?" ++ urlEncodeVars [("destination", destination)])] << "click here to register." ]]++loginUserForm :: String -> Params -> Web Response+loginUserForm _ params = formattedPage [HidePageControls] [] "Login" params $ loginForm params++loginUser :: String -> Params -> Web Response+loginUser _ params = do+ let uname = pUsername params+ let pword = pPassword params+ let destination = pDestination params+ cfg <- query GetConfig+ let passwordHash = SHA512.hash $ map c2w $ passwordSalt cfg ++ pword+ allowed <- query $ AuthUser uname passwordHash+ if allowed+ then do+ key <- update $ NewSession (SessionData uname)+ addCookie (3600) (mkCookie "sid" (show key))+ seeOther ("/" ++ intercalate "%20" (words destination)) $ toResponse $ p << ("Welcome, " ++ uname)+ else+ formattedPage [HidePageControls] [] "Login" (params { pMessages = ["Authentication failed."] }) (loginForm params)++logoutUser :: String -> Params -> Web Response+logoutUser _ params = do+ let key = pSessionKey params+ let destination = pDestination params+ case key of+ Just k -> update $ DelSession k+ Nothing -> return ()+ seeOther ("/" ++ intercalate "%20" (words destination)) $ toResponse $ p << "You have been logged out."++registerForm :: Web Html+registerForm = do+ cfg <- query GetConfig+ let accessQ = case accessQuestion cfg of+ Nothing -> noHtml+ Just (prompt, _) -> label << prompt +++ br ++++ X.password "accessCode" ! [size "15"] +++ br+ return $ gui "" ! [identifier "loginForm"] <<+ [ accessQ,+ label << "Username (at least 3 letters or digits):", br,+ textfield "username" ! [size "15"], stringToHtml " ", br,+ textfield "email" ! [size "15", theclass "req"],+ label << "Password (at least 6 characters, including at least one non-letter):", br,+ X.password "password" ! [size "15"], stringToHtml " ", br,+ label << "Confirm Password:", br, X.password "password2" ! [size "15"], stringToHtml " ", br,+ submit "register" "Register" ]++registerUserForm :: String -> Params -> Web Response+registerUserForm _ params = do+ regForm <- registerForm+ formattedPage [HidePageControls] [] "Register" params regForm++registerUser :: String -> Params -> Web Response+registerUser _ params = do+ regForm <- registerForm+ let isValidUsername u = length u >= 3 && all isAlphaNum u+ let isValidPassword pw = length pw >= 6 && not (all isAlpha pw)+ let accessCode = pAccessCode params+ let uname = pUsername params+ let pword = pPassword params+ let pword2 = pPassword2 params+ let fakeField = pEmail params+ taken <- query $ IsUser uname+ cfg <- query GetConfig+ let isValidAccessCode = case accessQuestion cfg of+ Nothing -> True+ Just (_, answers) -> accessCode `elem` answers+ let errors = validate [ (taken, "Sorry, that username is already taken.")+ , (not isValidAccessCode, "Incorrect response to access prompt.")+ , (not (isValidUsername uname), "Username must be at least 3 charcaters, all letters or digits.")+ , (not (isValidPassword pword), "Password must be at least 6 characters, with at least one non-letter.")+ , (pword /= pword2, "Password does not match confirmation.")+ , (not (null fakeField), "You do not seem human enough.") ] -- fakeField is hidden in CSS (honeypot)+ if null errors+ then do+ let passwordHash = SHA512.hash $ map c2w $ passwordSalt cfg ++ pword+ update $ AddUser uname (User { username = uname, password = passwordHash })+ loginUser "Front Page" (params { pUsername = uname, pPassword = pword, pDestination = "Front Page" })+ else formattedPage [HidePageControls] [] "Register" (params { pMessages = errors }) regForm++
+ Gitit/Git.hs view
@@ -0,0 +1,207 @@+{-+Copyright (C) 2008 John MacFarlane <jgm@berkeley.edu>++This program 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 2 of the License, or+(at your option) any later version.++This program 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 this program; if not, write to the Free Software+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA+-}++{- Auxiliary functions for running git commands -}++module Gitit.Git+ ( runGitCommand+ , gitLastCommitHash+ , gitLog+ , gitLsTree+ , gitGrep+ , gitCatFile+ , gitDiff+ , gitCommit+ , gitRemove+ , gitGetSHA1+ , gitMergeFile+ , LogEntry (..) )+where++import Control.Monad (unless)+import Control.Monad.Trans+import Network.CGI (urlEncode)+import System.FilePath+import System.Exit+import System.Process+import qualified Text.ParserCombinators.Parsec as P+import qualified Data.ByteString.Lazy as B+import System.Directory+import System.IO (openTempFile)+import Data.ByteString.Lazy.UTF8 (toString)+import HAppS.State+import Gitit.State++-- | Run shell command and return error status, standard output, and error output.+runShellCommand :: FilePath -> Maybe [(String, String)] -> String -> [String] -> IO (ExitCode, String, String)+runShellCommand workingDir environment command optionList = do+ tempPath <- getTemporaryDirectory+ (outputPath, hOut) <- openTempFile tempPath "out"+ (errorPath, hErr) <- openTempFile tempPath "err"+ hProcess <- runProcess command optionList (Just workingDir) environment Nothing (Just hOut) (Just hErr)+ status <- waitForProcess hProcess+ errorOutput <- B.readFile errorPath >>= return . toString+ output <- B.readFile outputPath >>= return . toString+ return (status, errorOutput, output)++-- | Run git command and return error status, standard output, and error output. The repository+-- is used as working directory.+runGitCommand :: MonadIO m => String -> [String] -> m (ExitCode, String, String)+runGitCommand command args = do+ repo <- (query GetConfig) >>= return . repositoryPath+ liftIO $ runShellCommand repo Nothing "git" (command : args)++-- | Return SHA1 hash of last commit for filename.+gitLastCommitHash :: MonadIO m => String -> m (Maybe String)+gitLastCommitHash filename = do+ (status, _, output) <- runGitCommand "log" $ ["--pretty=format:%H", "--"] ++ [filename]+ let outputWords = words output+ if status == ExitSuccess && not (null outputWords)+ then return $ Just $ head outputWords+ else return Nothing++-- | Return list of log entries for the given time frame and commit author.+-- If author is null, return entries for all authors.+gitLog :: MonadIO m => String -> String -> [String] -> m [LogEntry]+gitLog since author files = do+ (status, err, output) <- runGitCommand "whatchanged" $ ["--pretty=format:%h%n%cr%n%an%n%s%n"] +++ ["--since='" ++ urlEncode since ++ "'"] +++ (if null author then [] else ["--author=" ++ author]) +++ ["--"] ++ files+ if status == ExitSuccess+ then case P.parse parseGitLog "" output of+ Left err' -> error $ show err'+ Right parsed -> return parsed+ else error $ "git whatchanged returned error status.\n" ++ err++gitLsTree :: MonadIO m => String -> m String+gitLsTree rev = do+ (status, errOutput, output) <- runGitCommand "ls-tree" ["-r", rev]+ if status == ExitSuccess+ then return output+ else error $ "git ls-tree returned error status.\n" ++ errOutput++gitGrep :: MonadIO m => [String] -> m String+gitGrep patterns = do+ (status, errOutput, output) <- runGitCommand "grep" (["--all-match", "--ignore-case", "--word-regexp"] +++ concatMap (\term -> ["-e", term]) patterns)+ if status == ExitSuccess+ then return output+ else error $ "git grep returned error status.\n" ++ errOutput++gitCatFile :: MonadIO m => String -> FilePath -> m (Maybe String)+gitCatFile revision file = do+ (status, _, output) <- runGitCommand "cat-file" ["-p", revision ++ ":" ++ file]+ return $ if status == ExitSuccess+ then Just output+ else Nothing++gitDiff :: MonadIO m+ => String -- ^ Filename+ -> String -- ^ Old version (sha1)+ -> String -- ^ New version (sha1)+ -> m String -- ^ String+gitDiff file from to = do+ repo <- (query GetConfig) >>= return . repositoryPath+ (status, errOut, output) <- liftIO $ runShellCommand repo (Just [("GIT_DIFF_OPTS","-u100000")])+ "git" ["diff", from, to, file]+ if status == ExitSuccess+ then return output+ else error $ "git diff returned error: " ++ errOut++-- | Add and then commit file, raising errors if either step fails.+gitCommit :: MonadIO m => FilePath -> (String, String) -> String -> m ()+gitCommit file (author, email) logMsg = do+ (statusAdd, errAdd, _) <- runGitCommand "add" [file]+ if statusAdd == ExitSuccess+ then do (statusCommit, errCommit, _) <- runGitCommand "commit" ["--author", author ++ " <" +++ email ++ ">", "-m", logMsg]+ if statusCommit == ExitSuccess+ then return ()+ else unless (null errCommit) $ error $ "Could not git commit " ++ file ++ "\n" ++ errCommit+ else error $ "Could not git add " ++ file ++ "\n" ++ errAdd++-- | Remove file from repository and commit, raising errors if either step fails.+gitRemove :: MonadIO m => FilePath -> (String, String) -> String -> m ()+gitRemove file (author, email) logMsg = do+ (statusAdd, errAdd, _) <- runGitCommand "rm" [file]+ if statusAdd == ExitSuccess+ then do (statusCommit, errCommit, _) <- runGitCommand "commit" ["--author", author ++ " <" +++ email ++ ">", "-m", logMsg]+ if statusCommit == ExitSuccess+ then return ()+ else unless (null errCommit) $ error $ "Could not git commit " ++ file ++ "\n" ++ errCommit+ else error $ "Could not git rm " ++ file ++ "\n" ++ errAdd++gitGetSHA1 :: MonadIO m => FilePath -> m (Maybe String)+gitGetSHA1 file = do+ (status, _, out) <- runGitCommand "log" ["-n", "1", "--pretty=oneline", file]+ if status == ExitSuccess && length out > 0+ then return $ Just $ head $ words out+ else return $ Nothing++gitMergeFile :: MonadIO m => FilePath -> FilePath -> FilePath -> m String+gitMergeFile edited original latest = do+ (status, err, out) <- runGitCommand "merge-file" ["--stdout", edited, original, latest]+ case status of+ ExitSuccess -> return out+ ExitFailure n | n >= 0 -> return out -- indicates number of merge conflicts+ _ -> error $ "git merge-file returned an error.\n" ++ err++--+-- Parsers to parse git log into LogEntry records.+--++-- | Abstract representation of a git log entry.+data LogEntry = LogEntry+ { logRevision :: String+ , logDate :: String+ , logAuthor :: String+ , logSubject :: String+ , logFiles :: [String]+ } deriving (Read, Show)++parseGitLog :: P.Parser [LogEntry]+parseGitLog = P.manyTill gitLogEntry P.eof++wholeLine :: P.GenParser Char st [Char]+wholeLine = P.manyTill P.anyChar P.newline++nonblankLine :: P.GenParser Char st [Char]+nonblankLine = P.notFollowedBy P.newline >> wholeLine++gitLogEntry :: P.Parser LogEntry+gitLogEntry = do+ rev <- nonblankLine+ date <- nonblankLine+ author <- wholeLine+ subject <- P.manyTill wholeLine (P.eof P.<|> (P.lookAhead (P.char ':') >> return ())) >>= return . unlines+ P.spaces+ files <- P.many gitLogChange+ P.spaces+ return $ LogEntry { logRevision = rev,+ logDate = date,+ logAuthor = author,+ logSubject = subject,+ logFiles = files }++gitLogChange :: P.Parser String+gitLogChange = do+ P.char ':'+ line <- nonblankLine+ return $ unwords $ drop 5 $ words line
+ Gitit/State.hs view
@@ -0,0 +1,177 @@+{-# OPTIONS -fglasgow-exts #-}+{-# LANGUAGE TemplateHaskell , FlexibleInstances,+ UndecidableInstances, OverlappingInstances,+ MultiParamTypeClasses, GeneralizedNewtypeDeriving #-}+{-+Copyright (C) 2008 John MacFarlane <jgm@berkeley.edu>++This program 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 2 of the License, or+(at your option) any later version.++This program 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 this program; if not, write to the Free Software+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA+-}++{- Functions for maintaining user list and session state.+ Parts of this code are based on http://hpaste.org/5957 mightybyte rev by + dbpatterson -}++module Gitit.State where++import qualified Data.Map as M+import Control.Monad.Reader+import Control.Monad.State (modify, MonadState)+import Data.Generics hiding ((:+:))+import HAppS.State+import HAppS.Data+import GHC.Conc (STM)+import Codec.Utils (Octet)++-- | Data structure for information read from config file.+data Config = Config {+ repositoryPath :: FilePath, -- path of git repository for pages+ staticDir :: FilePath, -- path of static directory+ wikiBanner :: String, -- HTML to be included at top of pages+ wikiTitle :: String, -- title of wiki + wikiFooter :: String, -- HTML to be included at bottom of pages+ tableOfContents :: Bool, -- should each page have an automatic table of contents?+ maxUploadSize :: Integer, -- maximum size of pages and file uploads+ portNumber :: Int, -- port number to serve content on+ passwordSalt :: String, -- text to serve as salt in encrypting passwords+ debugMode :: Bool, -- should debug info be printed to the console?+ accessQuestion :: Maybe (String, [String]) -- if Nothing, then anyone can register for an account.+ -- if Just (prompt, answers), then a user will be given the prompt+ -- and must give one of the answers in order to register.+ } deriving (Read, Show,Eq,Typeable,Data)++defaultConfig :: Config+defaultConfig = Config {+ repositoryPath = "wikidata",+ staticDir = "static",+ wikiBanner = "",+ wikiTitle = "Wiki",+ wikiFooter = "Powered by Gitit",+ tableOfContents = True,+ maxUploadSize = 100000,+ portNumber = 5001,+ passwordSalt = "l91snthoae8eou2340987",+ debugMode = False,+ accessQuestion = Nothing+ }++type SessionKey = Integer++data SessionData = SessionData {+ sessionUser :: String+} deriving (Read,Show,Eq,Typeable,Data)++data Sessions a = Sessions {unsession::M.Map SessionKey a}+ deriving (Read,Show,Eq,Typeable,Data)++data User = User {+ username :: String,+ password :: [Octet] -- password stored as MD5 hash+} deriving (Show,Read,Typeable,Data)++data AppState = AppState {+ sessions :: Sessions SessionData,+ users :: M.Map String User,+ config :: Config+} deriving (Show,Read,Typeable,Data)++instance Version SessionData+instance Version (Sessions a)+instance Version Config++$(deriveSerialize ''SessionData)+$(deriveSerialize ''Sessions)+$(deriveSerialize ''Config)++instance Version AppState+instance Version User++$(deriveSerialize ''User)+$(deriveSerialize ''AppState)++instance Component AppState where+ type Dependencies AppState = End+ initialValue = AppState {sessions = (Sessions M.empty), users = M.empty, config = defaultConfig}++askUsers :: MonadReader AppState m => m (M.Map String User)+askUsers = return . users =<< ask++askSessions::MonadReader AppState m => m (Sessions SessionData)+askSessions = return . sessions =<< ask++modUsers :: MonadState AppState m => (M.Map String User -> M.Map String User) -> m ()+modUsers f = modify $ \s -> s {users = f $ users s}++modSessions :: MonadState AppState m => (Sessions SessionData -> Sessions SessionData) -> m ()+modSessions f = modify $ \s -> s {sessions = f $ sessions s}++isUser :: MonadReader AppState m => String -> m Bool+isUser name = liftM (M.member name) askUsers++addUser :: MonadState AppState m => String -> User -> m ()+addUser name u = modUsers $ M.insert name u++delUser :: MonadState AppState m => String -> m ()+delUser name = modUsers $ M.delete name++authUser :: MonadReader AppState m => String -> [Octet] -> m Bool+authUser name pass = do+ users' <- askUsers+ case M.lookup name users' of+ Just u -> return $ pass == password u+ Nothing -> return False ++listUsers :: MonadReader AppState m => m [String]+listUsers = liftM M.keys askUsers++numUsers :: MonadReader AppState m => m Int+numUsers = liftM length listUsers++isSession :: MonadReader AppState m => SessionKey -> m Bool+isSession key = liftM ((M.member key) . unsession) askSessions++setSession :: (MonadState AppState m) => SessionKey -> SessionData -> m ()+setSession key u = do+ modSessions $ Sessions . (M.insert key u) . unsession+ return ()++newSession :: (MonadState AppState (Ev (t GHC.Conc.STM)), MonadTrans t, Monad (t GHC.Conc.STM)) =>+ SessionData -> Ev (t GHC.Conc.STM) SessionKey+newSession u = do+ key <- getRandom+ setSession key u+ return key++delSession :: (MonadState AppState m) => SessionKey -> m ()+delSession key = do+ modSessions $ Sessions . (M.delete key) . unsession+ return ()++getSession::SessionKey -> Query AppState (Maybe SessionData)+getSession key = liftM ((M.lookup key) . unsession) askSessions++getConfig :: Query AppState Config+getConfig = return . config =<< ask++setConfig :: MonadState AppState m => Config -> m ()+setConfig conf = modify $ \s -> s {config = conf}++numSessions:: Proxy AppState -> Query AppState Int+numSessions = proxyQuery $ liftM (M.size . unsession) askSessions++$(mkMethods ''AppState ['addUser, 'delUser, 'authUser, 'isUser, 'listUsers, 'numUsers,+ 'isSession, 'setSession, 'getSession, 'newSession, 'delSession, 'numSessions,+ 'setConfig, 'getConfig])+
+ LICENSE view
@@ -0,0 +1,340 @@+ GNU GENERAL PUBLIC LICENSE+ Version 2, June 1991++ Copyright (C) 1989, 1991 Free Software Foundation, Inc.+ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA+ Everyone is permitted to copy and distribute verbatim copies+ of this license document, but changing it is not allowed.++ Preamble++ The licenses for most software are designed to take away your+freedom to share and change it. By contrast, the GNU General Public+License is intended to guarantee your freedom to share and change free+software--to make sure the software is free for all its users. This+General Public License applies to most of the Free Software+Foundation's software and to any other program whose authors commit to+using it. (Some other Free Software Foundation software is covered by+the GNU Library General Public License instead.) You can apply it to+your programs, too.++ When we speak of free software, we are referring to freedom, not+price. Our General Public Licenses are designed to make sure that you+have the freedom to distribute copies of free software (and charge for+this service if you wish), that you receive source code or can get it+if you want it, that you can change the software or use pieces of it+in new free programs; and that you know you can do these things.++ To protect your rights, we need to make restrictions that forbid+anyone to deny you these rights or to ask you to surrender the rights.+These restrictions translate to certain responsibilities for you if you+distribute copies of the software, or if you modify it.++ For example, if you distribute copies of such a program, whether+gratis or for a fee, you must give the recipients all the rights that+you have. You must make sure that they, too, receive or can get the+source code. And you must show them these terms so they know their+rights.++ We protect your rights with two steps: (1) copyright the software, and+(2) offer you this license which gives you legal permission to copy,+distribute and/or modify the software.++ Also, for each author's protection and ours, we want to make certain+that everyone understands that there is no warranty for this free+software. If the software is modified by someone else and passed on, we+want its recipients to know that what they have is not the original, so+that any problems introduced by others will not reflect on the original+authors' reputations.++ Finally, any free program is threatened constantly by software+patents. We wish to avoid the danger that redistributors of a free+program will individually obtain patent licenses, in effect making the+program proprietary. To prevent this, we have made it clear that any+patent must be licensed for everyone's free use or not licensed at all.++ The precise terms and conditions for copying, distribution and+modification follow.++ GNU GENERAL PUBLIC LICENSE+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION++ 0. This License applies to any program or other work which contains+a notice placed by the copyright holder saying it may be distributed+under the terms of this General Public License. The "Program", below,+refers to any such program or work, and a "work based on the Program"+means either the Program or any derivative work under copyright law:+that is to say, a work containing the Program or a portion of it,+either verbatim or with modifications and/or translated into another+language. (Hereinafter, translation is included without limitation in+the term "modification".) Each licensee is addressed as "you".++Activities other than copying, distribution and modification are not+covered by this License; they are outside its scope. The act of+running the Program is not restricted, and the output from the Program+is covered only if its contents constitute a work based on the+Program (independent of having been made by running the Program).+Whether that is true depends on what the Program does.++ 1. You may copy and distribute verbatim copies of the Program's+source code as you receive it, in any medium, provided that you+conspicuously and appropriately publish on each copy an appropriate+copyright notice and disclaimer of warranty; keep intact all the+notices that refer to this License and to the absence of any warranty;+and give any other recipients of the Program a copy of this License+along with the Program.++You may charge a fee for the physical act of transferring a copy, and+you may at your option offer warranty protection in exchange for a fee.++ 2. You may modify your copy or copies of the Program or any portion+of it, thus forming a work based on the Program, and copy and+distribute such modifications or work under the terms of Section 1+above, provided that you also meet all of these conditions:++ a) You must cause the modified files to carry prominent notices+ stating that you changed the files and the date of any change.++ b) You must cause any work that you distribute or publish, that in+ whole or in part contains or is derived from the Program or any+ part thereof, to be licensed as a whole at no charge to all third+ parties under the terms of this License.++ c) If the modified program normally reads commands interactively+ when run, you must cause it, when started running for such+ interactive use in the most ordinary way, to print or display an+ announcement including an appropriate copyright notice and a+ notice that there is no warranty (or else, saying that you provide+ a warranty) and that users may redistribute the program under+ these conditions, and telling the user how to view a copy of this+ License. (Exception: if the Program itself is interactive but+ does not normally print such an announcement, your work based on+ the Program is not required to print an announcement.)++These requirements apply to the modified work as a whole. If+identifiable sections of that work are not derived from the Program,+and can be reasonably considered independent and separate works in+themselves, then this License, and its terms, do not apply to those+sections when you distribute them as separate works. But when you+distribute the same sections as part of a whole which is a work based+on the Program, the distribution of the whole must be on the terms of+this License, whose permissions for other licensees extend to the+entire whole, and thus to each and every part regardless of who wrote it.++Thus, it is not the intent of this section to claim rights or contest+your rights to work written entirely by you; rather, the intent is to+exercise the right to control the distribution of derivative or+collective works based on the Program.++In addition, mere aggregation of another work not based on the Program+with the Program (or with a work based on the Program) on a volume of+a storage or distribution medium does not bring the other work under+the scope of this License.++ 3. You may copy and distribute the Program (or a work based on it,+under Section 2) in object code or executable form under the terms of+Sections 1 and 2 above provided that you also do one of the following:++ a) Accompany it with the complete corresponding machine-readable+ source code, which must be distributed under the terms of Sections+ 1 and 2 above on a medium customarily used for software interchange; or,++ b) Accompany it with a written offer, valid for at least three+ years, to give any third party, for a charge no more than your+ cost of physically performing source distribution, a complete+ machine-readable copy of the corresponding source code, to be+ distributed under the terms of Sections 1 and 2 above on a medium+ customarily used for software interchange; or,++ c) Accompany it with the information you received as to the offer+ to distribute corresponding source code. (This alternative is+ allowed only for noncommercial distribution and only if you+ received the program in object code or executable form with such+ an offer, in accord with Subsection b above.)++The source code for a work means the preferred form of the work for+making modifications to it. For an executable work, complete source+code means all the source code for all modules it contains, plus any+associated interface definition files, plus the scripts used to+control compilation and installation of the executable. However, as a+special exception, the source code distributed need not include+anything that is normally distributed (in either source or binary+form) with the major components (compiler, kernel, and so on) of the+operating system on which the executable runs, unless that component+itself accompanies the executable.++If distribution of executable or object code is made by offering+access to copy from a designated place, then offering equivalent+access to copy the source code from the same place counts as+distribution of the source code, even though third parties are not+compelled to copy the source along with the object code.++ 4. You may not copy, modify, sublicense, or distribute the Program+except as expressly provided under this License. Any attempt+otherwise to copy, modify, sublicense or distribute the Program is+void, and will automatically terminate your rights under this License.+However, parties who have received copies, or rights, from you under+this License will not have their licenses terminated so long as such+parties remain in full compliance.++ 5. You are not required to accept this License, since you have not+signed it. However, nothing else grants you permission to modify or+distribute the Program or its derivative works. These actions are+prohibited by law if you do not accept this License. Therefore, by+modifying or distributing the Program (or any work based on the+Program), you indicate your acceptance of this License to do so, and+all its terms and conditions for copying, distributing or modifying+the Program or works based on it.++ 6. Each time you redistribute the Program (or any work based on the+Program), the recipient automatically receives a license from the+original licensor to copy, distribute or modify the Program subject to+these terms and conditions. You may not impose any further+restrictions on the recipients' exercise of the rights granted herein.+You are not responsible for enforcing compliance by third parties to+this License.++ 7. If, as a consequence of a court judgment or allegation of patent+infringement or for any other reason (not limited to patent issues),+conditions are imposed on you (whether by court order, agreement or+otherwise) that contradict the conditions of this License, they do not+excuse you from the conditions of this License. If you cannot+distribute so as to satisfy simultaneously your obligations under this+License and any other pertinent obligations, then as a consequence you+may not distribute the Program at all. For example, if a patent+license would not permit royalty-free redistribution of the Program by+all those who receive copies directly or indirectly through you, then+the only way you could satisfy both it and this License would be to+refrain entirely from distribution of the Program.++If any portion of this section is held invalid or unenforceable under+any particular circumstance, the balance of the section is intended to+apply and the section as a whole is intended to apply in other+circumstances.++It is not the purpose of this section to induce you to infringe any+patents or other property right claims or to contest validity of any+such claims; this section has the sole purpose of protecting the+integrity of the free software distribution system, which is+implemented by public license practices. Many people have made+generous contributions to the wide range of software distributed+through that system in reliance on consistent application of that+system; it is up to the author/donor to decide if he or she is willing+to distribute software through any other system and a licensee cannot+impose that choice.++This section is intended to make thoroughly clear what is believed to+be a consequence of the rest of this License.++ 8. If the distribution and/or use of the Program is restricted in+certain countries either by patents or by copyrighted interfaces, the+original copyright holder who places the Program under this License+may add an explicit geographical distribution limitation excluding+those countries, so that distribution is permitted only in or among+countries not thus excluded. In such case, this License incorporates+the limitation as if written in the body of this License.++ 9. The Free Software Foundation may publish revised and/or new versions+of the General Public License from time to time. Such new versions will+be similar in spirit to the present version, but may differ in detail to+address new problems or concerns.++Each version is given a distinguishing version number. If the Program+specifies a version number of this License which applies to it and "any+later version", you have the option of following the terms and conditions+either of that version or of any later version published by the Free+Software Foundation. If the Program does not specify a version number of+this License, you may choose any version ever published by the Free Software+Foundation.++ 10. If you wish to incorporate parts of the Program into other free+programs whose distribution conditions are different, write to the author+to ask for permission. For software which is copyrighted by the Free+Software Foundation, write to the Free Software Foundation; we sometimes+make exceptions for this. Our decision will be guided by the two goals+of preserving the free status of all derivatives of our free software and+of promoting the sharing and reuse of software generally.++ NO WARRANTY++ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,+REPAIR OR CORRECTION.++ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE+POSSIBILITY OF SUCH DAMAGES.++ END OF TERMS AND CONDITIONS++ How to Apply These Terms to Your New Programs++ If you develop a new program, and you want it to be of the greatest+possible use to the public, the best way to achieve this is to make it+free software which everyone can redistribute and change under these terms.++ To do so, attach the following notices to the program. It is safest+to attach them to the start of each source file to most effectively+convey the exclusion of warranty; and each file should have at least+the "copyright" line and a pointer to where the full notice is found.++ <one line to give the program's name and a brief idea of what it does.>+ Copyright (C) <year> <name of author>++ This program 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 2 of the License, or+ (at your option) any later version.++ This program 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 this program; if not, write to the Free Software+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA+++Also add information on how to contact you by electronic and paper mail.++If the program is interactive, make it output a short notice like this+when it starts in an interactive mode:++ Gnomovision version 69, Copyright (C) year name of author+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.+ This is free software, and you are welcome to redistribute it+ under certain conditions; type `show c' for details.++The hypothetical commands `show w' and `show c' should show the appropriate+parts of the General Public License. Of course, the commands you use may+be called something other than `show w' and `show c'; they could even be+mouse-clicks or menu items--whatever suits your program.++You should also get your employer (if you work as a programmer) or your+school, if any, to sign a "copyright disclaimer" for the program, if+necessary. Here is a sample; alter the names:++ Yoyodyne, Inc., hereby disclaims all copyright interest in the program+ `Gnomovision' (which makes passes at compilers) written by James Hacker.++ <signature of Ty Coon>, 1 April 1989+ Ty Coon, President of Vice++This General Public License does not permit incorporating your program into+proprietary programs. If your program is a subroutine library, you may+consider it more useful to permit linking proprietary applications with the+library. If this is what you want to do, use the GNU Library General+Public License instead of this License.
+ README.markdown view
@@ -0,0 +1,207 @@+Gitit+=====++Gitit is a wiki program written in Haskell. It uses [HAppS][] for the web+server and session state, [git][] for storage, history, search, diffs,+and merging, and [pandoc][] for markup processing. Pages can be added,+changed, and removed either on the web or using git's command-line+tools. Gitit uses [pandoc][]'s extended version of markdown as its markup+language.++[git]: http://git.or.cz +[pandoc]: http://johnmacfarlane.net/pandoc+[HAppS]: http://happs.org++Getting started+===============++Compiling and installing gitit+------------------------------++You'll need the [GHC][] compiler and the [cabal-install][] tool. GHC can+be downloaded [here][]. For [cabal-install][] on *nix, follow the [quick+install][] instructions.++[GHC]: http://www.haskell.org/ghc/+[here]: http://www.haskell.org/ghc/+[cabal-install]: http://hackage.haskell.org/trac/hackage/wiki/CabalInstall+[quick install]: http://hackage.haskell.org/trac/hackage/wiki/CabalInstall#Quick Installation on Unix+[pcre]: http://www.pcre.org/ ++If you want the syntax highlighting feature, you need to make sure+that pandoc is compiled with support for it. First, make sure your system+has the [pcre][] library installed. Then:++ cabal install pandoc -fhighlighting++You can skip this step if you don't care about highlighting support.++You can now install the latest release of gitit:++ cabal update+ cabal install gitit++To install a version of gitit checked out from the repository,+change to the gitit directory and type:++ cabal install++The `cabal` tool will automatically install all of the required haskell+libraries. If all goes well, by the end of this process, the latest+release of gitit will be installed in your local `.cabal` directory. You+can check this by trying:++ gitit --version++If that doesn't work, check to see that `gitit` is in your local+cabal-install executable directory (usually `~/.cabal/bin`). And make+sure `~/.cabal/bin` is in your system path.++Running gitit+-------------++To run gitit, you'll need [git][] in your system path. Check this by doing++ git --version++Switch to the directory where you want to run gitit. This should be a directory+where you have write access, since two directories, `static` and `wikidata`, will be+created here. To start gitit, just type:++ gitit++If all goes well, gitit will do the following:++ 1. Create a git repository, `wikidata`, and add a default front page.+ 2. Create a `static` directory containing the scripts and CSS used by gitit.+ 3. Start a web server on port 5001.++Check that it worked: open a web browser and go to http://localhost:5001.++Configuration options+---------------------++You can set some configuration options when starting gitit, using the+option `-f [filename]`. A configuration file takes the following form:++ Config {+ repositoryPath = "wikidata",+ staticDir = "static",+ wikiBanner = "<img src=\"/images/bann.png\" alt=\"banner\"",+ wikiTitle = "Wiki",+ wikiFooter = "Powered by Gitit",+ tableOfContents = False,+ maxUploadSize = 100000,+ portNumber = 5001,+ passwordSalt = "l91snthoae8eou2340987",+ debugMode = True+ accessQuestion = Just ("Enter the access code (to request a code, contact me@foo.bar.com):", ["abcd"])+ }++For the most part, these options should be self-explanatory.++- The `wikiBanner` will be inserted before the top navigation bar on pages,+ and can be used to include a banner or wiki title. It is raw HTML. Similarly,+ the `wikiFooter` is raw HTML that will be inserted at the bottom of the page.+- The `tableOfContents` boolean determines whether a table of contents,+ derived from the page's headers, will be included for every page.+- `maxUploadSize` (in bytes) limits the size of pages and uploads.+- The `passwordSalt` is used to encrypt passwords and should be+ changed for every new site.+- `debugMode` causes diagnostic information to be printed to the console.+- The `accessQuestion` is either `Nothing` (in which case anyone will be+ allowed to register for an account) or `Just (question, [ans1, ans2, ...])`+ (in which case anyone who registers must first answer the `question` with+ one of the provided answers). One can deter automated spammers by using+ an `accessQuestion` with an easy and obvious answer. Or one can use an+ `accessQuestion` to limit those who can edit a wiki to a trusted group.++Configuring gitit+=================++The `static` directory+----------------------++If there is no wiki page or uploaded file corresponding to a request, gitit+always looks last in the `static` directory. So, for example, a file+`foo.jpg` in the `images` subdirectory of the `static` directory will be+accessible at the url `/images/foo.jpg`. Pandoc creates two subdirectories+of `static`, `stylesheets` and `javascripts`, which include the CSS and+scripts it uses.++Changing the theme+------------------++To change the look of the wiki, modify `gitit.css` in `static/stylesheets`.++Adding support for math+-----------------------++Gitit is designed to work with [jsMath][] to display LaTeX math in HTML. +Download `jsMath` and `jsMath Image Fonts` from the [jsMath download page][].+You'll have two `.zip` archives. Unzip them both in the+`static/javascripts` directory (a new subdirectory, `jsMath`, will be+created). You can test to see if math is working properly by clicking+"help" on the top navigation bar and looking for the math example+(the quadratic formula).++To write math on a wiki page, just enclose it in dollar signs, as in LaTeX:++ Here is a formula: $\frac{1}{\sqrt{c^2}}$++You can write display math by enclosing it in double dollar signs:++ $$\frac{1}{\sqrt{c^2}}$$++[jsMath download page]: http://sourceforge.net/project/showfiles.php?group_id=172663+[jsMath]: http://www.math.union.edu/~dpvc/jsMath/++Highlighted source code+-----------------------++If gitit was compiled against a version of pandoc that has highlighting support+(see above), you can get highlighted source code by using [delimited code blocks][]:++ ~~~ {.haskell .numberLines}+ qsort [] = []+ qsort (x:xs) = qsort (filter (< x) xs) ++ [x] +++ qsort (filter (>= x) xs) + ~~~++To see what languages are available:++ pandoc -v++[delimited code blocks]: http://johnmacfarlane.net/pandoc/README.html#delimited-code-blocks++Accessing the wiki via git+==========================++All the pages and uploaded files are stored in a git repository. By default, this+lives in the `wikidata` directory (though this can be changed through configuration+options). So you can interact with the wiki using git command line tools:++ git clone ssh://my.server.edu/path/of/wiki/wikidata+ cd wikidata+ vim Front\ Page.page # edit the page+ git commit -m "Added message about wiki etiquette" Front\ Page.page+ git push ++If you now look at the Front Page on the wiki, you should see your changes+reflected there. Note that the pages all have the extension `.page`.++Reporting bugs+==============++There is no bug tracker as yet, so report bugs directly to the author,+jgm at berkeley . edu++Acknowledgements+================++I borrowed some ideas about visual layout from Jeff Barczewski's fork+of Simon Rozet's `git-wiki`.++The code in `Gitit/State.hs` is based on http://hpaste.org/5957 by mightybyte,+as revised by dbpatterson.+
+ Setup.lhs view
@@ -0,0 +1,3 @@+#!/usr/bin/env runhaskell+> import Distribution.Simple+> main = defaultMain
+ data/FrontPage.page view
@@ -0,0 +1,32 @@+# Welcome to Gitit!++Gitit is a [Wiki] written in [Haskell]. [HAppS] is used for the web+server and session state. Pages and uploaded files are stored in a [git]+repository and may be modified either by using git's command-line tools+or through the wiki's web interface. [Pandoc]'s extended version of+[markdown] is used as a markup language. Gitit can be configured to+display TeX math and highlighted source code.++You can edit this page by double-clicking on it, or by clicking on the+"edit" button at the bottom of the screen.++You can make a link to another wiki page like this:+`[French Cheeses]()`.+This will produce a link like this: [French Cheeses](). Note that+the names of wiki pages need not be in CamelCase, and they may contain+spaces. Wiki pages may be organized into directories. Use the+slash ("/") character between directories and page names or+subdirectories: `[Wines/Pinot Noir]()`.++To create a new wiki page, just create a link to it and follow+the link.++Help is always available through the "help" link on the top bar.++[Wiki]: http://en.wikipedia.org/wiki/Wiki+[git]: http://git.or.cz/+[HAppS]: http://happs.org+[Haskell]: http://www.haskell.org/+[pandoc]: http://johnmacfarlane.net/pandoc/+[markdown]: http://daringfireball.net/projects/markdown/+
+ data/Help.page view
@@ -0,0 +1,273 @@+# Navigating++The most natural way of navigating is by clicking wiki links that+connect one page with another. The "front" button on the top navigation+bar will always take you to the Front Page of the wiki. The "index"+button will take you to a list of all pages on the wiki (organized into+folders if directories are used). Alternatively, you can search using+the search box. Note that the search is set to look for whole words, so+if you are looking for "gremlins", type that and not "gremlin".++# Markdown++This wiki's pages are written in [pandoc]'s extended form of [markdown].+If you're not familiar with markdown, you should start by looking+at the [markdown "basics" page] and the [markdown syntax description].+Consult the [pandoc User's Guide] for information about pandoc's syntax+for footnotes, tables, description lists, and other elements not present+in standard markdown.++[pandoc]: http://johnmacfarlane.net/pandoc+[pandoc User's Guide]: http://johnmacfarlane.net/pandoc/README.html+[markdown]: http://daringfireball.net/projects/markdown+[markdown "basics" page]: http://daringfireball.net/projects/markdown/basics+[markdown syntax description]: http://daringfireball.net/projects/markdown/syntax ++Markdown is pretty intuitive, since it is based on email conventions.+Here are some examples to get you started:++<table>+<tr>+<td>`*emphasized text*`</td>+<td>*emphasized text*</td>+</tr>+<tr>+<td>`**strong emphasis**`</td>+<td>**strong emphasis**</td>+</tr>+<tr>+<td>`` `literal text` ``</td>+<td>`literal text`</td>+</tr>+<tr>+<td>`\*escaped special characters\*`</td>+<td>\*escaped special characters\*</td>+</tr>+<tr>+<td>`[external link](http://google.com)`</td>+<td>[external link](http://google.com)</td>+</tr>+<tr>+<td>``</td>+<td></td>+</tr>+<tr>+<td>Wikilink: `[Front Page]()`</td>+<td>Wikilink: [Front Page]()</td>+</tr>+<tr>+<td>`H~2~O`</td>+<td>H~2~O</td>+</tr>+<tr>+<td>`10^100^`</td>+<td>10^100^</td>+</tr>+<tr>+<td>`~~strikeout~~`</td>+<td>~~strikeout~~</td>+</tr>+<tr>+<td>+`$x = \frac{{ - b \pm \sqrt {b^2 - 4ac} }}{{2a}}$`+</td>+<td>+$x = \frac{{ - b \pm \sqrt {b^2 - 4ac} }}{{2a}}$^[If this looks like+code, it's because jsMath is+not installed on your system. Contact your administrator to request it.]+</td>+</tr>+<tr>+<td>+`A simple footnote.^[Or is it so simple?]`+</td>+<td>+A simple footnote.^[Or is it so simple?]+</td>+</tr>+<tr>+<td>+<pre>+> an indented paragraph,+> usually used for quotations+</pre>+</td>+<td>++> an indented paragraph,+> usually used for quotations++</td>+<tr>+<td>+<pre>+ #!/bin/sh -e+ # code, indented four spaces+ echo "Hello world"+</pre>+</td>+<td>++ #!/bin/sh -e+ # code, indented four spaces+ echo "Hello world"++</td>+</tr>+<tr>+<td>+<pre>+* a bulleted list+* second item+ - sublist+ - and more+* back to main list+ 1. this item has an ordered+ 2. sublist+ a) you can also use letters+ b) another item+</pre>+</td>+<td>++* a bulleted list+* second item+ - sublist+ - and more+* back to main list+ 1. this item has an ordered+ 2. sublist+ a) you can also use letters+ b) another item++</td>+</tr>+<tr>+<td>+<pre>+Fruit Quantity+-------- -----------+apples 30,200+oranges 1,998+pears 42++Table: Our fruit inventory+</pre>+</td>+<td>++Fruit Quantity+-------- -----------+apples 30,200+oranges 1,998+pears 42++Table: Our fruit inventory++</td>+</tr>+</table>++For headings, prefix a line with one or more `#` signs: one for a major heading,+two for a subheading, three for a subsubheading. Be sure to leave space before+and after the heading.++ # Markdown++ Text...+ + ## Some examples...+ + Text...++## Wiki links++Links to other wiki pages are formed this way: `[Page Name]()`.+(Gitit converts markdown links with empty targets into wikilinks.)++To link to a wiki page using something else as the link text:+`[something else](Page Name)`.++Note that page names may contain spaces and some special characters.+They need not be CamelCase. CamelCase words are *not* automatically+converted to wiki links.++Wiki pages may be organized into directories. So, if you have+several pages on wine, you may wish to organize them like so:++ Wine/Pinot Noir+ Wine/Burgundy+ Wine/Cabernet Sauvignon++# Creating and modifying pages++## Registering for an account++In order to modify pages, you'll need to be logged in. To register+for an account, just click the "register" button in the bar on top+of the screen. You'll be asked to choose a username and a password,+which you can use to log in in the future by clicking the "login"+button. While you are logged in, these buttons are replaced by+a "logout so-and-so" button, which you should click to log out+when you are finished.++Note that logins are persistent through session cookies, so if you+don't log out, you'll still be logged in when you return to the+wiki from the same browser in the future.++## Editing a page++To edit a page, just double-click it, or click the "edit" button at+the bottom right corner of the page.++You can click "Preview" at any time to see how your changes will look.+Nothing is saved until you press "Save."++Note that you must provide a description of your changes. This is to+make it easier for others to see how a wiki page has been changed.++## Creating a new page++To create a new page, just create a wiki link that links to it, and+click the link. If the page does not exist, you will be editing it+immediately.++## Reverting to an earlier version++If you click the "history" button at the bottom of the page, you will+get a record of previous versions of the page. You can see the differences+between two versions by dragging one onto the other; additions will be+highlighted in yellow, and deletions will be crossed out with a horizontal+line. Clicking on the description of changes will take you to the page+as it existed after those changes. To revert the page to the revision+you're currently looking at, just click the "revert" button at the bottom+of the page, then "Save". ++## Deleting a page++The "delete" button at the bottom of the page will delete a page. Note+that deleted pages can be recovered, since a record of them will still be+accessible via the "activity" button on the top of the page.++# Uploading files++To upload a file--a picture, a PDF, or some other resource--click the+"upload" button in the navigation bar. You will be prompted to select+the file to upload. As with edits, you will be asked to provide a+description of the resource (or of the change, if you are overwriting+an existing file).++Often you may leave "Name on wiki" blank, since the existing name of the+file will be used by default. If that isn't desired, supply a name.+Note that uploaded files *must* include a file extension (e.g. `.pdf`).++If you are providing a new version of a file that already exists on the+wiki, check the box "Overwrite existing file." Otherwise, leave it+unchecked.++To link to an uploaded file, just use its name in a regular markdown link.+For example, if you uploaded a picture `fido.jpg`, you can insert the+picture into a page using the markdown: ``.+If you uploaded a PDF `projection.pdf`, you can insert a link to it+using: `[projection](projection.pdf)`.+
+ data/SampleConfig.hs view
@@ -0,0 +1,14 @@+Config {+repositoryPath = "wikidata",+staticDir = "static",+wikiBanner = "<img src=\"/images/bann.png\" alt=\"banner\"",+wikiTitle = "Wiki",+wikiFooter = "Powered by Gitit",+tableOfContents = False,+maxUploadSize = 100000,+portNumber = 5001,+passwordSalt = "l91snthoae8eou2340987",+debugMode = True,+accessQuestion = Just ("Enter the access code (to request an access code, contact me@somewhere.org):", ["abcd"])+}+
+ data/post-update view
@@ -0,0 +1,85 @@+#!/bin/sh+#+# This hook does two things:+#+# 1. update the "info" files that allow the list of references to be+# queries over dumb transports such as http+#+# 2. if this repository looks like it is a non-bare repository, and+# the checked-out branch is pushed to, then update the working copy.+# This makes "push" function somewhat similarly to darcs and bzr.+#+# To enable this hook, make this file executable by "chmod +x post-update".++git-update-server-info++is_bare=$(git-config --get --bool core.bare)++if [ -z "$is_bare" ]+then+ # for compatibility's sake, guess+ git_dir_full=$(cd $GIT_DIR; pwd)+ case $git_dir_full in */.git) is_bare=false;; *) is_bare=true;; esac+fi++update_wc() {+ ref=$1+ echo "Push to checked out branch $ref" >&2+ if [ ! -f $GIT_DIR/logs/HEAD ]+ then+ echo "E:push to non-bare repository requires a HEAD reflog" >&2+ exit 1+ fi+ if (cd $GIT_WORK_TREE; git-diff-files -q --exit-code >/dev/null)+ then+ wc_dirty=0+ else+ echo "W:unstaged changes found in working copy" >&2+ wc_dirty=1+ desc="working copy"+ fi+ if git diff-index --cached HEAD@{1} >/dev/null+ then+ index_dirty=0+ else+ echo "W:uncommitted, staged changes found" >&2+ index_dirty=1+ if [ -n "$desc" ]+ then+ desc="$desc and index"+ else+ desc="index"+ fi+ fi+ if [ "$wc_dirty" -ne 0 -o "$index_dirty" -ne 0 ]+ then+ new=$(git rev-parse HEAD)+ echo "W:stashing dirty $desc - see git-stash(1)" >&2+ ( trap 'echo trapped $$; git symbolic-ref HEAD "'"$ref"'"' 2 3 13 15 ERR EXIT+ git-update-ref --no-deref HEAD HEAD@{1}+ cd $GIT_WORK_TREE+ git stash save "dirty $desc before update to $new";+ git-symbolic-ref HEAD "$ref"+ )+ fi++ # eye candy - show the WC updates :)+ echo "Updating working copy" >&2+ (cd $GIT_WORK_TREE+ git-diff-index -R --name-status HEAD >&2+ git-reset --hard HEAD)+}++if [ "$is_bare" = "false" ]+then+ active_branch=`git-symbolic-ref HEAD`+ export GIT_DIR=$(cd $GIT_DIR; pwd)+ GIT_WORK_TREE=${GIT_WORK_TREE-..}+ for ref+ do+ if [ "$ref" = "$active_branch" ]+ then+ update_wc $ref+ fi+ done+fi
+ gitit.cabal view
@@ -0,0 +1,43 @@+name: gitit+version: 0.1.0.1+build-type: Simple+synopsis: Wiki using HAppS, git, and pandoc.+description: Gitit is a wiki program. HAppS is used for the web+ server and session state. Pages and uploaded files+ are stored in a git repository and may be modified+ either by using git's command-line tools or through+ the wiki's web interface. Pandoc's extended version+ of markdown is used as a markup language. Gitit can+ be configured to display TeX math (using jsMath)+ and highlighted source code. Ajax is used in a+ few places, but all the basic functionality is+ available even in browsers that do not support+ javascript.++category: Network+license: GPL+license-file: LICENSE+author: John MacFarlane+maintainer: jgm@berkeley.edu+data-files: stylesheets/gitit.css, stylesheets/hk-pyg.css,+ stylesheets/folder.png, stylesheets/page.png,+ javascripts/dragdiff.js, javascripts/folding.js,+ javascripts/jquery.min.js,+ javascripts/jquery-ui-personalized-1.6rc2.min.js,+ javascripts/preview.js, javascripts/search.js,+ data/post-update, data/FrontPage.page, data/Help.page,+ README.markdown, data/SampleConfig.hs++hs-source-dirs: .+executable: gitit+main-is: Gitit.hs +other-modules: Gitit.State, Gitit.Git+build-depends: base, parsec < 3, pretty, xhtml, containers, pandoc+ >= 1.1, process, filepath, directory, mtl, cgi,+ network, old-time, highlighting-kate, bytestring,+ utf8-string, HAppS-Server, HAppS-State, HAppS-Data,+ Crypto, HTTP++ghc-options: -Wall -threaded+ghc-prof-options: -auto-all+
+ javascripts/dragdiff.js view
@@ -0,0 +1,21 @@+$(document).ready(function(){+ $("#content").prepend("<p>Drag one revision onto another to see differences.</p>");+ $(".difflink").draggable({helper: "clone"}); + $(".difflink").droppable({+ accept: ".difflink",+ drop: function(ev, ui) {+ var targetOrder = parseInt($(this).attr("order"));+ var sourceOrder = parseInt($(ui.draggable).attr("order"));+ if (targetOrder < sourceOrder) {+ var fromRev = $(this).attr("revision");+ var toRev = $(ui.draggable).attr("revision");+ } else {+ var toRev = $(this).attr("revision");+ var fromRev = $(ui.draggable).attr("revision");+ };+ location.href = location.protocol + '//' + location.host + location.pathname ++ '?diff&from=' + fromRev + '&to=' + toRev;+ }+ });+});+
+ javascripts/folding.js view
@@ -0,0 +1,28 @@+// Execute this after the site is loaded.+// from http://homework.nwsnet.de/news/view/31-turn-nested-lists-into-a-collapsible-tree-with-jquery+$(function() {+ // Find list items representing folders and+ // style them accordingly. Also, turn them+ // into links that can expand/collapse the+ // tree leaf.+ $('ul.folding li > ul').each(function() {+ // Find this list's parent list item.+ var parent_li = $(this).parent('li');++ // Style the list item as folder.+ parent_li.addClass('folder');++ // Temporarily remove the list from the+ // parent list item, wrap the remaining+ // text in an anchor, then reattach it.+ var sub_ul = $(this).remove();+ parent_li.wrapInner('<a/>').find('a').click(function() {+ // Make the anchor toggle the leaf display.+ sub_ul.toggle();+ });+ parent_li.append(sub_ul);+ });++ // Hide all lists except the outermost.+ $('ul ul').hide();+});
+ javascripts/jquery-ui-personalized-1.6rc2.min.js view
@@ -0,0 +1,79 @@+;(function($){var _remove=$.fn.remove;$.fn.remove=function(){$("*",this).add(this).triggerHandler("remove");return _remove.apply(this,arguments);};function isVisible(element){function checkStyles(element){var style=element.style;return(style.display!='none'&&style.visibility!='hidden');}+var visible=checkStyles(element);(visible&&$.each($.dir(element,'parentNode'),function(){return(visible=checkStyles(this));}));return visible;}+$.extend($.expr[':'],{data:function(a,i,m){return $.data(a,m[3]);},tabbable:function(a,i,m){var nodeName=a.nodeName.toLowerCase();return(a.tabIndex>=0&&(('a'==nodeName&&a.href)||(/input|select|textarea|button/.test(nodeName)&&'hidden'!=a.type&&!a.disabled))&&isVisible(a));}});$.keyCode={BACKSPACE:8,CAPS_LOCK:20,COMMA:188,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38};function getter(namespace,plugin,method,args){function getMethods(type){var methods=$[namespace][plugin][type]||[];return(typeof methods=='string'?methods.split(/,?\s+/):methods);}+var methods=getMethods('getter');if(args.length==1&&typeof args[0]=='string'){methods=methods.concat(getMethods('getterSetter'));}+return($.inArray(method,methods)!=-1);}+$.widget=function(name,prototype){var namespace=name.split(".")[0];name=name.split(".")[1];$.fn[name]=function(options){var isMethodCall=(typeof options=='string'),args=Array.prototype.slice.call(arguments,1);if(isMethodCall&&options.substring(0,1)=='_'){return this;}+if(isMethodCall&&getter(namespace,name,options,args)){var instance=$.data(this[0],name);return(instance?instance[options].apply(instance,args):undefined);}+return this.each(function(){var instance=$.data(this,name);(!instance&&!isMethodCall&&$.data(this,name,new $[namespace][name](this,options)));(instance&&isMethodCall&&$.isFunction(instance[options])&&instance[options].apply(instance,args));});};$[namespace][name]=function(element,options){var self=this;this.widgetName=name;this.widgetEventPrefix=$[namespace][name].eventPrefix||name;this.widgetBaseClass=namespace+'-'+name;this.options=$.extend({},$.widget.defaults,$[namespace][name].defaults,$.metadata&&$.metadata.get(element)[name],options);this.element=$(element).bind('setData.'+name,function(e,key,value){return self._setData(key,value);}).bind('getData.'+name,function(e,key){return self._getData(key);}).bind('remove',function(){return self.destroy();});this._init();};$[namespace][name].prototype=$.extend({},$.widget.prototype,prototype);$[namespace][name].getterSetter='option';};$.widget.prototype={_init:function(){},destroy:function(){this.element.removeData(this.widgetName);},option:function(key,value){var options=key,self=this;if(typeof key=="string"){if(value===undefined){return this._getData(key);}+options={};options[key]=value;}+$.each(options,function(key,value){self._setData(key,value);});},_getData:function(key){return this.options[key];},_setData:function(key,value){this.options[key]=value;if(key=='disabled'){this.element[value?'addClass':'removeClass'](this.widgetBaseClass+'-disabled');}},enable:function(){this._setData('disabled',false);},disable:function(){this._setData('disabled',true);},_trigger:function(type,e,data){var eventName=(type==this.widgetEventPrefix?type:this.widgetEventPrefix+type);e=e||$.event.fix({type:eventName,target:this.element[0]});return this.element.triggerHandler(eventName,[e,data],this.options[type]);}};$.widget.defaults={disabled:false};$.ui={plugin:{add:function(module,option,set){var proto=$.ui[module].prototype;for(var i in set){proto.plugins[i]=proto.plugins[i]||[];proto.plugins[i].push([option,set[i]]);}},call:function(instance,name,args){var set=instance.plugins[name];if(!set){return;}+for(var i=0;i<set.length;i++){if(instance.options[set[i][0]]){set[i][1].apply(instance.element,args);}}}},cssCache:{},css:function(name){if($.ui.cssCache[name]){return $.ui.cssCache[name];}+var tmp=$('<div class="ui-gen">').addClass(name).css({position:'absolute',top:'-5000px',left:'-5000px',display:'block'}).appendTo('body');$.ui.cssCache[name]=!!((!(/auto|default/).test(tmp.css('cursor'))||(/^[1-9]/).test(tmp.css('height'))||(/^[1-9]/).test(tmp.css('width'))||!(/none/).test(tmp.css('backgroundImage'))||!(/transparent|rgba\(0, 0, 0, 0\)/).test(tmp.css('backgroundColor'))));try{$('body').get(0).removeChild(tmp.get(0));}catch(e){}+return $.ui.cssCache[name];},disableSelection:function(el){return $(el).attr('unselectable','on').css('MozUserSelect','none').bind('selectstart.ui',function(){return false;});},enableSelection:function(el){return $(el).attr('unselectable','off').css('MozUserSelect','').unbind('selectstart.ui');},hasScroll:function(e,a){if($(e).css('overflow')=='hidden'){return false;}+var scroll=(a&&a=='left')?'scrollLeft':'scrollTop',has=false;if(e[scroll]>0){return true;}+e[scroll]=1;has=(e[scroll]>0);e[scroll]=0;return has;}};$.ui.mouse={_mouseInit:function(){var self=this;this.element.bind('mousedown.'+this.widgetName,function(e){return self._mouseDown(e);});if($.browser.msie){this._mouseUnselectable=this.element.attr('unselectable');this.element.attr('unselectable','on');}+this.started=false;},_mouseDestroy:function(){this.element.unbind('.'+this.widgetName);($.browser.msie&&this.element.attr('unselectable',this._mouseUnselectable));},_mouseDown:function(e){(this._mouseStarted&&this._mouseUp(e));this._mouseDownEvent=e;var self=this,btnIsLeft=(e.which==1),elIsCancel=(typeof this.options.cancel=="string"?$(e.target).parents().add(e.target).filter(this.options.cancel).length:false);if(!btnIsLeft||elIsCancel||!this._mouseCapture(e)){return true;}+this.mouseDelayMet=!this.options.delay;if(!this.mouseDelayMet){this._mouseDelayTimer=setTimeout(function(){self.mouseDelayMet=true;},this.options.delay);}+if(this._mouseDistanceMet(e)&&this._mouseDelayMet(e)){this._mouseStarted=(this._mouseStart(e)!==false);if(!this._mouseStarted){e.preventDefault();return true;}}+this._mouseMoveDelegate=function(e){return self._mouseMove(e);};this._mouseUpDelegate=function(e){return self._mouseUp(e);};$(document).bind('mousemove.'+this.widgetName,this._mouseMoveDelegate).bind('mouseup.'+this.widgetName,this._mouseUpDelegate);return false;},_mouseMove:function(e){if($.browser.msie&&!e.button){return this._mouseUp(e);}+if(this._mouseStarted){this._mouseDrag(e);return false;}+if(this._mouseDistanceMet(e)&&this._mouseDelayMet(e)){this._mouseStarted=(this._mouseStart(this._mouseDownEvent,e)!==false);(this._mouseStarted?this._mouseDrag(e):this._mouseUp(e));}+return!this._mouseStarted;},_mouseUp:function(e){$(document).unbind('mousemove.'+this.widgetName,this._mouseMoveDelegate).unbind('mouseup.'+this.widgetName,this._mouseUpDelegate);if(this._mouseStarted){this._mouseStarted=false;this._mouseStop(e);}+return false;},_mouseDistanceMet:function(e){return(Math.max(Math.abs(this._mouseDownEvent.pageX-e.pageX),Math.abs(this._mouseDownEvent.pageY-e.pageY))>=this.options.distance);},_mouseDelayMet:function(e){return this.mouseDelayMet;},_mouseStart:function(e){},_mouseDrag:function(e){},_mouseStop:function(e){},_mouseCapture:function(e){return true;}};$.ui.mouse.defaults={cancel:null,distance:1,delay:0};})(jQuery);(function($){$.widget("ui.draggable",$.extend({},$.ui.mouse,{getHandle:function(e){var handle=!this.options.handle||!$(this.options.handle,this.element).length?true:false;$(this.options.handle,this.element).find("*").andSelf().each(function(){if(this==e.target)handle=true;});return handle;},createHelper:function(){var o=this.options;var helper=$.isFunction(o.helper)?$(o.helper.apply(this.element[0],[e])):(o.helper=='clone'?this.element.clone():this.element);if(!helper.parents('body').length)+helper.appendTo((o.appendTo=='parent'?this.element[0].parentNode:o.appendTo));if(helper[0]!=this.element[0]&&!(/(fixed|absolute)/).test(helper.css("position")))+helper.css("position","absolute");return helper;},_init:function(){if(this.options.helper=='original'&&!(/^(?:r|a|f)/).test(this.element.css("position")))+this.element[0].style.position='relative';(this.options.cssNamespace&&this.element.addClass(this.options.cssNamespace+"-draggable"));(this.options.disabled&&this.element.addClass('ui-draggable-disabled'));this._mouseInit();},_mouseCapture:function(e){var o=this.options;if(this.helper||o.disabled||$(e.target).is('.ui-resizable-handle'))+return false;this.handle=this.getHandle(e);if(!this.handle)+return false;return true;},_mouseStart:function(e){var o=this.options;this.helper=this.createHelper();if($.ui.ddmanager)+$.ui.ddmanager.current=this;this.margins={left:(parseInt(this.element.css("marginLeft"),10)||0),top:(parseInt(this.element.css("marginTop"),10)||0)};this.cssPosition=this.helper.css("position");this.offset=this.element.offset();this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left};this.offset.click={left:e.pageX-this.offset.left,top:e.pageY-this.offset.top};this.cacheScrollParents();this.offsetParent=this.helper.offsetParent();var po=this.offsetParent.offset();if(this.offsetParent[0]==document.body&&$.browser.mozilla)po={top:0,left:0};this.offset.parent={top:po.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:po.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)};if(this.cssPosition=="relative"){var p=this.element.position();this.offset.relative={top:p.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollTopParent.scrollTop(),left:p.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollLeftParent.scrollLeft()};}else{this.offset.relative={top:0,left:0};}+this.originalPosition=this._generatePosition(e);this.cacheHelperProportions();if(o.cursorAt)+this.adjustOffsetFromHelper(o.cursorAt);$.extend(this,{PAGEY_INCLUDES_SCROLL:(this.cssPosition=="absolute"&&(!this.scrollTopParent[0].tagName||(/(html|body)/i).test(this.scrollTopParent[0].tagName))),PAGEX_INCLUDES_SCROLL:(this.cssPosition=="absolute"&&(!this.scrollLeftParent[0].tagName||(/(html|body)/i).test(this.scrollLeftParent[0].tagName))),OFFSET_PARENT_NOT_SCROLL_PARENT_Y:this.scrollTopParent[0]!=this.offsetParent[0]&&!(this.scrollTopParent[0]==document&&(/(body|html)/i).test(this.offsetParent[0].tagName)),OFFSET_PARENT_NOT_SCROLL_PARENT_X:this.scrollLeftParent[0]!=this.offsetParent[0]&&!(this.scrollLeftParent[0]==document&&(/(body|html)/i).test(this.offsetParent[0].tagName))});if(o.containment)+this.setContainment();this._propagate("start",e);this.cacheHelperProportions();if($.ui.ddmanager&&!o.dropBehaviour)+$.ui.ddmanager.prepareOffsets(this,e);this.helper.addClass("ui-draggable-dragging");this._mouseDrag(e);return true;},cacheScrollParents:function(){this.scrollTopParent=function(el){do{if(/auto|scroll/.test(el.css('overflow'))||(/auto|scroll/).test(el.css('overflow-y')))return el;el=el.parent();}while(el[0].parentNode);return $(document);}(this.helper);this.scrollLeftParent=function(el){do{if(/auto|scroll/.test(el.css('overflow'))||(/auto|scroll/).test(el.css('overflow-x')))return el;el=el.parent();}while(el[0].parentNode);return $(document);}(this.helper);},adjustOffsetFromHelper:function(obj){if(obj.left!=undefined)this.offset.click.left=obj.left+this.margins.left;if(obj.right!=undefined)this.offset.click.left=this.helperProportions.width-obj.right+this.margins.left;if(obj.top!=undefined)this.offset.click.top=obj.top+this.margins.top;if(obj.bottom!=undefined)this.offset.click.top=this.helperProportions.height-obj.bottom+this.margins.top;},cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()};},setContainment:function(){var o=this.options;if(o.containment=='parent')o.containment=this.helper[0].parentNode;if(o.containment=='document'||o.containment=='window')this.containment=[0-this.offset.relative.left-this.offset.parent.left,0-this.offset.relative.top-this.offset.parent.top,$(o.containment=='document'?document:window).width()-this.offset.relative.left-this.offset.parent.left-this.helperProportions.width-this.margins.left-(parseInt(this.element.css("marginRight"),10)||0),($(o.containment=='document'?document:window).height()||document.body.parentNode.scrollHeight)-this.offset.relative.top-this.offset.parent.top-this.helperProportions.height-this.margins.top-(parseInt(this.element.css("marginBottom"),10)||0)];if(!(/^(document|window|parent)$/).test(o.containment)){var ce=$(o.containment)[0];var co=$(o.containment).offset();var over=($(ce).css("overflow")!='hidden');this.containment=[co.left+(parseInt($(ce).css("borderLeftWidth"),10)||0)-this.offset.relative.left-this.offset.parent.left,co.top+(parseInt($(ce).css("borderTopWidth"),10)||0)-this.offset.relative.top-this.offset.parent.top,co.left+(over?Math.max(ce.scrollWidth,ce.offsetWidth):ce.offsetWidth)-(parseInt($(ce).css("borderLeftWidth"),10)||0)-this.offset.relative.left-this.offset.parent.left-this.helperProportions.width-this.margins.left-(parseInt(this.element.css("marginRight"),10)||0),co.top+(over?Math.max(ce.scrollHeight,ce.offsetHeight):ce.offsetHeight)-(parseInt($(ce).css("borderTopWidth"),10)||0)-this.offset.relative.top-this.offset.parent.top-this.helperProportions.height-this.margins.top-(parseInt(this.element.css("marginBottom"),10)||0)];}},_convertPositionTo:function(d,pos){if(!pos)pos=this.position;var mod=d=="absolute"?1:-1;return{top:(pos.top++this.offset.relative.top*mod++this.offset.parent.top*mod+-(this.cssPosition=="fixed"||this.PAGEY_INCLUDES_SCROLL||this.OFFSET_PARENT_NOT_SCROLL_PARENT_Y?0:this.scrollTopParent.scrollTop())*mod++(this.cssPosition=="fixed"?$(document).scrollTop():0)*mod++this.margins.top*mod),left:(pos.left++this.offset.relative.left*mod++this.offset.parent.left*mod+-(this.cssPosition=="fixed"||this.PAGEX_INCLUDES_SCROLL||this.OFFSET_PARENT_NOT_SCROLL_PARENT_X?0:this.scrollLeftParent.scrollLeft())*mod++(this.cssPosition=="fixed"?$(document).scrollLeft():0)*mod++this.margins.left*mod)};},_generatePosition:function(e){var o=this.options;var position={top:(e.pageY+-this.offset.click.top+-this.offset.relative.top+-this.offset.parent.top++(this.cssPosition=="fixed"||this.PAGEY_INCLUDES_SCROLL||this.OFFSET_PARENT_NOT_SCROLL_PARENT_Y?0:this.scrollTopParent.scrollTop())+-(this.cssPosition=="fixed"?$(document).scrollTop():0)),left:(e.pageX+-this.offset.click.left+-this.offset.relative.left+-this.offset.parent.left++(this.cssPosition=="fixed"||this.PAGEX_INCLUDES_SCROLL||this.OFFSET_PARENT_NOT_SCROLL_PARENT_X?0:this.scrollLeftParent.scrollLeft())+-(this.cssPosition=="fixed"?$(document).scrollLeft():0))};if(!this.originalPosition)return position;if(this.containment){if(position.left<this.containment[0])position.left=this.containment[0];if(position.top<this.containment[1])position.top=this.containment[1];if(position.left>this.containment[2])position.left=this.containment[2];if(position.top>this.containment[3])position.top=this.containment[3];}+if(o.grid){var top=this.originalPosition.top+Math.round((position.top-this.originalPosition.top)/o.grid[1])*o.grid[1];position.top=this.containment?(!(top<this.containment[1]||top>this.containment[3])?top:(!(top<this.containment[1])?top-o.grid[1]:top+o.grid[1])):top;var left=this.originalPosition.left+Math.round((position.left-this.originalPosition.left)/o.grid[0])*o.grid[0];position.left=this.containment?(!(left<this.containment[0]||left>this.containment[2])?left:(!(left<this.containment[0])?left-o.grid[0]:left+o.grid[0])):left;}+return position;},_mouseDrag:function(e){this.position=this._generatePosition(e);this.positionAbs=this._convertPositionTo("absolute");this.position=this._propagate("drag",e)||this.position;if(!this.options.axis||this.options.axis!="y")this.helper[0].style.left=this.position.left+'px';if(!this.options.axis||this.options.axis!="x")this.helper[0].style.top=this.position.top+'px';if($.ui.ddmanager)$.ui.ddmanager.drag(this,e);return false;},_mouseStop:function(e){var dropped=false;if($.ui.ddmanager&&!this.options.dropBehaviour)+var dropped=$.ui.ddmanager.drop(this,e);if((this.options.revert=="invalid"&&!dropped)||(this.options.revert=="valid"&&dropped)||this.options.revert===true||($.isFunction(this.options.revert)&&this.options.revert.call(this.element,dropped))){var self=this;$(this.helper).animate(this.originalPosition,parseInt(this.options.revertDuration,10)||500,function(){self._propagate("stop",e);self._clear();});}else{this._propagate("stop",e);this._clear();}+return false;},_clear:function(){this.helper.removeClass("ui-draggable-dragging");if(this.options.helper!='original'&&!this.cancelHelperRemoval)this.helper.remove();this.helper=null;this.cancelHelperRemoval=false;},plugins:{},uiHash:function(e){return{helper:this.helper,position:this.position,absolutePosition:this.positionAbs,options:this.options};},_propagate:function(n,e){$.ui.plugin.call(this,n,[e,this.uiHash()]);if(n=="drag")this.positionAbs=this._convertPositionTo("absolute");return this.element.triggerHandler(n=="drag"?n:"drag"+n,[e,this.uiHash()],this.options[n]);},destroy:function(){if(!this.element.data('draggable'))return;this.element.removeData("draggable").unbind(".draggable").removeClass('ui-draggable ui-draggable-dragging ui-draggable-disabled');this._mouseDestroy();}}));$.extend($.ui.draggable,{defaults:{appendTo:"parent",axis:false,cancel:":input",delay:0,distance:1,helper:"original",scope:"default",cssNamespace:"ui"}});$.ui.plugin.add("draggable","cursor",{start:function(e,ui){var t=$('body');if(t.css("cursor"))ui.options._cursor=t.css("cursor");t.css("cursor",ui.options.cursor);},stop:function(e,ui){if(ui.options._cursor)$('body').css("cursor",ui.options._cursor);}});$.ui.plugin.add("draggable","zIndex",{start:function(e,ui){var t=$(ui.helper);if(t.css("zIndex"))ui.options._zIndex=t.css("zIndex");t.css('zIndex',ui.options.zIndex);},stop:function(e,ui){if(ui.options._zIndex)$(ui.helper).css('zIndex',ui.options._zIndex);}});$.ui.plugin.add("draggable","opacity",{start:function(e,ui){var t=$(ui.helper);if(t.css("opacity"))ui.options._opacity=t.css("opacity");t.css('opacity',ui.options.opacity);},stop:function(e,ui){if(ui.options._opacity)$(ui.helper).css('opacity',ui.options._opacity);}});$.ui.plugin.add("draggable","iframeFix",{start:function(e,ui){$(ui.options.iframeFix===true?"iframe":ui.options.iframeFix).each(function(){$('<div class="ui-draggable-iframeFix" style="background: #fff;"></div>').css({width:this.offsetWidth+"px",height:this.offsetHeight+"px",position:"absolute",opacity:"0.001",zIndex:1000}).css($(this).offset()).appendTo("body");});},stop:function(e,ui){$("div.ui-draggable-iframeFix").each(function(){this.parentNode.removeChild(this);});}});$.ui.plugin.add("draggable","scroll",{start:function(e,ui){var o=ui.options;var i=$(this).data("draggable");o.scrollSensitivity=o.scrollSensitivity||20;o.scrollSpeed=o.scrollSpeed||20;i.overflowY=function(el){do{if(/auto|scroll/.test(el.css('overflow'))||(/auto|scroll/).test(el.css('overflow-y')))return el;el=el.parent();}while(el[0].parentNode);return $(document);}(this);i.overflowX=function(el){do{if(/auto|scroll/.test(el.css('overflow'))||(/auto|scroll/).test(el.css('overflow-x')))return el;el=el.parent();}while(el[0].parentNode);return $(document);}(this);if(i.overflowY[0]!=document&&i.overflowY[0].tagName!='HTML')i.overflowYOffset=i.overflowY.offset();if(i.overflowX[0]!=document&&i.overflowX[0].tagName!='HTML')i.overflowXOffset=i.overflowX.offset();},drag:function(e,ui){var o=ui.options,scrolled=false;var i=$(this).data("draggable");if(i.overflowY[0]!=document&&i.overflowY[0].tagName!='HTML'){if((i.overflowYOffset.top+i.overflowY[0].offsetHeight)-e.pageY<o.scrollSensitivity)+i.overflowY[0].scrollTop=scrolled=i.overflowY[0].scrollTop+o.scrollSpeed;if(e.pageY-i.overflowYOffset.top<o.scrollSensitivity)+i.overflowY[0].scrollTop=scrolled=i.overflowY[0].scrollTop-o.scrollSpeed;}else{if(e.pageY-$(document).scrollTop()<o.scrollSensitivity)+scrolled=$(document).scrollTop($(document).scrollTop()-o.scrollSpeed);if($(window).height()-(e.pageY-$(document).scrollTop())<o.scrollSensitivity)+scrolled=$(document).scrollTop($(document).scrollTop()+o.scrollSpeed);}+if(i.overflowX[0]!=document&&i.overflowX[0].tagName!='HTML'){if((i.overflowXOffset.left+i.overflowX[0].offsetWidth)-e.pageX<o.scrollSensitivity)+i.overflowX[0].scrollLeft=scrolled=i.overflowX[0].scrollLeft+o.scrollSpeed;if(e.pageX-i.overflowXOffset.left<o.scrollSensitivity)+i.overflowX[0].scrollLeft=scrolled=i.overflowX[0].scrollLeft-o.scrollSpeed;}else{if(e.pageX-$(document).scrollLeft()<o.scrollSensitivity)+scrolled=$(document).scrollLeft($(document).scrollLeft()-o.scrollSpeed);if($(window).width()-(e.pageX-$(document).scrollLeft())<o.scrollSensitivity)+scrolled=$(document).scrollLeft($(document).scrollLeft()+o.scrollSpeed);}+if(scrolled!==false)+$.ui.ddmanager.prepareOffsets(i,e);}});$.ui.plugin.add("draggable","snap",{start:function(e,ui){var inst=$(this).data("draggable");inst.snapElements=[];$(ui.options.snap.constructor!=String?(ui.options.snap.items||':data(draggable)'):ui.options.snap).each(function(){var $t=$(this);var $o=$t.offset();if(this!=inst.element[0])inst.snapElements.push({item:this,width:$t.outerWidth(),height:$t.outerHeight(),top:$o.top,left:$o.left});});},drag:function(e,ui){var inst=$(this).data("draggable");var d=ui.options.snapTolerance||20;var x1=ui.absolutePosition.left,x2=x1+inst.helperProportions.width,y1=ui.absolutePosition.top,y2=y1+inst.helperProportions.height;for(var i=inst.snapElements.length-1;i>=0;i--){var l=inst.snapElements[i].left,r=l+inst.snapElements[i].width,t=inst.snapElements[i].top,b=t+inst.snapElements[i].height;if(!((l-d<x1&&x1<r+d&&t-d<y1&&y1<b+d)||(l-d<x1&&x1<r+d&&t-d<y2&&y2<b+d)||(l-d<x2&&x2<r+d&&t-d<y1&&y1<b+d)||(l-d<x2&&x2<r+d&&t-d<y2&&y2<b+d))){if(inst.snapElements[i].snapping)(inst.options.snap.release&&inst.options.snap.release.call(inst.element,null,$.extend(inst.uiHash(),{snapItem:inst.snapElements[i].item})));inst.snapElements[i].snapping=false;continue;}+if(ui.options.snapMode!='inner'){var ts=Math.abs(t-y2)<=d;var bs=Math.abs(b-y1)<=d;var ls=Math.abs(l-x2)<=d;var rs=Math.abs(r-x1)<=d;if(ts)ui.position.top=inst._convertPositionTo("relative",{top:t-inst.helperProportions.height,left:0}).top;if(bs)ui.position.top=inst._convertPositionTo("relative",{top:b,left:0}).top;if(ls)ui.position.left=inst._convertPositionTo("relative",{top:0,left:l-inst.helperProportions.width}).left;if(rs)ui.position.left=inst._convertPositionTo("relative",{top:0,left:r}).left;}+var first=(ts||bs||ls||rs);if(ui.options.snapMode!='outer'){var ts=Math.abs(t-y1)<=d;var bs=Math.abs(b-y2)<=d;var ls=Math.abs(l-x1)<=d;var rs=Math.abs(r-x2)<=d;if(ts)ui.position.top=inst._convertPositionTo("relative",{top:t,left:0}).top;if(bs)ui.position.top=inst._convertPositionTo("relative",{top:b-inst.helperProportions.height,left:0}).top;if(ls)ui.position.left=inst._convertPositionTo("relative",{top:0,left:l}).left;if(rs)ui.position.left=inst._convertPositionTo("relative",{top:0,left:r-inst.helperProportions.width}).left;}+if(!inst.snapElements[i].snapping&&(ts||bs||ls||rs||first))+(inst.options.snap.snap&&inst.options.snap.snap.call(inst.element,null,$.extend(inst.uiHash(),{snapItem:inst.snapElements[i].item})));inst.snapElements[i].snapping=(ts||bs||ls||rs||first);};}});$.ui.plugin.add("draggable","connectToSortable",{start:function(e,ui){var inst=$(this).data("draggable");inst.sortables=[];$(ui.options.connectToSortable).each(function(){if($.data(this,'sortable')){var sortable=$.data(this,'sortable');inst.sortables.push({instance:sortable,shouldRevert:sortable.options.revert});sortable._refreshItems();sortable._propagate("activate",e,inst);}});},stop:function(e,ui){var inst=$(this).data("draggable");$.each(inst.sortables,function(){if(this.instance.isOver){this.instance.isOver=0;inst.cancelHelperRemoval=true;this.instance.cancelHelperRemoval=false;if(this.shouldRevert)this.instance.options.revert=true;this.instance._mouseStop(e);this.instance.element.triggerHandler("sortreceive",[e,$.extend(this.instance.ui(),{sender:inst.element})],this.instance.options["receive"]);this.instance.options.helper=this.instance.options._helper;}else{this.instance._propagate("deactivate",e,inst);}});},drag:function(e,ui){var inst=$(this).data("draggable"),self=this;var checkPos=function(o){var l=o.left,r=l+o.width,t=o.top,b=t+o.height;return(l<(this.positionAbs.left+this.offset.click.left)&&(this.positionAbs.left+this.offset.click.left)<r&&t<(this.positionAbs.top+this.offset.click.top)&&(this.positionAbs.top+this.offset.click.top)<b);};$.each(inst.sortables,function(i){if(checkPos.call(inst,this.instance.containerCache)){if(!this.instance.isOver){this.instance.isOver=1;this.instance.currentItem=$(self).clone().appendTo(this.instance.element).data("sortable-item",true);this.instance.options._helper=this.instance.options.helper;this.instance.options.helper=function(){return ui.helper[0];};e.target=this.instance.currentItem[0];this.instance._mouseCapture(e,true);this.instance._mouseStart(e,true,true);this.instance.offset.click.top=inst.offset.click.top;this.instance.offset.click.left=inst.offset.click.left;this.instance.offset.parent.left-=inst.offset.parent.left-this.instance.offset.parent.left;this.instance.offset.parent.top-=inst.offset.parent.top-this.instance.offset.parent.top;inst._propagate("toSortable",e);}+if(this.instance.currentItem)this.instance._mouseDrag(e);}else{if(this.instance.isOver){this.instance.isOver=0;this.instance.cancelHelperRemoval=true;this.instance.options.revert=false;this.instance._mouseStop(e,true);this.instance.options.helper=this.instance.options._helper;this.instance.currentItem.remove();if(this.instance.placeholder)this.instance.placeholder.remove();inst._propagate("fromSortable",e);}};});}});$.ui.plugin.add("draggable","stack",{start:function(e,ui){var group=$.makeArray($(ui.options.stack.group)).sort(function(a,b){return(parseInt($(a).css("zIndex"),10)||ui.options.stack.min)-(parseInt($(b).css("zIndex"),10)||ui.options.stack.min);});$(group).each(function(i){this.style.zIndex=ui.options.stack.min+i;});this[0].style.zIndex=ui.options.stack.min+group.length;}});})(jQuery);(function($){$.widget("ui.droppable",{_setData:function(key,value){if(key=='accept'){this.options.accept=value&&$.isFunction(value)?value:function(d){return d.is(accept);};}else{$.widget.prototype._setData.apply(this,arguments);}},_init:function(){var o=this.options,accept=o.accept;this.isover=0;this.isout=1;this.options.accept=this.options.accept&&$.isFunction(this.options.accept)?this.options.accept:function(d){return d.is(accept);};this.proportions={width:this.element[0].offsetWidth,height:this.element[0].offsetHeight};$.ui.ddmanager.droppables[this.options.scope]=$.ui.ddmanager.droppables[this.options.scope]||[];$.ui.ddmanager.droppables[this.options.scope].push(this);(this.options.cssNamespace&&this.element.addClass(this.options.cssNamespace+"-droppable"));},plugins:{},ui:function(c){return{draggable:(c.currentItem||c.element),helper:c.helper,position:c.position,absolutePosition:c.positionAbs,options:this.options,element:this.element};},destroy:function(){var drop=$.ui.ddmanager.droppables[this.options.scope];for(var i=0;i<drop.length;i++)+if(drop[i]==this)+drop.splice(i,1);this.element.removeClass("ui-droppable-disabled").removeData("droppable").unbind(".droppable");},_over:function(e){var draggable=$.ui.ddmanager.current;if(!draggable||(draggable.currentItem||draggable.element)[0]==this.element[0])return;if(this.options.accept.call(this.element,(draggable.currentItem||draggable.element))){$.ui.plugin.call(this,'over',[e,this.ui(draggable)]);this.element.triggerHandler("dropover",[e,this.ui(draggable)],this.options.over);}},_out:function(e){var draggable=$.ui.ddmanager.current;if(!draggable||(draggable.currentItem||draggable.element)[0]==this.element[0])return;if(this.options.accept.call(this.element,(draggable.currentItem||draggable.element))){$.ui.plugin.call(this,'out',[e,this.ui(draggable)]);this.element.triggerHandler("dropout",[e,this.ui(draggable)],this.options.out);}},_drop:function(e,custom){var draggable=custom||$.ui.ddmanager.current;if(!draggable||(draggable.currentItem||draggable.element)[0]==this.element[0])return false;var childrenIntersection=false;this.element.find(":data(droppable)").not(".ui-draggable-dragging").each(function(){var inst=$.data(this,'droppable');if(inst.options.greedy&&$.ui.intersect(draggable,$.extend(inst,{offset:inst.element.offset()}),inst.options.tolerance)){childrenIntersection=true;return false;}});if(childrenIntersection)return false;if(this.options.accept.call(this.element,(draggable.currentItem||draggable.element))){$.ui.plugin.call(this,'drop',[e,this.ui(draggable)]);this.element.triggerHandler("drop",[e,this.ui(draggable)],this.options.drop);return this.element;}+return false;},_activate:function(e){var draggable=$.ui.ddmanager.current;$.ui.plugin.call(this,'activate',[e,this.ui(draggable)]);if(draggable)this.element.triggerHandler("dropactivate",[e,this.ui(draggable)],this.options.activate);},_deactivate:function(e){var draggable=$.ui.ddmanager.current;$.ui.plugin.call(this,'deactivate',[e,this.ui(draggable)]);if(draggable)this.element.triggerHandler("dropdeactivate",[e,this.ui(draggable)],this.options.deactivate);}});$.extend($.ui.droppable,{defaults:{disabled:false,tolerance:'intersect',scope:'default',cssNamespace:'ui'}});$.ui.intersect=function(draggable,droppable,toleranceMode){if(!droppable.offset)return false;var x1=(draggable.positionAbs||draggable.position.absolute).left,x2=x1+draggable.helperProportions.width,y1=(draggable.positionAbs||draggable.position.absolute).top,y2=y1+draggable.helperProportions.height;var l=droppable.offset.left,r=l+droppable.proportions.width,t=droppable.offset.top,b=t+droppable.proportions.height;switch(toleranceMode){case'fit':return(l<x1&&x2<r&&t<y1&&y2<b);break;case'intersect':return(l<x1+(draggable.helperProportions.width/2)&&x2-(draggable.helperProportions.width/2)<r&&t<y1+(draggable.helperProportions.height/2)&&y2-(draggable.helperProportions.height/2)<b);break;case'pointer':return(l<((draggable.positionAbs||draggable.position.absolute).left+(draggable.clickOffset||draggable.offset.click).left)&&((draggable.positionAbs||draggable.position.absolute).left+(draggable.clickOffset||draggable.offset.click).left)<r&&t<((draggable.positionAbs||draggable.position.absolute).top+(draggable.clickOffset||draggable.offset.click).top)&&((draggable.positionAbs||draggable.position.absolute).top+(draggable.clickOffset||draggable.offset.click).top)<b);break;case'touch':return((y1>=t&&y1<=b)||(y2>=t&&y2<=b)||(y1<t&&y2>b))&&((x1>=l&&x1<=r)||(x2>=l&&x2<=r)||(x1<l&&x2>r));break;default:return false;break;}};$.ui.ddmanager={current:null,droppables:{'default':[]},prepareOffsets:function(t,e){var m=$.ui.ddmanager.droppables[t.options.scope];var type=e?e.type:null;var list=(t.currentItem||t.element).find(":data(droppable)").andSelf();droppablesLoop:for(var i=0;i<m.length;i++){if(m[i].options.disabled||(t&&!m[i].options.accept.call(m[i].element,(t.currentItem||t.element))))continue;for(var j=0;j<list.length;j++){if(list[j]==m[i].element[0]){m[i].proportions.height=0;continue droppablesLoop;}};m[i].visible=m[i].element.css("display")!="none";if(!m[i].visible)continue;m[i].offset=m[i].element.offset();m[i].proportions={width:m[i].element[0].offsetWidth,height:m[i].element[0].offsetHeight};if(type=="dragstart"||type=="sortactivate")m[i]._activate.call(m[i],e);}},drop:function(draggable,e){var dropped=false;$.each($.ui.ddmanager.droppables[draggable.options.scope],function(){if(!this.options)return;if(!this.options.disabled&&this.visible&&$.ui.intersect(draggable,this,this.options.tolerance))+dropped=this._drop.call(this,e);if(!this.options.disabled&&this.visible&&this.options.accept.call(this.element,(draggable.currentItem||draggable.element))){this.isout=1;this.isover=0;this._deactivate.call(this,e);}});return dropped;},drag:function(draggable,e){if(draggable.options.refreshPositions)$.ui.ddmanager.prepareOffsets(draggable,e);$.each($.ui.ddmanager.droppables[draggable.options.scope],function(){if(this.options.disabled||this.greedyChild||!this.visible)return;var intersects=$.ui.intersect(draggable,this,this.options.tolerance);var c=!intersects&&this.isover==1?'isout':(intersects&&this.isover==0?'isover':null);if(!c)return;var parentInstance;if(this.options.greedy){var parent=this.element.parents(':data(droppable):eq(0)');if(parent.length){parentInstance=$.data(parent[0],'droppable');parentInstance.greedyChild=(c=='isover'?1:0);}}+if(parentInstance&&c=='isover'){parentInstance['isover']=0;parentInstance['isout']=1;parentInstance._out.call(parentInstance,e);}+this[c]=1;this[c=='isout'?'isover':'isout']=0;this[c=="isover"?"_over":"_out"].call(this,e);if(parentInstance&&c=='isout'){parentInstance['isout']=0;parentInstance['isover']=1;parentInstance._over.call(parentInstance,e);}});}};$.ui.plugin.add("droppable","activeClass",{activate:function(e,ui){$(this).addClass(ui.options.activeClass);},deactivate:function(e,ui){$(this).removeClass(ui.options.activeClass);},drop:function(e,ui){$(this).removeClass(ui.options.activeClass);}});$.ui.plugin.add("droppable","hoverClass",{over:function(e,ui){$(this).addClass(ui.options.hoverClass);},out:function(e,ui){$(this).removeClass(ui.options.hoverClass);},drop:function(e,ui){$(this).removeClass(ui.options.hoverClass);}});})(jQuery);
+ javascripts/jquery.min.js view
@@ -0,0 +1,32 @@+/*+ * jQuery 1.2.6 - New Wave Javascript+ *+ * Copyright (c) 2008 John Resig (jquery.com)+ * Dual licensed under the MIT (MIT-LICENSE.txt)+ * and GPL (GPL-LICENSE.txt) licenses.+ *+ * $Date: 2008-05-24 14:22:17 -0400 (Sat, 24 May 2008) $+ * $Rev: 5685 $+ */+(function(){var _jQuery=window.jQuery,_$=window.$;var jQuery=window.jQuery=window.$=function(selector,context){return new jQuery.fn.init(selector,context);};var quickExpr=/^[^<]*(<(.|\s)+>)[^>]*$|^#(\w+)$/,isSimple=/^.[^:#\[\.]*$/,undefined;jQuery.fn=jQuery.prototype={init:function(selector,context){selector=selector||document;if(selector.nodeType){this[0]=selector;this.length=1;return this;}if(typeof selector=="string"){var match=quickExpr.exec(selector);if(match&&(match[1]||!context)){if(match[1])selector=jQuery.clean([match[1]],context);else{var elem=document.getElementById(match[3]);if(elem){if(elem.id!=match[3])return jQuery().find(selector);return jQuery(elem);}selector=[];}}else+return jQuery(context).find(selector);}else if(jQuery.isFunction(selector))return jQuery(document)[jQuery.fn.ready?"ready":"load"](selector);return this.setArray(jQuery.makeArray(selector));},jquery:"1.2.6",size:function(){return this.length;},length:0,get:function(num){return num==undefined?jQuery.makeArray(this):this[num];},pushStack:function(elems){var ret=jQuery(elems);ret.prevObject=this;return ret;},setArray:function(elems){this.length=0;Array.prototype.push.apply(this,elems);return this;},each:function(callback,args){return jQuery.each(this,callback,args);},index:function(elem){var ret=-1;return jQuery.inArray(elem&&elem.jquery?elem[0]:elem,this);},attr:function(name,value,type){var options=name;if(name.constructor==String)if(value===undefined)return this[0]&&jQuery[type||"attr"](this[0],name);else{options={};options[name]=value;}return this.each(function(i){for(name in options)jQuery.attr(type?this.style:this,name,jQuery.prop(this,options[name],type,i,name));});},css:function(key,value){if((key=='width'||key=='height')&&parseFloat(value)<0)value=undefined;return this.attr(key,value,"curCSS");},text:function(text){if(typeof text!="object"&&text!=null)return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(text));var ret="";jQuery.each(text||this,function(){jQuery.each(this.childNodes,function(){if(this.nodeType!=8)ret+=this.nodeType!=1?this.nodeValue:jQuery.fn.text([this]);});});return ret;},wrapAll:function(html){if(this[0])jQuery(html,this[0].ownerDocument).clone().insertBefore(this[0]).map(function(){var elem=this;while(elem.firstChild)elem=elem.firstChild;return elem;}).append(this);return this;},wrapInner:function(html){return this.each(function(){jQuery(this).contents().wrapAll(html);});},wrap:function(html){return this.each(function(){jQuery(this).wrapAll(html);});},append:function(){return this.domManip(arguments,true,false,function(elem){if(this.nodeType==1)this.appendChild(elem);});},prepend:function(){return this.domManip(arguments,true,true,function(elem){if(this.nodeType==1)this.insertBefore(elem,this.firstChild);});},before:function(){return this.domManip(arguments,false,false,function(elem){this.parentNode.insertBefore(elem,this);});},after:function(){return this.domManip(arguments,false,true,function(elem){this.parentNode.insertBefore(elem,this.nextSibling);});},end:function(){return this.prevObject||jQuery([]);},find:function(selector){var elems=jQuery.map(this,function(elem){return jQuery.find(selector,elem);});return this.pushStack(/[^+>] [^+>]/.test(selector)||selector.indexOf("..")>-1?jQuery.unique(elems):elems);},clone:function(events){var ret=this.map(function(){if(jQuery.browser.msie&&!jQuery.isXMLDoc(this)){var clone=this.cloneNode(true),container=document.createElement("div");container.appendChild(clone);return jQuery.clean([container.innerHTML])[0];}else+return this.cloneNode(true);});var clone=ret.find("*").andSelf().each(function(){if(this[expando]!=undefined)this[expando]=null;});if(events===true)this.find("*").andSelf().each(function(i){if(this.nodeType==3)return;var events=jQuery.data(this,"events");for(var type in events)for(var handler in events[type])jQuery.event.add(clone[i],type,events[type][handler],events[type][handler].data);});return ret;},filter:function(selector){return this.pushStack(jQuery.isFunction(selector)&&jQuery.grep(this,function(elem,i){return selector.call(elem,i);})||jQuery.multiFilter(selector,this));},not:function(selector){if(selector.constructor==String)if(isSimple.test(selector))return this.pushStack(jQuery.multiFilter(selector,this,true));else+selector=jQuery.multiFilter(selector,this);var isArrayLike=selector.length&&selector[selector.length-1]!==undefined&&!selector.nodeType;return this.filter(function(){return isArrayLike?jQuery.inArray(this,selector)<0:this!=selector;});},add:function(selector){return this.pushStack(jQuery.unique(jQuery.merge(this.get(),typeof selector=='string'?jQuery(selector):jQuery.makeArray(selector))));},is:function(selector){return!!selector&&jQuery.multiFilter(selector,this).length>0;},hasClass:function(selector){return this.is("."+selector);},val:function(value){if(value==undefined){if(this.length){var elem=this[0];if(jQuery.nodeName(elem,"select")){var index=elem.selectedIndex,values=[],options=elem.options,one=elem.type=="select-one";if(index<0)return null;for(var i=one?index:0,max=one?index+1:options.length;i<max;i++){var option=options[i];if(option.selected){value=jQuery.browser.msie&&!option.attributes.value.specified?option.text:option.value;if(one)return value;values.push(value);}}return values;}else+return(this[0].value||"").replace(/\r/g,"");}return undefined;}if(value.constructor==Number)value+='';return this.each(function(){if(this.nodeType!=1)return;if(value.constructor==Array&&/radio|checkbox/.test(this.type))this.checked=(jQuery.inArray(this.value,value)>=0||jQuery.inArray(this.name,value)>=0);else if(jQuery.nodeName(this,"select")){var values=jQuery.makeArray(value);jQuery("option",this).each(function(){this.selected=(jQuery.inArray(this.value,values)>=0||jQuery.inArray(this.text,values)>=0);});if(!values.length)this.selectedIndex=-1;}else+this.value=value;});},html:function(value){return value==undefined?(this[0]?this[0].innerHTML:null):this.empty().append(value);},replaceWith:function(value){return this.after(value).remove();},eq:function(i){return this.slice(i,i+1);},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments));},map:function(callback){return this.pushStack(jQuery.map(this,function(elem,i){return callback.call(elem,i,elem);}));},andSelf:function(){return this.add(this.prevObject);},data:function(key,value){var parts=key.split(".");parts[1]=parts[1]?"."+parts[1]:"";if(value===undefined){var data=this.triggerHandler("getData"+parts[1]+"!",[parts[0]]);if(data===undefined&&this.length)data=jQuery.data(this[0],key);return data===undefined&&parts[1]?this.data(parts[0]):data;}else+return this.trigger("setData"+parts[1]+"!",[parts[0],value]).each(function(){jQuery.data(this,key,value);});},removeData:function(key){return this.each(function(){jQuery.removeData(this,key);});},domManip:function(args,table,reverse,callback){var clone=this.length>1,elems;return this.each(function(){if(!elems){elems=jQuery.clean(args,this.ownerDocument);if(reverse)elems.reverse();}var obj=this;if(table&&jQuery.nodeName(this,"table")&&jQuery.nodeName(elems[0],"tr"))obj=this.getElementsByTagName("tbody")[0]||this.appendChild(this.ownerDocument.createElement("tbody"));var scripts=jQuery([]);jQuery.each(elems,function(){var elem=clone?jQuery(this).clone(true)[0]:this;if(jQuery.nodeName(elem,"script"))scripts=scripts.add(elem);else{if(elem.nodeType==1)scripts=scripts.add(jQuery("script",elem).remove());callback.call(obj,elem);}});scripts.each(evalScript);});}};jQuery.fn.init.prototype=jQuery.fn;function evalScript(i,elem){if(elem.src)jQuery.ajax({url:elem.src,async:false,dataType:"script"});else+jQuery.globalEval(elem.text||elem.textContent||elem.innerHTML||"");if(elem.parentNode)elem.parentNode.removeChild(elem);}function now(){return+new Date;}jQuery.extend=jQuery.fn.extend=function(){var target=arguments[0]||{},i=1,length=arguments.length,deep=false,options;if(target.constructor==Boolean){deep=target;target=arguments[1]||{};i=2;}if(typeof target!="object"&&typeof target!="function")target={};if(length==i){target=this;--i;}for(;i<length;i++)if((options=arguments[i])!=null)for(var name in options){var src=target[name],copy=options[name];if(target===copy)continue;if(deep&©&&typeof copy=="object"&&!copy.nodeType)target[name]=jQuery.extend(deep,src||(copy.length!=null?[]:{}),copy);else if(copy!==undefined)target[name]=copy;}return target;};var expando="jQuery"+now(),uuid=0,windowData={},exclude=/z-?index|font-?weight|opacity|zoom|line-?height/i,defaultView=document.defaultView||{};jQuery.extend({noConflict:function(deep){window.$=_$;if(deep)window.jQuery=_jQuery;return jQuery;},isFunction:function(fn){return!!fn&&typeof fn!="string"&&!fn.nodeName&&fn.constructor!=Array&&/^[\s[]?function/.test(fn+"");},isXMLDoc:function(elem){return elem.documentElement&&!elem.body||elem.tagName&&elem.ownerDocument&&!elem.ownerDocument.body;},globalEval:function(data){data=jQuery.trim(data);if(data){var head=document.getElementsByTagName("head")[0]||document.documentElement,script=document.createElement("script");script.type="text/javascript";if(jQuery.browser.msie)script.text=data;else+script.appendChild(document.createTextNode(data));head.insertBefore(script,head.firstChild);head.removeChild(script);}},nodeName:function(elem,name){return elem.nodeName&&elem.nodeName.toUpperCase()==name.toUpperCase();},cache:{},data:function(elem,name,data){elem=elem==window?windowData:elem;var id=elem[expando];if(!id)id=elem[expando]=++uuid;if(name&&!jQuery.cache[id])jQuery.cache[id]={};if(data!==undefined)jQuery.cache[id][name]=data;return name?jQuery.cache[id][name]:id;},removeData:function(elem,name){elem=elem==window?windowData:elem;var id=elem[expando];if(name){if(jQuery.cache[id]){delete jQuery.cache[id][name];name="";for(name in jQuery.cache[id])break;if(!name)jQuery.removeData(elem);}}else{try{delete elem[expando];}catch(e){if(elem.removeAttribute)elem.removeAttribute(expando);}delete jQuery.cache[id];}},each:function(object,callback,args){var name,i=0,length=object.length;if(args){if(length==undefined){for(name in object)if(callback.apply(object[name],args)===false)break;}else+for(;i<length;)if(callback.apply(object[i++],args)===false)break;}else{if(length==undefined){for(name in object)if(callback.call(object[name],name,object[name])===false)break;}else+for(var value=object[0];i<length&&callback.call(value,i,value)!==false;value=object[++i]){}}return object;},prop:function(elem,value,type,i,name){if(jQuery.isFunction(value))value=value.call(elem,i);return value&&value.constructor==Number&&type=="curCSS"&&!exclude.test(name)?value+"px":value;},className:{add:function(elem,classNames){jQuery.each((classNames||"").split(/\s+/),function(i,className){if(elem.nodeType==1&&!jQuery.className.has(elem.className,className))elem.className+=(elem.className?" ":"")+className;});},remove:function(elem,classNames){if(elem.nodeType==1)elem.className=classNames!=undefined?jQuery.grep(elem.className.split(/\s+/),function(className){return!jQuery.className.has(classNames,className);}).join(" "):"";},has:function(elem,className){return jQuery.inArray(className,(elem.className||elem).toString().split(/\s+/))>-1;}},swap:function(elem,options,callback){var old={};for(var name in options){old[name]=elem.style[name];elem.style[name]=options[name];}callback.call(elem);for(var name in options)elem.style[name]=old[name];},css:function(elem,name,force){if(name=="width"||name=="height"){var val,props={position:"absolute",visibility:"hidden",display:"block"},which=name=="width"?["Left","Right"]:["Top","Bottom"];function getWH(){val=name=="width"?elem.offsetWidth:elem.offsetHeight;var padding=0,border=0;jQuery.each(which,function(){padding+=parseFloat(jQuery.curCSS(elem,"padding"+this,true))||0;border+=parseFloat(jQuery.curCSS(elem,"border"+this+"Width",true))||0;});val-=Math.round(padding+border);}if(jQuery(elem).is(":visible"))getWH();else+jQuery.swap(elem,props,getWH);return Math.max(0,val);}return jQuery.curCSS(elem,name,force);},curCSS:function(elem,name,force){var ret,style=elem.style;function color(elem){if(!jQuery.browser.safari)return false;var ret=defaultView.getComputedStyle(elem,null);return!ret||ret.getPropertyValue("color")=="";}if(name=="opacity"&&jQuery.browser.msie){ret=jQuery.attr(style,"opacity");return ret==""?"1":ret;}if(jQuery.browser.opera&&name=="display"){var save=style.outline;style.outline="0 solid black";style.outline=save;}if(name.match(/float/i))name=styleFloat;if(!force&&style&&style[name])ret=style[name];else if(defaultView.getComputedStyle){if(name.match(/float/i))name="float";name=name.replace(/([A-Z])/g,"-$1").toLowerCase();var computedStyle=defaultView.getComputedStyle(elem,null);if(computedStyle&&!color(elem))ret=computedStyle.getPropertyValue(name);else{var swap=[],stack=[],a=elem,i=0;for(;a&&color(a);a=a.parentNode)stack.unshift(a);for(;i<stack.length;i++)if(color(stack[i])){swap[i]=stack[i].style.display;stack[i].style.display="block";}ret=name=="display"&&swap[stack.length-1]!=null?"none":(computedStyle&&computedStyle.getPropertyValue(name))||"";for(i=0;i<swap.length;i++)if(swap[i]!=null)stack[i].style.display=swap[i];}if(name=="opacity"&&ret=="")ret="1";}else if(elem.currentStyle){var camelCase=name.replace(/\-(\w)/g,function(all,letter){return letter.toUpperCase();});ret=elem.currentStyle[name]||elem.currentStyle[camelCase];if(!/^\d+(px)?$/i.test(ret)&&/^\d/.test(ret)){var left=style.left,rsLeft=elem.runtimeStyle.left;elem.runtimeStyle.left=elem.currentStyle.left;style.left=ret||0;ret=style.pixelLeft+"px";style.left=left;elem.runtimeStyle.left=rsLeft;}}return ret;},clean:function(elems,context){var ret=[];context=context||document;if(typeof context.createElement=='undefined')context=context.ownerDocument||context[0]&&context[0].ownerDocument||document;jQuery.each(elems,function(i,elem){if(!elem)return;if(elem.constructor==Number)elem+='';if(typeof elem=="string"){elem=elem.replace(/(<(\w+)[^>]*?)\/>/g,function(all,front,tag){return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)?all:front+"></"+tag+">";});var tags=jQuery.trim(elem).toLowerCase(),div=context.createElement("div");var wrap=!tags.indexOf("<opt")&&[1,"<select multiple='multiple'>","</select>"]||!tags.indexOf("<leg")&&[1,"<fieldset>","</fieldset>"]||tags.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,"<table>","</table>"]||!tags.indexOf("<tr")&&[2,"<table><tbody>","</tbody></table>"]||(!tags.indexOf("<td")||!tags.indexOf("<th"))&&[3,"<table><tbody><tr>","</tr></tbody></table>"]||!tags.indexOf("<col")&&[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"]||jQuery.browser.msie&&[1,"div<div>","</div>"]||[0,"",""];div.innerHTML=wrap[1]+elem+wrap[2];while(wrap[0]--)div=div.lastChild;if(jQuery.browser.msie){var tbody=!tags.indexOf("<table")&&tags.indexOf("<tbody")<0?div.firstChild&&div.firstChild.childNodes:wrap[1]=="<table>"&&tags.indexOf("<tbody")<0?div.childNodes:[];for(var j=tbody.length-1;j>=0;--j)if(jQuery.nodeName(tbody[j],"tbody")&&!tbody[j].childNodes.length)tbody[j].parentNode.removeChild(tbody[j]);if(/^\s/.test(elem))div.insertBefore(context.createTextNode(elem.match(/^\s*/)[0]),div.firstChild);}elem=jQuery.makeArray(div.childNodes);}if(elem.length===0&&(!jQuery.nodeName(elem,"form")&&!jQuery.nodeName(elem,"select")))return;if(elem[0]==undefined||jQuery.nodeName(elem,"form")||elem.options)ret.push(elem);else+ret=jQuery.merge(ret,elem);});return ret;},attr:function(elem,name,value){if(!elem||elem.nodeType==3||elem.nodeType==8)return undefined;var notxml=!jQuery.isXMLDoc(elem),set=value!==undefined,msie=jQuery.browser.msie;name=notxml&&jQuery.props[name]||name;if(elem.tagName){var special=/href|src|style/.test(name);if(name=="selected"&&jQuery.browser.safari)elem.parentNode.selectedIndex;if(name in elem&¬xml&&!special){if(set){if(name=="type"&&jQuery.nodeName(elem,"input")&&elem.parentNode)throw"type property can't be changed";elem[name]=value;}if(jQuery.nodeName(elem,"form")&&elem.getAttributeNode(name))return elem.getAttributeNode(name).nodeValue;return elem[name];}if(msie&¬xml&&name=="style")return jQuery.attr(elem.style,"cssText",value);if(set)elem.setAttribute(name,""+value);var attr=msie&¬xml&&special?elem.getAttribute(name,2):elem.getAttribute(name);return attr===null?undefined:attr;}if(msie&&name=="opacity"){if(set){elem.zoom=1;elem.filter=(elem.filter||"").replace(/alpha\([^)]*\)/,"")+(parseInt(value)+''=="NaN"?"":"alpha(opacity="+value*100+")");}return elem.filter&&elem.filter.indexOf("opacity=")>=0?(parseFloat(elem.filter.match(/opacity=([^)]*)/)[1])/100)+'':"";}name=name.replace(/-([a-z])/ig,function(all,letter){return letter.toUpperCase();});if(set)elem[name]=value;return elem[name];},trim:function(text){return(text||"").replace(/^\s+|\s+$/g,"");},makeArray:function(array){var ret=[];if(array!=null){var i=array.length;if(i==null||array.split||array.setInterval||array.call)ret[0]=array;else+while(i)ret[--i]=array[i];}return ret;},inArray:function(elem,array){for(var i=0,length=array.length;i<length;i++)if(array[i]===elem)return i;return-1;},merge:function(first,second){var i=0,elem,pos=first.length;if(jQuery.browser.msie){while(elem=second[i++])if(elem.nodeType!=8)first[pos++]=elem;}else+while(elem=second[i++])first[pos++]=elem;return first;},unique:function(array){var ret=[],done={};try{for(var i=0,length=array.length;i<length;i++){var id=jQuery.data(array[i]);if(!done[id]){done[id]=true;ret.push(array[i]);}}}catch(e){ret=array;}return ret;},grep:function(elems,callback,inv){var ret=[];for(var i=0,length=elems.length;i<length;i++)if(!inv!=!callback(elems[i],i))ret.push(elems[i]);return ret;},map:function(elems,callback){var ret=[];for(var i=0,length=elems.length;i<length;i++){var value=callback(elems[i],i);if(value!=null)ret[ret.length]=value;}return ret.concat.apply([],ret);}});var userAgent=navigator.userAgent.toLowerCase();jQuery.browser={version:(userAgent.match(/.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/)||[])[1],safari:/webkit/.test(userAgent),opera:/opera/.test(userAgent),msie:/msie/.test(userAgent)&&!/opera/.test(userAgent),mozilla:/mozilla/.test(userAgent)&&!/(compatible|webkit)/.test(userAgent)};var styleFloat=jQuery.browser.msie?"styleFloat":"cssFloat";jQuery.extend({boxModel:!jQuery.browser.msie||document.compatMode=="CSS1Compat",props:{"for":"htmlFor","class":"className","float":styleFloat,cssFloat:styleFloat,styleFloat:styleFloat,readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing"}});jQuery.each({parent:function(elem){return elem.parentNode;},parents:function(elem){return jQuery.dir(elem,"parentNode");},next:function(elem){return jQuery.nth(elem,2,"nextSibling");},prev:function(elem){return jQuery.nth(elem,2,"previousSibling");},nextAll:function(elem){return jQuery.dir(elem,"nextSibling");},prevAll:function(elem){return jQuery.dir(elem,"previousSibling");},siblings:function(elem){return jQuery.sibling(elem.parentNode.firstChild,elem);},children:function(elem){return jQuery.sibling(elem.firstChild);},contents:function(elem){return jQuery.nodeName(elem,"iframe")?elem.contentDocument||elem.contentWindow.document:jQuery.makeArray(elem.childNodes);}},function(name,fn){jQuery.fn[name]=function(selector){var ret=jQuery.map(this,fn);if(selector&&typeof selector=="string")ret=jQuery.multiFilter(selector,ret);return this.pushStack(jQuery.unique(ret));};});jQuery.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(name,original){jQuery.fn[name]=function(){var args=arguments;return this.each(function(){for(var i=0,length=args.length;i<length;i++)jQuery(args[i])[original](this);});};});jQuery.each({removeAttr:function(name){jQuery.attr(this,name,"");if(this.nodeType==1)this.removeAttribute(name);},addClass:function(classNames){jQuery.className.add(this,classNames);},removeClass:function(classNames){jQuery.className.remove(this,classNames);},toggleClass:function(classNames){jQuery.className[jQuery.className.has(this,classNames)?"remove":"add"](this,classNames);},remove:function(selector){if(!selector||jQuery.filter(selector,[this]).r.length){jQuery("*",this).add(this).each(function(){jQuery.event.remove(this);jQuery.removeData(this);});if(this.parentNode)this.parentNode.removeChild(this);}},empty:function(){jQuery(">*",this).remove();while(this.firstChild)this.removeChild(this.firstChild);}},function(name,fn){jQuery.fn[name]=function(){return this.each(fn,arguments);};});jQuery.each(["Height","Width"],function(i,name){var type=name.toLowerCase();jQuery.fn[type]=function(size){return this[0]==window?jQuery.browser.opera&&document.body["client"+name]||jQuery.browser.safari&&window["inner"+name]||document.compatMode=="CSS1Compat"&&document.documentElement["client"+name]||document.body["client"+name]:this[0]==document?Math.max(Math.max(document.body["scroll"+name],document.documentElement["scroll"+name]),Math.max(document.body["offset"+name],document.documentElement["offset"+name])):size==undefined?(this.length?jQuery.css(this[0],type):null):this.css(type,size.constructor==String?size:size+"px");};});function num(elem,prop){return elem[0]&&parseInt(jQuery.curCSS(elem[0],prop,true),10)||0;}var chars=jQuery.browser.safari&&parseInt(jQuery.browser.version)<417?"(?:[\\w*_-]|\\\\.)":"(?:[\\w\u0128-\uFFFF*_-]|\\\\.)",quickChild=new RegExp("^>\\s*("+chars+"+)"),quickID=new RegExp("^("+chars+"+)(#)("+chars+"+)"),quickClass=new RegExp("^([#.]?)("+chars+"*)");jQuery.extend({expr:{"":function(a,i,m){return m[2]=="*"||jQuery.nodeName(a,m[2]);},"#":function(a,i,m){return a.getAttribute("id")==m[2];},":":{lt:function(a,i,m){return i<m[3]-0;},gt:function(a,i,m){return i>m[3]-0;},nth:function(a,i,m){return m[3]-0==i;},eq:function(a,i,m){return m[3]-0==i;},first:function(a,i){return i==0;},last:function(a,i,m,r){return i==r.length-1;},even:function(a,i){return i%2==0;},odd:function(a,i){return i%2;},"first-child":function(a){return a.parentNode.getElementsByTagName("*")[0]==a;},"last-child":function(a){return jQuery.nth(a.parentNode.lastChild,1,"previousSibling")==a;},"only-child":function(a){return!jQuery.nth(a.parentNode.lastChild,2,"previousSibling");},parent:function(a){return a.firstChild;},empty:function(a){return!a.firstChild;},contains:function(a,i,m){return(a.textContent||a.innerText||jQuery(a).text()||"").indexOf(m[3])>=0;},visible:function(a){return"hidden"!=a.type&&jQuery.css(a,"display")!="none"&&jQuery.css(a,"visibility")!="hidden";},hidden:function(a){return"hidden"==a.type||jQuery.css(a,"display")=="none"||jQuery.css(a,"visibility")=="hidden";},enabled:function(a){return!a.disabled;},disabled:function(a){return a.disabled;},checked:function(a){return a.checked;},selected:function(a){return a.selected||jQuery.attr(a,"selected");},text:function(a){return"text"==a.type;},radio:function(a){return"radio"==a.type;},checkbox:function(a){return"checkbox"==a.type;},file:function(a){return"file"==a.type;},password:function(a){return"password"==a.type;},submit:function(a){return"submit"==a.type;},image:function(a){return"image"==a.type;},reset:function(a){return"reset"==a.type;},button:function(a){return"button"==a.type||jQuery.nodeName(a,"button");},input:function(a){return/input|select|textarea|button/i.test(a.nodeName);},has:function(a,i,m){return jQuery.find(m[3],a).length;},header:function(a){return/h\d/i.test(a.nodeName);},animated:function(a){return jQuery.grep(jQuery.timers,function(fn){return a==fn.elem;}).length;}}},parse:[/^(\[) *@?([\w-]+) *([!*$^~=]*) *('?"?)(.*?)\4 *\]/,/^(:)([\w-]+)\("?'?(.*?(\(.*?\))?[^(]*?)"?'?\)/,new RegExp("^([:.#]*)("+chars+"+)")],multiFilter:function(expr,elems,not){var old,cur=[];while(expr&&expr!=old){old=expr;var f=jQuery.filter(expr,elems,not);expr=f.t.replace(/^\s*,\s*/,"");cur=not?elems=f.r:jQuery.merge(cur,f.r);}return cur;},find:function(t,context){if(typeof t!="string")return[t];if(context&&context.nodeType!=1&&context.nodeType!=9)return[];context=context||document;var ret=[context],done=[],last,nodeName;while(t&&last!=t){var r=[];last=t;t=jQuery.trim(t);var foundToken=false,re=quickChild,m=re.exec(t);if(m){nodeName=m[1].toUpperCase();for(var i=0;ret[i];i++)for(var c=ret[i].firstChild;c;c=c.nextSibling)if(c.nodeType==1&&(nodeName=="*"||c.nodeName.toUpperCase()==nodeName))r.push(c);ret=r;t=t.replace(re,"");if(t.indexOf(" ")==0)continue;foundToken=true;}else{re=/^([>+~])\s*(\w*)/i;if((m=re.exec(t))!=null){r=[];var merge={};nodeName=m[2].toUpperCase();m=m[1];for(var j=0,rl=ret.length;j<rl;j++){var n=m=="~"||m=="+"?ret[j].nextSibling:ret[j].firstChild;for(;n;n=n.nextSibling)if(n.nodeType==1){var id=jQuery.data(n);if(m=="~"&&merge[id])break;if(!nodeName||n.nodeName.toUpperCase()==nodeName){if(m=="~")merge[id]=true;r.push(n);}if(m=="+")break;}}ret=r;t=jQuery.trim(t.replace(re,""));foundToken=true;}}if(t&&!foundToken){if(!t.indexOf(",")){if(context==ret[0])ret.shift();done=jQuery.merge(done,ret);r=ret=[context];t=" "+t.substr(1,t.length);}else{var re2=quickID;var m=re2.exec(t);if(m){m=[0,m[2],m[3],m[1]];}else{re2=quickClass;m=re2.exec(t);}m[2]=m[2].replace(/\\/g,"");var elem=ret[ret.length-1];if(m[1]=="#"&&elem&&elem.getElementById&&!jQuery.isXMLDoc(elem)){var oid=elem.getElementById(m[2]);if((jQuery.browser.msie||jQuery.browser.opera)&&oid&&typeof oid.id=="string"&&oid.id!=m[2])oid=jQuery('[@id="'+m[2]+'"]',elem)[0];ret=r=oid&&(!m[3]||jQuery.nodeName(oid,m[3]))?[oid]:[];}else{for(var i=0;ret[i];i++){var tag=m[1]=="#"&&m[3]?m[3]:m[1]!=""||m[0]==""?"*":m[2];if(tag=="*"&&ret[i].nodeName.toLowerCase()=="object")tag="param";r=jQuery.merge(r,ret[i].getElementsByTagName(tag));}if(m[1]==".")r=jQuery.classFilter(r,m[2]);if(m[1]=="#"){var tmp=[];for(var i=0;r[i];i++)if(r[i].getAttribute("id")==m[2]){tmp=[r[i]];break;}r=tmp;}ret=r;}t=t.replace(re2,"");}}if(t){var val=jQuery.filter(t,r);ret=r=val.r;t=jQuery.trim(val.t);}}if(t)ret=[];if(ret&&context==ret[0])ret.shift();done=jQuery.merge(done,ret);return done;},classFilter:function(r,m,not){m=" "+m+" ";var tmp=[];for(var i=0;r[i];i++){var pass=(" "+r[i].className+" ").indexOf(m)>=0;if(!not&&pass||not&&!pass)tmp.push(r[i]);}return tmp;},filter:function(t,r,not){var last;while(t&&t!=last){last=t;var p=jQuery.parse,m;for(var i=0;p[i];i++){m=p[i].exec(t);if(m){t=t.substring(m[0].length);m[2]=m[2].replace(/\\/g,"");break;}}if(!m)break;if(m[1]==":"&&m[2]=="not")r=isSimple.test(m[3])?jQuery.filter(m[3],r,true).r:jQuery(r).not(m[3]);else if(m[1]==".")r=jQuery.classFilter(r,m[2],not);else if(m[1]=="["){var tmp=[],type=m[3];for(var i=0,rl=r.length;i<rl;i++){var a=r[i],z=a[jQuery.props[m[2]]||m[2]];if(z==null||/href|src|selected/.test(m[2]))z=jQuery.attr(a,m[2])||'';if((type==""&&!!z||type=="="&&z==m[5]||type=="!="&&z!=m[5]||type=="^="&&z&&!z.indexOf(m[5])||type=="$="&&z.substr(z.length-m[5].length)==m[5]||(type=="*="||type=="~=")&&z.indexOf(m[5])>=0)^not)tmp.push(a);}r=tmp;}else if(m[1]==":"&&m[2]=="nth-child"){var merge={},tmp=[],test=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(m[3]=="even"&&"2n"||m[3]=="odd"&&"2n+1"||!/\D/.test(m[3])&&"0n+"+m[3]||m[3]),first=(test[1]+(test[2]||1))-0,last=test[3]-0;for(var i=0,rl=r.length;i<rl;i++){var node=r[i],parentNode=node.parentNode,id=jQuery.data(parentNode);if(!merge[id]){var c=1;for(var n=parentNode.firstChild;n;n=n.nextSibling)if(n.nodeType==1)n.nodeIndex=c++;merge[id]=true;}var add=false;if(first==0){if(node.nodeIndex==last)add=true;}else if((node.nodeIndex-last)%first==0&&(node.nodeIndex-last)/first>=0)add=true;if(add^not)tmp.push(node);}r=tmp;}else{var fn=jQuery.expr[m[1]];if(typeof fn=="object")fn=fn[m[2]];if(typeof fn=="string")fn=eval("false||function(a,i){return "+fn+";}");r=jQuery.grep(r,function(elem,i){return fn(elem,i,m,r);},not);}}return{r:r,t:t};},dir:function(elem,dir){var matched=[],cur=elem[dir];while(cur&&cur!=document){if(cur.nodeType==1)matched.push(cur);cur=cur[dir];}return matched;},nth:function(cur,result,dir,elem){result=result||1;var num=0;for(;cur;cur=cur[dir])if(cur.nodeType==1&&++num==result)break;return cur;},sibling:function(n,elem){var r=[];for(;n;n=n.nextSibling){if(n.nodeType==1&&n!=elem)r.push(n);}return r;}});jQuery.event={add:function(elem,types,handler,data){if(elem.nodeType==3||elem.nodeType==8)return;if(jQuery.browser.msie&&elem.setInterval)elem=window;if(!handler.guid)handler.guid=this.guid++;if(data!=undefined){var fn=handler;handler=this.proxy(fn,function(){return fn.apply(this,arguments);});handler.data=data;}var events=jQuery.data(elem,"events")||jQuery.data(elem,"events",{}),handle=jQuery.data(elem,"handle")||jQuery.data(elem,"handle",function(){if(typeof jQuery!="undefined"&&!jQuery.event.triggered)return jQuery.event.handle.apply(arguments.callee.elem,arguments);});handle.elem=elem;jQuery.each(types.split(/\s+/),function(index,type){var parts=type.split(".");type=parts[0];handler.type=parts[1];var handlers=events[type];if(!handlers){handlers=events[type]={};if(!jQuery.event.special[type]||jQuery.event.special[type].setup.call(elem)===false){if(elem.addEventListener)elem.addEventListener(type,handle,false);else if(elem.attachEvent)elem.attachEvent("on"+type,handle);}}handlers[handler.guid]=handler;jQuery.event.global[type]=true;});elem=null;},guid:1,global:{},remove:function(elem,types,handler){if(elem.nodeType==3||elem.nodeType==8)return;var events=jQuery.data(elem,"events"),ret,index;if(events){if(types==undefined||(typeof types=="string"&&types.charAt(0)=="."))for(var type in events)this.remove(elem,type+(types||""));else{if(types.type){handler=types.handler;types=types.type;}jQuery.each(types.split(/\s+/),function(index,type){var parts=type.split(".");type=parts[0];if(events[type]){if(handler)delete events[type][handler.guid];else+for(handler in events[type])if(!parts[1]||events[type][handler].type==parts[1])delete events[type][handler];for(ret in events[type])break;if(!ret){if(!jQuery.event.special[type]||jQuery.event.special[type].teardown.call(elem)===false){if(elem.removeEventListener)elem.removeEventListener(type,jQuery.data(elem,"handle"),false);else if(elem.detachEvent)elem.detachEvent("on"+type,jQuery.data(elem,"handle"));}ret=null;delete events[type];}}});}for(ret in events)break;if(!ret){var handle=jQuery.data(elem,"handle");if(handle)handle.elem=null;jQuery.removeData(elem,"events");jQuery.removeData(elem,"handle");}}},trigger:function(type,data,elem,donative,extra){data=jQuery.makeArray(data);if(type.indexOf("!")>=0){type=type.slice(0,-1);var exclusive=true;}if(!elem){if(this.global[type])jQuery("*").add([window,document]).trigger(type,data);}else{if(elem.nodeType==3||elem.nodeType==8)return undefined;var val,ret,fn=jQuery.isFunction(elem[type]||null),event=!data[0]||!data[0].preventDefault;if(event){data.unshift({type:type,target:elem,preventDefault:function(){},stopPropagation:function(){},timeStamp:now()});data[0][expando]=true;}data[0].type=type;if(exclusive)data[0].exclusive=true;var handle=jQuery.data(elem,"handle");if(handle)val=handle.apply(elem,data);if((!fn||(jQuery.nodeName(elem,'a')&&type=="click"))&&elem["on"+type]&&elem["on"+type].apply(elem,data)===false)val=false;if(event)data.shift();if(extra&&jQuery.isFunction(extra)){ret=extra.apply(elem,val==null?data:data.concat(val));if(ret!==undefined)val=ret;}if(fn&&donative!==false&&val!==false&&!(jQuery.nodeName(elem,'a')&&type=="click")){this.triggered=true;try{elem[type]();}catch(e){}}this.triggered=false;}return val;},handle:function(event){var val,ret,namespace,all,handlers;event=arguments[0]=jQuery.event.fix(event||window.event);namespace=event.type.split(".");event.type=namespace[0];namespace=namespace[1];all=!namespace&&!event.exclusive;handlers=(jQuery.data(this,"events")||{})[event.type];for(var j in handlers){var handler=handlers[j];if(all||handler.type==namespace){event.handler=handler;event.data=handler.data;ret=handler.apply(this,arguments);if(val!==false)val=ret;if(ret===false){event.preventDefault();event.stopPropagation();}}}return val;},fix:function(event){if(event[expando]==true)return event;var originalEvent=event;event={originalEvent:originalEvent};var props="altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target timeStamp toElement type view wheelDelta which".split(" ");for(var i=props.length;i;i--)event[props[i]]=originalEvent[props[i]];event[expando]=true;event.preventDefault=function(){if(originalEvent.preventDefault)originalEvent.preventDefault();originalEvent.returnValue=false;};event.stopPropagation=function(){if(originalEvent.stopPropagation)originalEvent.stopPropagation();originalEvent.cancelBubble=true;};event.timeStamp=event.timeStamp||now();if(!event.target)event.target=event.srcElement||document;if(event.target.nodeType==3)event.target=event.target.parentNode;if(!event.relatedTarget&&event.fromElement)event.relatedTarget=event.fromElement==event.target?event.toElement:event.fromElement;if(event.pageX==null&&event.clientX!=null){var doc=document.documentElement,body=document.body;event.pageX=event.clientX+(doc&&doc.scrollLeft||body&&body.scrollLeft||0)-(doc.clientLeft||0);event.pageY=event.clientY+(doc&&doc.scrollTop||body&&body.scrollTop||0)-(doc.clientTop||0);}if(!event.which&&((event.charCode||event.charCode===0)?event.charCode:event.keyCode))event.which=event.charCode||event.keyCode;if(!event.metaKey&&event.ctrlKey)event.metaKey=event.ctrlKey;if(!event.which&&event.button)event.which=(event.button&1?1:(event.button&2?3:(event.button&4?2:0)));return event;},proxy:function(fn,proxy){proxy.guid=fn.guid=fn.guid||proxy.guid||this.guid++;return proxy;},special:{ready:{setup:function(){bindReady();return;},teardown:function(){return;}},mouseenter:{setup:function(){if(jQuery.browser.msie)return false;jQuery(this).bind("mouseover",jQuery.event.special.mouseenter.handler);return true;},teardown:function(){if(jQuery.browser.msie)return false;jQuery(this).unbind("mouseover",jQuery.event.special.mouseenter.handler);return true;},handler:function(event){if(withinElement(event,this))return true;event.type="mouseenter";return jQuery.event.handle.apply(this,arguments);}},mouseleave:{setup:function(){if(jQuery.browser.msie)return false;jQuery(this).bind("mouseout",jQuery.event.special.mouseleave.handler);return true;},teardown:function(){if(jQuery.browser.msie)return false;jQuery(this).unbind("mouseout",jQuery.event.special.mouseleave.handler);return true;},handler:function(event){if(withinElement(event,this))return true;event.type="mouseleave";return jQuery.event.handle.apply(this,arguments);}}}};jQuery.fn.extend({bind:function(type,data,fn){return type=="unload"?this.one(type,data,fn):this.each(function(){jQuery.event.add(this,type,fn||data,fn&&data);});},one:function(type,data,fn){var one=jQuery.event.proxy(fn||data,function(event){jQuery(this).unbind(event,one);return(fn||data).apply(this,arguments);});return this.each(function(){jQuery.event.add(this,type,one,fn&&data);});},unbind:function(type,fn){return this.each(function(){jQuery.event.remove(this,type,fn);});},trigger:function(type,data,fn){return this.each(function(){jQuery.event.trigger(type,data,this,true,fn);});},triggerHandler:function(type,data,fn){return this[0]&&jQuery.event.trigger(type,data,this[0],false,fn);},toggle:function(fn){var args=arguments,i=1;while(i<args.length)jQuery.event.proxy(fn,args[i++]);return this.click(jQuery.event.proxy(fn,function(event){this.lastToggle=(this.lastToggle||0)%i;event.preventDefault();return args[this.lastToggle++].apply(this,arguments)||false;}));},hover:function(fnOver,fnOut){return this.bind('mouseenter',fnOver).bind('mouseleave',fnOut);},ready:function(fn){bindReady();if(jQuery.isReady)fn.call(document,jQuery);else+jQuery.readyList.push(function(){return fn.call(this,jQuery);});return this;}});jQuery.extend({isReady:false,readyList:[],ready:function(){if(!jQuery.isReady){jQuery.isReady=true;if(jQuery.readyList){jQuery.each(jQuery.readyList,function(){this.call(document);});jQuery.readyList=null;}jQuery(document).triggerHandler("ready");}}});var readyBound=false;function bindReady(){if(readyBound)return;readyBound=true;if(document.addEventListener&&!jQuery.browser.opera)document.addEventListener("DOMContentLoaded",jQuery.ready,false);if(jQuery.browser.msie&&window==top)(function(){if(jQuery.isReady)return;try{document.documentElement.doScroll("left");}catch(error){setTimeout(arguments.callee,0);return;}jQuery.ready();})();if(jQuery.browser.opera)document.addEventListener("DOMContentLoaded",function(){if(jQuery.isReady)return;for(var i=0;i<document.styleSheets.length;i++)if(document.styleSheets[i].disabled){setTimeout(arguments.callee,0);return;}jQuery.ready();},false);if(jQuery.browser.safari){var numStyles;(function(){if(jQuery.isReady)return;if(document.readyState!="loaded"&&document.readyState!="complete"){setTimeout(arguments.callee,0);return;}if(numStyles===undefined)numStyles=jQuery("style, link[rel=stylesheet]").length;if(document.styleSheets.length!=numStyles){setTimeout(arguments.callee,0);return;}jQuery.ready();})();}jQuery.event.add(window,"load",jQuery.ready);}jQuery.each(("blur,focus,load,resize,scroll,unload,click,dblclick,"+"mousedown,mouseup,mousemove,mouseover,mouseout,change,select,"+"submit,keydown,keypress,keyup,error").split(","),function(i,name){jQuery.fn[name]=function(fn){return fn?this.bind(name,fn):this.trigger(name);};});var withinElement=function(event,elem){var parent=event.relatedTarget;while(parent&&parent!=elem)try{parent=parent.parentNode;}catch(error){parent=elem;}return parent==elem;};jQuery(window).bind("unload",function(){jQuery("*").add(document).unbind();});jQuery.fn.extend({_load:jQuery.fn.load,load:function(url,params,callback){if(typeof url!='string')return this._load(url);var off=url.indexOf(" ");if(off>=0){var selector=url.slice(off,url.length);url=url.slice(0,off);}callback=callback||function(){};var type="GET";if(params)if(jQuery.isFunction(params)){callback=params;params=null;}else{params=jQuery.param(params);type="POST";}var self=this;jQuery.ajax({url:url,type:type,dataType:"html",data:params,complete:function(res,status){if(status=="success"||status=="notmodified")self.html(selector?jQuery("<div/>").append(res.responseText.replace(/<script(.|\s)*?\/script>/g,"")).find(selector):res.responseText);self.each(callback,[res.responseText,status,res]);}});return this;},serialize:function(){return jQuery.param(this.serializeArray());},serializeArray:function(){return this.map(function(){return jQuery.nodeName(this,"form")?jQuery.makeArray(this.elements):this;}).filter(function(){return this.name&&!this.disabled&&(this.checked||/select|textarea/i.test(this.nodeName)||/text|hidden|password/i.test(this.type));}).map(function(i,elem){var val=jQuery(this).val();return val==null?null:val.constructor==Array?jQuery.map(val,function(val,i){return{name:elem.name,value:val};}):{name:elem.name,value:val};}).get();}});jQuery.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),function(i,o){jQuery.fn[o]=function(f){return this.bind(o,f);};});var jsc=now();jQuery.extend({get:function(url,data,callback,type){if(jQuery.isFunction(data)){callback=data;data=null;}return jQuery.ajax({type:"GET",url:url,data:data,success:callback,dataType:type});},getScript:function(url,callback){return jQuery.get(url,null,callback,"script");},getJSON:function(url,data,callback){return jQuery.get(url,data,callback,"json");},post:function(url,data,callback,type){if(jQuery.isFunction(data)){callback=data;data={};}return jQuery.ajax({type:"POST",url:url,data:data,success:callback,dataType:type});},ajaxSetup:function(settings){jQuery.extend(jQuery.ajaxSettings,settings);},ajaxSettings:{url:location.href,global:true,type:"GET",timeout:0,contentType:"application/x-www-form-urlencoded",processData:true,async:true,data:null,username:null,password:null,accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},ajax:function(s){s=jQuery.extend(true,s,jQuery.extend(true,{},jQuery.ajaxSettings,s));var jsonp,jsre=/=\?(&|$)/g,status,data,type=s.type.toUpperCase();if(s.data&&s.processData&&typeof s.data!="string")s.data=jQuery.param(s.data);if(s.dataType=="jsonp"){if(type=="GET"){if(!s.url.match(jsre))s.url+=(s.url.match(/\?/)?"&":"?")+(s.jsonp||"callback")+"=?";}else if(!s.data||!s.data.match(jsre))s.data=(s.data?s.data+"&":"")+(s.jsonp||"callback")+"=?";s.dataType="json";}if(s.dataType=="json"&&(s.data&&s.data.match(jsre)||s.url.match(jsre))){jsonp="jsonp"+jsc++;if(s.data)s.data=(s.data+"").replace(jsre,"="+jsonp+"$1");s.url=s.url.replace(jsre,"="+jsonp+"$1");s.dataType="script";window[jsonp]=function(tmp){data=tmp;success();complete();window[jsonp]=undefined;try{delete window[jsonp];}catch(e){}if(head)head.removeChild(script);};}if(s.dataType=="script"&&s.cache==null)s.cache=false;if(s.cache===false&&type=="GET"){var ts=now();var ret=s.url.replace(/(\?|&)_=.*?(&|$)/,"$1_="+ts+"$2");s.url=ret+((ret==s.url)?(s.url.match(/\?/)?"&":"?")+"_="+ts:"");}if(s.data&&type=="GET"){s.url+=(s.url.match(/\?/)?"&":"?")+s.data;s.data=null;}if(s.global&&!jQuery.active++)jQuery.event.trigger("ajaxStart");var remote=/^(?:\w+:)?\/\/([^\/?#]+)/;if(s.dataType=="script"&&type=="GET"&&remote.test(s.url)&&remote.exec(s.url)[1]!=location.host){var head=document.getElementsByTagName("head")[0];var script=document.createElement("script");script.src=s.url;if(s.scriptCharset)script.charset=s.scriptCharset;if(!jsonp){var done=false;script.onload=script.onreadystatechange=function(){if(!done&&(!this.readyState||this.readyState=="loaded"||this.readyState=="complete")){done=true;success();complete();head.removeChild(script);}};}head.appendChild(script);return undefined;}var requestDone=false;var xhr=window.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest();if(s.username)xhr.open(type,s.url,s.async,s.username,s.password);else+xhr.open(type,s.url,s.async);try{if(s.data)xhr.setRequestHeader("Content-Type",s.contentType);if(s.ifModified)xhr.setRequestHeader("If-Modified-Since",jQuery.lastModified[s.url]||"Thu, 01 Jan 1970 00:00:00 GMT");xhr.setRequestHeader("X-Requested-With","XMLHttpRequest");xhr.setRequestHeader("Accept",s.dataType&&s.accepts[s.dataType]?s.accepts[s.dataType]+", */*":s.accepts._default);}catch(e){}if(s.beforeSend&&s.beforeSend(xhr,s)===false){s.global&&jQuery.active--;xhr.abort();return false;}if(s.global)jQuery.event.trigger("ajaxSend",[xhr,s]);var onreadystatechange=function(isTimeout){if(!requestDone&&xhr&&(xhr.readyState==4||isTimeout=="timeout")){requestDone=true;if(ival){clearInterval(ival);ival=null;}status=isTimeout=="timeout"&&"timeout"||!jQuery.httpSuccess(xhr)&&"error"||s.ifModified&&jQuery.httpNotModified(xhr,s.url)&&"notmodified"||"success";if(status=="success"){try{data=jQuery.httpData(xhr,s.dataType,s.dataFilter);}catch(e){status="parsererror";}}if(status=="success"){var modRes;try{modRes=xhr.getResponseHeader("Last-Modified");}catch(e){}if(s.ifModified&&modRes)jQuery.lastModified[s.url]=modRes;if(!jsonp)success();}else+jQuery.handleError(s,xhr,status);complete();if(s.async)xhr=null;}};if(s.async){var ival=setInterval(onreadystatechange,13);if(s.timeout>0)setTimeout(function(){if(xhr){xhr.abort();if(!requestDone)onreadystatechange("timeout");}},s.timeout);}try{xhr.send(s.data);}catch(e){jQuery.handleError(s,xhr,null,e);}if(!s.async)onreadystatechange();function success(){if(s.success)s.success(data,status);if(s.global)jQuery.event.trigger("ajaxSuccess",[xhr,s]);}function complete(){if(s.complete)s.complete(xhr,status);if(s.global)jQuery.event.trigger("ajaxComplete",[xhr,s]);if(s.global&&!--jQuery.active)jQuery.event.trigger("ajaxStop");}return xhr;},handleError:function(s,xhr,status,e){if(s.error)s.error(xhr,status,e);if(s.global)jQuery.event.trigger("ajaxError",[xhr,s,e]);},active:0,httpSuccess:function(xhr){try{return!xhr.status&&location.protocol=="file:"||(xhr.status>=200&&xhr.status<300)||xhr.status==304||xhr.status==1223||jQuery.browser.safari&&xhr.status==undefined;}catch(e){}return false;},httpNotModified:function(xhr,url){try{var xhrRes=xhr.getResponseHeader("Last-Modified");return xhr.status==304||xhrRes==jQuery.lastModified[url]||jQuery.browser.safari&&xhr.status==undefined;}catch(e){}return false;},httpData:function(xhr,type,filter){var ct=xhr.getResponseHeader("content-type"),xml=type=="xml"||!type&&ct&&ct.indexOf("xml")>=0,data=xml?xhr.responseXML:xhr.responseText;if(xml&&data.documentElement.tagName=="parsererror")throw"parsererror";if(filter)data=filter(data,type);if(type=="script")jQuery.globalEval(data);if(type=="json")data=eval("("+data+")");return data;},param:function(a){var s=[];if(a.constructor==Array||a.jquery)jQuery.each(a,function(){s.push(encodeURIComponent(this.name)+"="+encodeURIComponent(this.value));});else+for(var j in a)if(a[j]&&a[j].constructor==Array)jQuery.each(a[j],function(){s.push(encodeURIComponent(j)+"="+encodeURIComponent(this));});else+s.push(encodeURIComponent(j)+"="+encodeURIComponent(jQuery.isFunction(a[j])?a[j]():a[j]));return s.join("&").replace(/%20/g,"+");}});jQuery.fn.extend({show:function(speed,callback){return speed?this.animate({height:"show",width:"show",opacity:"show"},speed,callback):this.filter(":hidden").each(function(){this.style.display=this.oldblock||"";if(jQuery.css(this,"display")=="none"){var elem=jQuery("<"+this.tagName+" />").appendTo("body");this.style.display=elem.css("display");if(this.style.display=="none")this.style.display="block";elem.remove();}}).end();},hide:function(speed,callback){return speed?this.animate({height:"hide",width:"hide",opacity:"hide"},speed,callback):this.filter(":visible").each(function(){this.oldblock=this.oldblock||jQuery.css(this,"display");this.style.display="none";}).end();},_toggle:jQuery.fn.toggle,toggle:function(fn,fn2){return jQuery.isFunction(fn)&&jQuery.isFunction(fn2)?this._toggle.apply(this,arguments):fn?this.animate({height:"toggle",width:"toggle",opacity:"toggle"},fn,fn2):this.each(function(){jQuery(this)[jQuery(this).is(":hidden")?"show":"hide"]();});},slideDown:function(speed,callback){return this.animate({height:"show"},speed,callback);},slideUp:function(speed,callback){return this.animate({height:"hide"},speed,callback);},slideToggle:function(speed,callback){return this.animate({height:"toggle"},speed,callback);},fadeIn:function(speed,callback){return this.animate({opacity:"show"},speed,callback);},fadeOut:function(speed,callback){return this.animate({opacity:"hide"},speed,callback);},fadeTo:function(speed,to,callback){return this.animate({opacity:to},speed,callback);},animate:function(prop,speed,easing,callback){var optall=jQuery.speed(speed,easing,callback);return this[optall.queue===false?"each":"queue"](function(){if(this.nodeType!=1)return false;var opt=jQuery.extend({},optall),p,hidden=jQuery(this).is(":hidden"),self=this;for(p in prop){if(prop[p]=="hide"&&hidden||prop[p]=="show"&&!hidden)return opt.complete.call(this);if(p=="height"||p=="width"){opt.display=jQuery.css(this,"display");opt.overflow=this.style.overflow;}}if(opt.overflow!=null)this.style.overflow="hidden";opt.curAnim=jQuery.extend({},prop);jQuery.each(prop,function(name,val){var e=new jQuery.fx(self,opt,name);if(/toggle|show|hide/.test(val))e[val=="toggle"?hidden?"show":"hide":val](prop);else{var parts=val.toString().match(/^([+-]=)?([\d+-.]+)(.*)$/),start=e.cur(true)||0;if(parts){var end=parseFloat(parts[2]),unit=parts[3]||"px";if(unit!="px"){self.style[name]=(end||1)+unit;start=((end||1)/e.cur(true))*start;self.style[name]=start+unit;}if(parts[1])end=((parts[1]=="-="?-1:1)*end)+start;e.custom(start,end,unit);}else+e.custom(start,val,"");}});return true;});},queue:function(type,fn){if(jQuery.isFunction(type)||(type&&type.constructor==Array)){fn=type;type="fx";}if(!type||(typeof type=="string"&&!fn))return queue(this[0],type);return this.each(function(){if(fn.constructor==Array)queue(this,type,fn);else{queue(this,type).push(fn);if(queue(this,type).length==1)fn.call(this);}});},stop:function(clearQueue,gotoEnd){var timers=jQuery.timers;if(clearQueue)this.queue([]);this.each(function(){for(var i=timers.length-1;i>=0;i--)if(timers[i].elem==this){if(gotoEnd)timers[i](true);timers.splice(i,1);}});if(!gotoEnd)this.dequeue();return this;}});var queue=function(elem,type,array){if(elem){type=type||"fx";var q=jQuery.data(elem,type+"queue");if(!q||array)q=jQuery.data(elem,type+"queue",jQuery.makeArray(array));}return q;};jQuery.fn.dequeue=function(type){type=type||"fx";return this.each(function(){var q=queue(this,type);q.shift();if(q.length)q[0].call(this);});};jQuery.extend({speed:function(speed,easing,fn){var opt=speed&&speed.constructor==Object?speed:{complete:fn||!fn&&easing||jQuery.isFunction(speed)&&speed,duration:speed,easing:fn&&easing||easing&&easing.constructor!=Function&&easing};opt.duration=(opt.duration&&opt.duration.constructor==Number?opt.duration:jQuery.fx.speeds[opt.duration])||jQuery.fx.speeds.def;opt.old=opt.complete;opt.complete=function(){if(opt.queue!==false)jQuery(this).dequeue();if(jQuery.isFunction(opt.old))opt.old.call(this);};return opt;},easing:{linear:function(p,n,firstNum,diff){return firstNum+diff*p;},swing:function(p,n,firstNum,diff){return((-Math.cos(p*Math.PI)/2)+0.5)*diff+firstNum;}},timers:[],timerId:null,fx:function(elem,options,prop){this.options=options;this.elem=elem;this.prop=prop;if(!options.orig)options.orig={};}});jQuery.fx.prototype={update:function(){if(this.options.step)this.options.step.call(this.elem,this.now,this);(jQuery.fx.step[this.prop]||jQuery.fx.step._default)(this);if(this.prop=="height"||this.prop=="width")this.elem.style.display="block";},cur:function(force){if(this.elem[this.prop]!=null&&this.elem.style[this.prop]==null)return this.elem[this.prop];var r=parseFloat(jQuery.css(this.elem,this.prop,force));return r&&r>-10000?r:parseFloat(jQuery.curCSS(this.elem,this.prop))||0;},custom:function(from,to,unit){this.startTime=now();this.start=from;this.end=to;this.unit=unit||this.unit||"px";this.now=this.start;this.pos=this.state=0;this.update();var self=this;function t(gotoEnd){return self.step(gotoEnd);}t.elem=this.elem;jQuery.timers.push(t);if(jQuery.timerId==null){jQuery.timerId=setInterval(function(){var timers=jQuery.timers;for(var i=0;i<timers.length;i++)if(!timers[i]())timers.splice(i--,1);if(!timers.length){clearInterval(jQuery.timerId);jQuery.timerId=null;}},13);}},show:function(){this.options.orig[this.prop]=jQuery.attr(this.elem.style,this.prop);this.options.show=true;this.custom(0,this.cur());if(this.prop=="width"||this.prop=="height")this.elem.style[this.prop]="1px";jQuery(this.elem).show();},hide:function(){this.options.orig[this.prop]=jQuery.attr(this.elem.style,this.prop);this.options.hide=true;this.custom(this.cur(),0);},step:function(gotoEnd){var t=now();if(gotoEnd||t>this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;var done=true;for(var i in this.options.curAnim)if(this.options.curAnim[i]!==true)done=false;if(done){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;this.elem.style.display=this.options.display;if(jQuery.css(this.elem,"display")=="none")this.elem.style.display="block";}if(this.options.hide)this.elem.style.display="none";if(this.options.hide||this.options.show)for(var p in this.options.curAnim)jQuery.attr(this.elem.style,p,this.options.orig[p]);}if(done)this.options.complete.call(this.elem);return false;}else{var n=t-this.startTime;this.state=n/this.options.duration;this.pos=jQuery.easing[this.options.easing||(jQuery.easing.swing?"swing":"linear")](this.state,n,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update();}return true;}};jQuery.extend(jQuery.fx,{speeds:{slow:600,fast:200,def:400},step:{scrollLeft:function(fx){fx.elem.scrollLeft=fx.now;},scrollTop:function(fx){fx.elem.scrollTop=fx.now;},opacity:function(fx){jQuery.attr(fx.elem.style,"opacity",fx.now);},_default:function(fx){fx.elem.style[fx.prop]=fx.now+fx.unit;}}});jQuery.fn.offset=function(){var left=0,top=0,elem=this[0],results;if(elem)with(jQuery.browser){var parent=elem.parentNode,offsetChild=elem,offsetParent=elem.offsetParent,doc=elem.ownerDocument,safari2=safari&&parseInt(version)<522&&!/adobeair/i.test(userAgent),css=jQuery.curCSS,fixed=css(elem,"position")=="fixed";if(elem.getBoundingClientRect){var box=elem.getBoundingClientRect();add(box.left+Math.max(doc.documentElement.scrollLeft,doc.body.scrollLeft),box.top+Math.max(doc.documentElement.scrollTop,doc.body.scrollTop));add(-doc.documentElement.clientLeft,-doc.documentElement.clientTop);}else{add(elem.offsetLeft,elem.offsetTop);while(offsetParent){add(offsetParent.offsetLeft,offsetParent.offsetTop);if(mozilla&&!/^t(able|d|h)$/i.test(offsetParent.tagName)||safari&&!safari2)border(offsetParent);if(!fixed&&css(offsetParent,"position")=="fixed")fixed=true;offsetChild=/^body$/i.test(offsetParent.tagName)?offsetChild:offsetParent;offsetParent=offsetParent.offsetParent;}while(parent&&parent.tagName&&!/^body|html$/i.test(parent.tagName)){if(!/^inline|table.*$/i.test(css(parent,"display")))add(-parent.scrollLeft,-parent.scrollTop);if(mozilla&&css(parent,"overflow")!="visible")border(parent);parent=parent.parentNode;}if((safari2&&(fixed||css(offsetChild,"position")=="absolute"))||(mozilla&&css(offsetChild,"position")!="absolute"))add(-doc.body.offsetLeft,-doc.body.offsetTop);if(fixed)add(Math.max(doc.documentElement.scrollLeft,doc.body.scrollLeft),Math.max(doc.documentElement.scrollTop,doc.body.scrollTop));}results={top:top,left:left};}function border(elem){add(jQuery.curCSS(elem,"borderLeftWidth",true),jQuery.curCSS(elem,"borderTopWidth",true));}function add(l,t){left+=parseInt(l,10)||0;top+=parseInt(t,10)||0;}return results;};jQuery.fn.extend({position:function(){var left=0,top=0,results;if(this[0]){var offsetParent=this.offsetParent(),offset=this.offset(),parentOffset=/^body|html$/i.test(offsetParent[0].tagName)?{top:0,left:0}:offsetParent.offset();offset.top-=num(this,'marginTop');offset.left-=num(this,'marginLeft');parentOffset.top+=num(offsetParent,'borderTopWidth');parentOffset.left+=num(offsetParent,'borderLeftWidth');results={top:offset.top-parentOffset.top,left:offset.left-parentOffset.left};}return results;},offsetParent:function(){var offsetParent=this[0].offsetParent;while(offsetParent&&(!/^body|html$/i.test(offsetParent.tagName)&&jQuery.css(offsetParent,'position')=='static'))offsetParent=offsetParent.offsetParent;return jQuery(offsetParent);}});jQuery.each(['Left','Top'],function(i,name){var method='scroll'+name;jQuery.fn[method]=function(val){if(!this[0])return;return val!=undefined?this.each(function(){this==window||this==document?window.scrollTo(!i?val:jQuery(window).scrollLeft(),i?val:jQuery(window).scrollTop()):this[method]=val;}):this[0]==window||this[0]==document?self[i?'pageYOffset':'pageXOffset']||jQuery.boxModel&&document.documentElement[method]||document.body[method]:this[0][method];};});jQuery.each(["Height","Width"],function(i,name){var tl=i?"Left":"Top",br=i?"Right":"Bottom";jQuery.fn["inner"+name]=function(){return this[name.toLowerCase()]()+num(this,"padding"+tl)+num(this,"padding"+br);};jQuery.fn["outer"+name]=function(margin){return this["inner"+name]()+num(this,"border"+tl+"Width")+num(this,"border"+br+"Width")+(margin?num(this,"margin"+tl)+num(this,"margin"+br):0);};});})();
+ javascripts/preview.js view
@@ -0,0 +1,8 @@+function updatePreviewPane() {+ $("#previewpane").hide();+ $("#previewpane").load("/_preview", { "raw" : $("#editedText").attr("value") });+ $("#previewpane").fadeIn(1000);+};+$(document).ready(function(){+ $("#previewpane").before("<a onClick=\"updatePreviewPane();\">Preview</a>");+});
+ javascripts/search.js view
@@ -0,0 +1,13 @@+function toggleMatches(obj) {+ obj.next('.matches').slideToggle(300);+ if (obj.html() == '[show matches]') {+ obj.html('[hide matches]');+ } else {+ obj.html('[show matches]');+ };+ }+$(function() {+ $('a.showmatch').attr("onClick", "toggleMatches($(this));");+ $('pre.matches').hide();+ $('a.showmatch').show();+ });
+ stylesheets/folder.png view
binary file changed (absent → 498 bytes)
+ stylesheets/gitit.css view
@@ -0,0 +1,268 @@+/* elements */++body {+ background-color: #fff;+ color: #333;+ font-family: sans-serif;+ font-size: 0.95em;+ line-height: 1.6em;+ margin: 2em;+}++a {+ color: #6d7fa3;+ text-decoration: none;+}+a:visited { color: #7b69b0; }+a:hover { text-decoration: underline; }+a.notfound { background-color: yellow; }++code, pre {+ font-family: monospace;+ font-size: 1em;+}++pre {+ padding-left: 4px;+ border: 1px dashed gray;+}++h1 {+ color: #333;+ font-size: 1.8em;+ margin: 24px 0 0 0;+}++input[type=text] {+ border: 1px solid #ccc;+ font-family: sans-serif;+ font-size: 0.9em;+}++label { font-size: .9em; }++table {+ font-size: 0.9em;+ width: 100%;+}++textarea {+ border: 1px solid #ccc;+ font-family: monospace;+ font-size: 0.9em;+ padding: 5px;+}++/* classes */++.added { background-color: yellow; }+.deleted { text-decoration: line-through; color: gray; }++.clearer { clear: both; }++.content h1, .content h2, .content h3, .content h4 {+ padding: 0 0 .4em 0;+ margin: 0 0 0 0;+}+#content h1 { font-size: 1.3em; }+#content h2 { font-size: 1.15em; }+#content h3 { font-size: 1em; }+#content h4 { font-size: 1em; font-weight: normal; }++.edit textarea {+ display: block;+ margin-bottom: 5px;+ max-height: 300px;+ /* min-width: 100%; */+}++.nav_link, .nav_link:visited {+ display: inline;+ padding: 3px;+ color: #666;+}++.nav_link:hover {+ background-color: #d1ccdb;+ color: #333;+ text-decoration: none;+}++/* .req is used to hide a honeypot in a form */+.req {+ display: none;+}++ul.messages > li {+ color: red;+ list-style: square;+ font-weight: bold;+}++#pageinfo {+ padding: 4px;+ border-top: 1px solid #ccc;+ border-bottom: 1px solid #ccc;+}++#footer {+ clear: both;+ text-align: center;+ font-size: 80%;+ padding: 4px;+}++#footer a, #footer a:visited { color: #666; }++#navbar {+ border-top: 1px solid #ccc;+ border-bottom: 1px solid #ccc;+ padding: 4px;+ clear: both;+}++#userbox {+ float: left;+}++#pageTitle {+ float: left;+}++#loginForm {+}++div#toc {+ float: right; + border: 1px dashed gray;+ margin: 0.8em;+}+#toc ul {+ margin: 0;+ padding-left: 1em;+ list-style: none;+}+#toc > ul {+ margin-right: 1em;+}+.usernav {+ text-align: left;+ float: left;+}++.sitenav {+ text-align: right;+}++.right { text-align: right; }++.search_result { margin-bottom: 15px; }++.search_result .match {+ line-height: 1em;+ margin-bottom: 15px;+}++.search_term { color: #999; }++.submit {+ font-size: 1.2em;+ font-weight: bold;+}++.pageControls {+ float: right;+}++.details {+ color: #888;+ font-size: .85em;+ margin-left: 0.3em;+}+span.detail {+ color: #888;+ font-size: .85em;+}+pre.matches {+ font-size: .85em;+ margin: 0;+ padding: 0;+}++.folding ul {+ list-style: none;+ margin: 0;+ padding: 0;+}+.folding li {+ list-style: none;+ background-position: 0 1px;+ background-repeat: no-repeat;+ padding-left: 20px;+}+.folding li.page {+ background-image: url(page.png);+}+.folding li.folder {+ background-image: url(folder.png);+}++.folding a {+ color: #000000;+ cursor: pointer;+ text-decoration: none;+}+.folding a:hover {+ text-decoration: underline;+}++/* ids */++#banner {+ float: left;+}++#container {+ margin: auto;+ width: 90%;+}++#content {+ clear: both;+ /* margin-left: 12em; */+ padding: 10px 10px 2px 0px;+}++#pageTitle {+ clear: both;+}++#searchform {+ display: inline;+}++#editform label, #editform input[type=text] {+ display: block;+ width: auto;+ float: left;+ margin-right: 10px;+}++#editform br {+ clear: left;+}++#editform textarea {+ height: 25em;+ margin-top: 0em;+ width: 100%;+}+#message_textarea {+ height: 4em;+ width: 100%;+}++#search_field {+ border: 1px solid #ccc;+ color: #999;+}
+ stylesheets/hk-pyg.css view
@@ -0,0 +1,20 @@+/* Loosely based on pygment's default colors */+table.sourceCode, tr.sourceCode, td.lineNumbers, td.sourceCode, table.sourceCode pre + { margin: 0; padding: 0; border: 0; vertical-align: baseline; border: none; }+td.lineNumbers { border-right: 1px solid #AAAAAA; text-align: right; color: #AAAAAA; padding-right: 5px; padding-left: 5px; } +td.sourceCode { padding-left: 5px; }+pre.sourceCode { }+pre.sourceCode span.Normal { }+pre.sourceCode span.Keyword { color: #007020; font-weight: bold; } +pre.sourceCode span.DataType { color: #902000; }+pre.sourceCode span.DecVal { color: #40a070; }+pre.sourceCode span.BaseN { color: #40a070; }+pre.sourceCode span.Float { color: #40a070; }+pre.sourceCode span.Char { color: #4070a0; }+pre.sourceCode span.String { color: #4070a0; }+pre.sourceCode span.Comment { color: #60a0b0; font-style: italic; }+pre.sourceCode span.Others { color: #007020; }+pre.sourceCode span.Alert { color: red; font-weight: bold; }+pre.sourceCode span.Function { color: #06287e; }+pre.sourceCode span.RegionMarker { }+pre.sourceCode span.Error { color: red; font-weight: bold; }
+ stylesheets/page.png view
binary file changed (absent → 333 bytes)