summaryrefslogtreecommitdiff
path: root/macros/latex/contrib/webquiz/latex/webquiz.cls
blob: 29e6f22675e5858673a6eb03eba80e2d360b910b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
% -----------------------------------------------------------------------
%   webquiz.cls | webquiz latex class file
% -----------------------------------------------------------------------
%
%   Copyright (C) Andrew Mathas, University of Sydney
%
%   Distributed under the terms of the GNU General Public License (GPL)
%               http://www.gnu.org/licenses/
%
%   This file is part of the WebQuiz system.
%
%   <Andrew.Mathas@sydney.edu.au>
% ----------------------------------------------------------------------

\NeedsTeXFormat{LaTeX2e}[1996/12/01]

% load webquiz-ini style
\input{webquiz-ini.code}

% ----------------------------------------------------------------------
\ProvidesClass{webquiz}[\webquiz{release date} Version \webquiz{version}]

% initialise and then process document class options usng pgfopts
\RequirePackage{etoolbox}
\RequirePackage{pgfopts}
\def\unknown@options{}%          unknown options are passed to article.cls
\def\WQ@HideSideMenu{DeFaUlT}%   side menu visible by default
\newif\ifWQ@Tikz\WQ@Tikzfalse%   true if tikz a document class option

\newif\ifWQ@Debugging  % for the debugging class option
% Process the documentclass options using pgfkeys and \ProcessPgfOptions
\pgfkeys{/WQ@ClassOptions/.is family,
  /WQ@ClassOptions,
    % turn debugging on
    debugging/.code           = {\WQ@Debuggingtrue},
    % pst2pdf
    pst2pdf/.style            = {pst2pdf value=true},
    pst2pdf value/.initial    = false,
    % languaage
    language/.initial         = DeFaUlT,
    % one page
    onepage/.style            = {onepage value=true},
    separatepages/.style      = {onepage value=false},
    onepage value/.initial    = DeFaUlT,
    % side menu
    showsidemenu/.style       = {hidesidemenu value=false},
    hidesidemenu/.style       = {hidesidemenu value=true},
    hidesidemenu value/.initial= DeFaUlT,
    % random order of quiz questions
    randomorder/.style        = {randomorder value=true},
    fixedorder/.style         = {randomorder value=false},
    randomorder value/.initial = DeFaUlT,
    % theme
    theme/.initial             = DeFaUlT,
    % tikz
    tikz/.code                 = {\global\WQ@Tikztrue},
    % unknown options => passed to article class
    .unknown/.code={\edef\unknown@options{\unknown@options,\pgfkeyscurrentname}}
}
\newcommand\WQ@ClassOption[1]{\pgfkeysvalueof{/WQ@ClassOptions/#1}}

% Use pgfkeys to check for valid options for the choice environment and the
% answer comparisons. As \ERROR is not defined an error message is
% generated by the unknown handlers below
\pgfkeys{/webquiz checker/.is family, /webquiz checker,
  mode/.initial          = single,
  columns value/.initial = 1,
  columns/.style         = {columns value=#1},
  single/.style          = {mode=single},
  multiple/.style        = {mode=multiple},
  .unknown/.code         = {\ERROR: invalid choice type: use single or multiple}
  comparison/.is choice,
  comparison/complex/.code      = \relax,
  comparison/integer/.code      = \relax,
  comparison/lowercase/.code    = \relax,
  comparison/number/.code       = \relax,
  comparison/string/.code       = \relax,
  comparison/.unknown/.code     = {\ERROR: invalid input type: use integer, string or eval}
}

% now process the options
\ProcessPgfOptions{/WQ@ClassOptions}% process options
\LoadClass[\unknown@options]{article}% load article class with options

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\RequirePackage{xparse}
\RequirePackage{pgffor}

% load Michal Hoftich's new tikz driver for tex4ht
\ifWQ@Tikz
   \ifdefined\HCode
      \def\pgfsysdriver{pgfsys-dvisvgm4ht.def}
   \fi
\fi

\def\WQ@Error{\PackageError{webquiz}}
\def\WQ@Debugging#1{\ifWQ@Debugging\typeout{Debugging: #1}\fi}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\RequirePackage{amsfonts,amsmath}
\RequirePackage{bbding}
\setlength{\parindent}{0pt}
\setlength{\parskip}{10pt}

% Macro to define a picture variant of \<command>. The latex version does
% nothing but the cfg version converts \<command> into an image

\newcommand\DisplayAsImage[2][]{\relax}

% Need to declare possible graphics extensions: see https://tex.stackexchange.com/questions/213461
% This is just placeholder - the real definition is used in the cfg file
%\newcommand\DeclareGraphicsExtensions[1]{}

% Macros for defining the university and department
\newcommand*{\DepartmentURL}[1]{\def\WQ@departmentURL{#1}}
\newcommand*{\Department}[1]{\def\WQ@department{#1}}

% University/instution - university included for backwards compatibility
\newcommand*{\InstitutionURL}[1]{\def\WQ@institutionURL{#1}}
\newcommand*{\Institution}[1]{\def\WQ@institution{#1}}
\newcommand*{\UniversityURL}[1]{\def\WQ@institutionURL{#1}}
\newcommand*{\University}[1]{\def\WQ@institution{#1}}

% Macros for defining the quiz web breadcrumbs using the unit code, name and
% URL. The names of these macros are chosen so as to be compatible with the
% in-house SMS package sms-uos.sty that specifies these for all units.
\newcommand*\UnitCode[1]{\def\uos@code{#1}}
\newcommand*\UnitName[1]{\def\uos@name{#1}}
\newcommand*\UnitURL[1]{\def\uos@url{#1}}
\newcommand*\QuizzesURL[1]{\def\WQ@quizzesUrl{#1}}
\newcommand*\BreadCrumb[1]{\def\WQ@breadcrumb{#1}}
\newcommand*\BreadCrumbs[1]{\def\WQ@breadcrumbs{#1}}

% Default values - the university and department defaults can also be
% set in the webquizrc file. The main purpose of these defaults is
% to remind the user to set them.
\UnitCode{Unit code?}
\UnitName{Unit name?}
\UnitURL{}
\QuizzesURL{DeFaUlT}
\BreadCrumb{}
\BreadCrumbs{DeFaUlT}
\Department{DeFaUlT}
\DepartmentURL{DeFaUlT}
\University{DeFaUlT}
\UniversityURL{DeFaUlT}

% Define page header and footer for the printable version of the quiz
% to show the unit code information.
\def\ps@quiz{\ps@empty
  \def\@oddhead{\hbox to\textwidth{%
      \uos@code\hss\textsc{\@title}\hss Page \thepage}}%
  \let\@evenhead=\@oddhead
}
\def\ps@firstpage{\ps@empty}
\pagestyle{quiz}


\def\@title{}
\def\maketitle{\vspace*{10mm}
  \begin{center}
    {\large\textsc{\@title}}%
  \end{center}
}
\AtBeginDocument{
  \maketitle%
  \def\title{\WQ@Error{\noexpand\title can only be used in the preamble}\@ehc}
  \thispagestyle{firstpage}%
  \ifdefined\HCode\else
     \textsc{WebQuiz} will process this quiz using \textbf{pst2pdf}.
  \fi
  \RequirePackage{tikz}
}

% -----------------------------------------------------------------------
% WebQuiz macros
% Define \WQ@XXXStart and \WQ@XXXEnd macros to avoid code duplication
% in webquiz.cfg. The following switches are used for syntax checking
% -----------------------------------------------------------------------

% These are all for error checking...
\newif\ifWQ@InFeedback
\newif\ifWQ@InAnswer
\newif\ifWQ@InChoice
\newif\ifWQ@InDiscussion
\newif\ifWQ@InItem
\newif\ifWQ@InQuestion
\newif\ifWQ@InQuizIndex
\newif\ifWQ@InWhenright
\newif\ifWQ@InWhenwrong

% -----------------------------------------------------------------------
% the quizindex environment and \quiz macro
% -----------------------------------------------------------------------

% the quiz counter keeps track of the question numbers in a quizindex environement
\newcounter{quiz}
\renewcommand\thequiz{\arabic{quiz}}
\def\WQ@QuizStart{%
  \WQ@Debugging{Starting \noexpand\quiz...}%
  \ifWQ@InQuizIndex\else\WQ@Error{\noexpand\quiz must appear inside a quizindex environment}\fi%
  \refstepcounter{quiz}% increment the quiz counter for the URLs
}
\NewDocumentCommand\quiz{som}{%
  \WQ@QuizStart%
  \IfBooleanTF{#1}{\def\WQ@Quiz{}}{\def\WQ@Quiz{Quiz~\thequiz.\space}}%
  \IfNoValueTF{#2}{\def\WQ@url{quiz\thequiz.html}}{\def\WQ@url{#2}}%
  \leavevmode\medskip\newline%
  \WQ@Quiz #3\newline%
  \phantom{Quiz 10}\textit{URL}:\quad \WQ@url%
}

\def\WQ@QuizIndexStart{%
  \WQ@Debugging{Starting quizindex...}%
  \ifWQ@InQuestion\WQ@Error{quizindex cannot appear in a question environment}\fi%
  \ifWQ@InDiscussion\WQ@Error{quizindex environments cannot be inside a discussion environment}\fi
  \ifWQ@InQuizIndex\WQ@Error{quizindex environments cannot be nested}\fi%
  \global\WQ@InQuizIndextrue% here, and below, using \global is probably overkill...
}
\def\WQ@QuizIndexEnd{\global\WQ@InQuizIndexfalse\WQ@Debugging{Ending quizindex...}}
\newenvironment{quizindex}{\WQ@QuizIndexStart\bigskip}{\WQ@QuizIndexEnd}

% -----------------------------------------------------------------------
% discussion environment
% -----------------------------------------------------------------------

\newcounter{discussion}

% \begin{dicussion}[short title][title -default-> Discussion]
\def\WQ@DiscussionStart{%
  \WQ@Debugging{Starting discussion...}%
  \ifWQ@InDiscussion\WQ@Error{discussion environments cannot be nested}\fi%
  \ifWQ@InQuestion\WQ@Error{discussion environments cannot be inside a question environment}\fi%
  \ifWQ@InQuizIndex\WQ@Error{discussion environments cannot be inside a quizindex environment}\fi%
  \global\WQ@InDiscussiontrue%
  \refstepcounter{discussion}%
}
\def\WQ@DiscussionEnd{\global\WQ@InDiscussionfalse\WQ@Debugging{Ending discussion...}}
\NewDocumentEnvironment{discussion}{O{Discussion}O{#1}}
    {\WQ@DiscussionStart\textbf{#1} (#2)}{\WQ@DiscussionEnd\par\bigskip}

% ---------------------------------------------------------------------------
% Cross referencing question and discussion environments
% ---------------------------------------------------------------------------

% \WQ@Ref{+ or -}[optional *][optional text]{mandatory label}
\NewDocumentCommand\WQ@ref{ m s o m }{%
\fbox{\IfBooleanTF{#2}{link}{button} to \IfNoValueTF{#3}{to #1 \ref{#4}}{ to ``#3'' (#1 \ref{#4})}}%
}
\newcommand\dref{\WQ@ref{discussion}}
\newcommand\qref{\WQ@ref{question}}
\newcommand\Qref{\WQ@ref{question number}}

% -----------------------------------------------------------------------
% question environment
% -----------------------------------------------------------------------

% the question counter keeps track of the quiz question numbers
\newcounter{question}

\def\WQ@QuestionStart{%
  \WQ@Debugging{Starting question...}%
  \ifWQ@InDiscussion\WQ@Error{question environments cannot be inside a discussion environment}\fi%
  \ifWQ@InQuizIndex\WQ@Error{question environments cannot be inside a quizindex environment}\fi%
  \ifWQ@InQuestion\WQ@Error{question environments cannot be nested!}\fi%
  \global\WQ@InAnswerfalse%
  \global\WQ@InChoicefalse%
  \global\WQ@InChoicefalse%
  \global\WQ@InItemfalse%
  \global\WQ@InQuestiontrue%
  \global\WQ@InFeedbackfalse%
  \global\WQ@InWhenrightfalse%
  \global\WQ@InWhenwrongfalse%
  \refstepcounter{question}%
}

\def\WQ@QuestionEnd{
  \ifWQ@InChoice\else\ifWQ@InAnswer\else%
    \WQ@Error{Each question must contain a choice environment or an \noexpand\answer!}%
  \fi\fi%
  \global\WQ@InQuestionfalse%
  \WQ@Debugging{Ending question...}%
}

\newenvironment{question}{\WQ@QuestionStart\bigskip\textbf{Question \arabic{question}.\newline}}
                         {\par\bigskip\WQ@QuestionEnd}

% -----------------------------------------------------------------------
% choice environment, \correct and \incorrect choices and \feedback
% -----------------------------------------------------------------------

% the choice counter keeps track of the choices in a choice environment
\newcounter{choice}
\renewcommand\thechoice{(\alph{choice})}

\def\WQ@ChoiceStart#1{%
  \WQ@Debugging{Starting choice...}%
  \ifWQ@InQuestion\relax% Give a warning if we are not inside a question
  \else\WQ@Error{Choice environments must be contained in a question}%
  \fi%
  \ifWQ@InAnswer\WQ@Error{choice environments cannot contain an \noexpand\answer!}\fi%
  \ifWQ@InChoice\WQ@Error{choice environments cannot be nested}\fi%
  \global\WQ@InChoicetrue%
  \global\WQ@InItemfalse%
  \pgfkeys{/webquiz checker, #1}%
  \setcounter{choice}{0}% reset choice counter
}

\def\WQ@ChoiceEnd{%
  \ifWQ@InItem\else\WQ@Error{Choice environment with no choices!}\fi%
  \WQ@InChoicefalse%
  \WQ@Debugging{Ending choice...}%
}

% \begin{choice}[choice type, number of columns] ... \end{choice} The
% allowed choice types are "single" or "multiple". The type and number
% of columns is allowed to appear in either order, so most of the fun
% and games below is sanity checking of the argunments.
\NewDocumentEnvironment{choice}{O{single}}{%
  \WQ@ChoiceStart{#1}%
  \leavevmode\newline%
% (\textit{The choices will be printed in \pgfkeys{/webquiz checker/columns value} columns in the HTML version of the quiz})%
  \par%
  \setcounter{choice}{0}%
}{\WQ@ChoiceEnd}

\def\WQ@ItemStart{%
  \WQ@Debugging{Starting item...}%
  \ifWQ@InChoice\relax%
  \else\WQ@Error{\noexpand\correct and \noexpand\incorrect must be inside a choice environment}%
  \fi%
  \global\WQ@InItemtrue\global%
  \WQ@InFeedbackfalse%
  \refstepcounter{choice}%
}

\def\WQ@Item#1{\WQ@ItemStart\par\bigskip #1\quad\textit{Option} \thequestion\thechoice:\qquad}
\def\correct{\WQ@Item\CheckmarkBold}
\def\incorrect{\WQ@Item\XSolidBrush}

\def\WQ@FeedbackStart{%
  \WQ@Debugging{Starting feedback...}%
  \ifWQ@InItem\relax%
  \else\WQ@Error{Missing \noexpand\correct or \noexpand\incorrect}%
  \fi%
  \ifWQ@InFeedback\WQ@Error{Only one feedback per option is allowed!}\fi%
  \WQ@InFeedbacktrue%
}

\def\feedback{\WQ@FeedbackStart\leavevmode\medskip\newline\textit{Feedback:}\space}

% -----------------------------------------------------------------------
% The \answer macro and the feedback macros \whenRight, \whenWrong
% -----------------------------------------------------------------------

% supported answer comparisons
\def\WQ@AnswerStart#1{%
  \WQ@Debugging{Starting answer...}%
  \ifWQ@InQuestion\relax%
  \else\WQ@Error{\noexpand\answer\ must be contained in a question}%
  \fi%
  \ifWQ@InChoice\WQ@Error{You cannot have \noexpand\answer inside a choice environment}\fi%
  \ifWQ@InAnswer\WQ@Error{Each question can contain only one \noexpand\answer command}\fi%
  \pgfkeys{/webquiz checker/comparison/#1}%
  \global\WQ@InAnswertrue%
  \global\WQ@InWhenrightfalse%
  \global\WQ@InWhenwrongfalse%
}

\NewDocumentCommand\answer{s O{string} m}{%
  \WQ@AnswerStart{#2}%
  \leavevmode\bigskip\newline%
  \IfBooleanTF{#1}{\fbox{\textit{(#2 comparison):}\quad#3}}%
                  {\fbox{\textit{Answer (#2 comparison):}\quad#3}}%
}

\def\WQ@WhenStart#1{%
  \WQ@Debugging{Starting when#1...}%
  \ifWQ@InAnswer\relax\else\WQ@Error{missing \noexpand\answer}\fi%
  \ifbool{WQ@InWhen#1}{%
    \WQ@Error{Only one \noexpand\when#1 per question is allowed!}%
  }{}%
  \global\booltrue{WQ@InWhen#1}%
}

\def\WQ@When#1{\WQ@WhenStart{#1}\leavevmode\medskip\newline\textit{Feedback when #1:}\space}
\def\whenRight{\WQ@When{right}}
\def\whenWrong{\WQ@When{wrong}}

\endinput
%% End of file `webquiz.cls'.