{-# LANGUAGE DeriveDataTypeable #-}{-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-}moduleGraphics.UI.Threepenny.Internal.TypeswhereimportPreludehiding(init)importControl.ApplicativeimportControl.ConcurrentimportqualifiedControl.EventasEimportControl.Monad.ReaderimportData.ByteString(ByteString)importData.Map(Map)importData.TimeimportNetwork.URIimportText.JSON.Generic{-----------------------------------------------------------------------------
Public types
------------------------------------------------------------------------------}-- | Reference to an element in the DOM of the client window.dataElement=Element{elId::ElementId,elSession::Session}instanceShowElementwhereshow=show.elId-- | An opaque reference to an element in the DOM.dataElementId=ElementIdStringderiving(Data,Typeable,Show)instanceJSONElementIdwhereshowJSON(ElementIdo)=showJSONoreadJSONobj=doobj<-readJSONobjElementId<$>valFromObj"Element"obj-- | A client session. This type is opaque, you don't need to inspect it.dataSession=Session{sSignals::ChanSignal,sInstructions::ChanInstruction,sEvent::EventKey->E.EventEventData,sEventHandler::E.Handler(EventKey,EventData),sClosures::MVar[Integer],sElementIds::MVar[Integer],sToken::Integer,sMutex::MVar(),sConnectedState::MVarConnectedState,sThreadId::ThreadId,sStartInfo::(URI,[(String,String)]),sServerState::ServerState}typeSessions=MapIntegerSessiontypeMimeType=ByteStringtypeFilepaths=(Integer,MapByteString(FilePath,MimeType))dataServerState=ServerState{sSessions::MVarSessions,sFiles::MVarFilepaths,sDirs::MVarFilepaths}typeEventKey=(String,String)-- | The client browser window.typeWindow=SessiondataConnectedState=DisconnectedUTCTime-- ^ The time that the poll disconnected, or-- the first initial connection time.|Connected-- ^ The client is connected, we don't care-- since when.deriving(Show)-- | Data from an event. At the moment it is empty.dataEventData=EventData[MaybeString]-- | Record for configuring the Threepenny GUI server.dataConfig=Config{tpPort::Int-- ^ Port number.,tpCustomHTML::MaybeFilePath-- ^ Custom HTML file to replace the default one.,tpStatic::FilePath-- ^ Directory that is served under @/static@.}{-----------------------------------------------------------------------------
Communication between client and server
------------------------------------------------------------------------------}-- | An instruction that is sent to the client as JSON.dataInstruction=DebugString|Begin()|End()|SetTokenInteger|Clear()|GetElementsById[String]|GetElementsByTagNameString|SetStyleElementId[(String,String)]|SetAttrElementIdStringString|AppendElementIdElementId|SetTextElementIdString|SetHtmlElementIdString|BindStringElementIdClosure|GetValueElementId|GetValues[ElementId]|SetTitleString|GetLocation()|RunJSFunctionString|CallJSFunctionString|CallDeferredFunction(Closure,String,[String])|EmptyElElementId|DeleteElementIdderiving(Typeable,Data,Show)instanceJSONInstructionwherereadJSON_=error"JSON.Instruction.readJSON: No method implemented."showJSONx=toJSONx-- | A signal (mostly events) that are sent from the client to the server.dataSignal=Init()|Elements[ElementId]|Event(String,String,[MaybeString])|ValueString|Values[String]|LocationString|FunctionCallValues[MaybeString]|FunctionResultJSValuederiving(Show)instanceJSONSignalwhereshowJSON_=error"JSON.Signal.showJSON: No method implemented."readJSONobj=doobj<-readJSONobjletinit=Init<$>valFromObj"Init"objelements=Elements<$>valFromObj"Elements"objevent=do(cid,typ,arguments)<-valFromObj"Event"objargs<-mapMnullableargumentsreturn$Event(cid,typ,args)value=Value<$>valFromObj"Value"objlocation=Location<$>valFromObj"Location"objvalues=Values<$>valFromObj"Values"objfcallvalues=doFunctionCallValues<$>(valFromObj"FunctionCallValues"obj>>=mapMnullable)fresult=FunctionResult<$>valFromObj"FunctionResult"objinit<|>elements<|>event<|>value<|>values<|>location<|>fcallvalues<|>fresult-- | Read a JSValue that may be null.nullable::JSONa=>JSValue->Result(Maybea)nullableJSNull=returnNothingnullablev=Just<$>readJSONv-- | An opaque reference to a closure that the event manager uses to-- trigger events signalled by the client.dataClosure=Closure(String,String)deriving(Typeable,Data,Show){-----------------------------------------------------------------------------
JavaScript Code and Foreign Function Interface
------------------------------------------------------------------------------}-- | JavaScript code snippet.newtypeJSCode=JSCode{unJSCode::String}deriving(Eq,Ord,Show,Data,Typeable)-- | Class for rendering Haskell types as JavaScript code.classToJSawhererender::a->JSCodeinstanceToJSStringwhererender=JSCode.showinstanceToJSIntwhererender=JSCode.showinstanceToJSBoolwhererenderb=JSCode$ifbthen"false"else"true"instanceToJSJSValuewhererenderx=JSCode$showJSValuex""instanceToJSElementIdwhererender(ElementIdx)=apply"elidToElement(%1)"[renderx]instanceToJSElementwhererender(Elemente_)=rendere-- | Representation of a JavaScript expression-- with a girven output type.dataJSFunctiona=JSFunction{code::JSCode-- code snippet,marshal::Window->JSValue->Resulta-- convert to Haskell value}instanceFunctorJSFunctionwherefmapf=fmapWindow(constf)fmapWindow::(Window->a->b)->JSFunctiona->JSFunctionbfmapWindowf(JSFunctioncm)=JSFunctionc(\wv->fw<$>mwv)fromJSCode::JSCode->JSFunction()fromJSCodec=JSFunction{code=c,marshal=\__->Ok()}-- | Helper class for making a simple JavaScript FFIclassFFIawherefancy::([JSCode]->JSCode)->ainstance(ToJSa,FFIb)=>FFI(a->b)wherefancyfa=fancy$f.(rendera:)instanceFFI(JSFunction())wherefancyf=fromJSCode$f[]instanceFFI(JSFunctionString)wherefancy=mkResult"%1.toString()"instanceFFI(JSFunctionJSValue)wherefancy=mkResult"%1"instanceFFI(JSFunctionElementId)wherefancy=mkResult"{ Element: elementToElid(%1) }"instanceFFI(JSFunctionElement)wherefancy=fmapWindow(\welid->Elementelidw).fancymkResult::JSONa=>String->([JSCode]->JSCode)->JSFunctionamkResultclientf=JSFunction{code=applyclient[f[]],marshal=\w->readJSON}-- | Simple JavaScript FFI with string substitution.---- Inspired by the Fay language. <http://fay-lang.org/>---- > example :: String -> Int -> JSFunction String-- > example = ffi "$(%1).prop('checked',%2)"---- The 'ffi' function takes a string argument representing the JavaScript-- code to be executed on the client.-- Occurrences of the substrings @%1@ to @%9@ will be replaced by-- subequent arguments.---- Note: Always specify a type signature! The types automate-- how values are marshalled between Haskell and JavaScript.-- The class instances for the 'FFI' class show which conversions are supported.--ffi::FFIa=>String->affimacro=fancy(applymacro)testFFI::String->Int->JSFunctionStringtestFFI=ffi"$(%1).prop('checked',%2)"-- | String substitution.-- Substitute occurences of %1, %2 up to %9 with the argument strings.-- The types ensure that the % character has no meaning in the generated output.-- -- > apply "%1 and %2" [x,y] = x ++ " and " ++ yapply::String->[JSCode]->JSCodeapplycodeargs=JSCode$gocodewhereargumenti=unJSCode(args!!i)go[]=[]go('%':c:cs)=argumentindex++gocswhereindex=fromEnumc-fromEnum'1'go(c:cs)=c:gocs