LogoLogo
ClausesDatafieldsSpecial FunctionsStylingQ&AAPI
  • Welcome!
  • Getting started
    • What is Clause9?
    • Structuring your clause library
    • Structuring your clauses
    • Drafting modes in Clause9
    • Creating a questionnaire
    • Sample clauses
    • Videos
      • Concepts and datafields
      • Conditions
      • Q&As
      • Binders
      • Styling
      • Enumerations
      • Tables
      • Definitions
      • Snippets
      • Cross-references
      • Special functions
      • Examples of common clauses
      • Import clauses from MS Word
      • Grammatical conjugations
      • Action buttons
      • Alternative clauses
  • Assemble document
    • Document toolbar
    • Clause hierarchies
    • Focus Mode
    • Bulk generation of documents
    • Exporting documents
    • Assemble Document - FAQ
    • How to: Assemble Document
      • Insert images
  • Assemble Document Operations Panel
    • Operations panel
    • File pane
    • Edit pane
    • Document pane
    • Binder pane in the operations panel
    • Search pane
    • Browse pane
    • Terms pane
    • Data dashboard
    • Advanced pane
    • Styling pane
    • Miscellaneous pane
    • Visibility settings & actions menu
  • Binders
    • Binders: general
    • Styling cross-references to subdocuments
    • Global and local definition lists
    • Document and binder properties
    • Styling of a Binder versus subdocuments
    • (Un)locking documents in a binder
    • Binders - FAQ
    • How to: binders
      • Make a subdocument in a binder conditional
  • Clauses
    • Introduction to clauses
    • Clause structure
    • Grammar sheet
    • Writing conditions
    • Examples of conditions
    • Using codes instead of text fragments
    • Bold, italic and underline
    • Special codes
    • Enabled?
    • Links
    • Cross-references
    • Introduction to tables
    • Deviating table styling
    • Shrinking clauses
    • Action buttons
    • Enumerations
    • File position
    • Snippets
    • Parameters
    • Conjugations
    • Mixing data types
    • For-loops
    • Clause versioning
    • Abstract article references
    • Advanced multi-language features
    • Clauses - FAQ
    • How to: clauses
      • Create an ad-hoc clause
      • Create a library clause
      • Make a clause repeat
      • Make a paragraph within a clause conditional
      • Use a shortcut to refer to a concept
      • Insert a line break or page break
      • Creating a list with both predefined options and free input
      • Defining alternative clauses
      • Creating cross-references
      • Creating signature blocks
      • Creating advanced party introduction clauses
      • Automatically numbered annexes or schedules
      • Reuse any clause in a different context
      • Setting MS Word document properties
      • Add action buttons to clauses
      • Electronically signing documents
  • concepts
    • Introduction to concepts
    • Creating concepts
    • Concept labels
    • Links
    • Organising concepts
    • Concepts - FAQ
    • How to: concepts
      • Add predefines to a datafield
  • Datafields
    • Introduction to datafields
    • Types of datafields
    • Rules of thumb for using datafields
    • Data-expressions
    • Datafield aliases
    • Datafield labels
    • Datafield special tags
    • Datafield descriptions
    • Repeating list datafields
    • Datafield predefines
    • Datafields - FAQ
    • How to: datafields
      • Change datafield type
      • Change the datafield's name or alias
  • Definitions
    • Introduction to definitions
    • How to: definitions
      • How do definitions work?
      • Create a definition
  • Files
    • How files are organised
    • Browse files
    • File types
    • Custom styling
    • Legal comments
    • File description
    • Attributes
    • Reporting
    • File name
    • File category
    • Access rights
    • How to: files
      • Creating advanced folders
      • Naming your files
      • Shortcuts to folders or files
  • Q&A
    • About cards
    • Cards pane
    • About changes
    • Changes pane
    • Types of changes
    • Adding conditions
    • Question options
    • Copying & pasting answers
    • Comments, notes & documentation
    • Interactive Q&A inspection
    • Embedding questions into a document
    • “Changes” button
    • Batch create pane
    • Identifiers pane
    • Import pane
    • Edit clauses pane
    • Q&A options
    • Q&A - FAQ
    • How to: Q&A
      • Create predefined answers to a question
      • Add disclaimers
      • Create categories of questions
      • Modify the exported filename
      • Create a question to change the language of a document
      • Send a questionnaire to someone without a ClauseBase account
      • Create questions for repeating list datafields
      • Selecting legal entities & addresses
      • Create a questionnaire using "batch create"
      • Launch other Q&As
    • Leveraging ClauseBuddy Smart Templates in Clause9
  • Import
    • Introduction to importing clauses
    • Uploading clauses
    • Defined terms in Import mode
    • Datafields in Import mode
    • Cross-references in Import mode
    • Assigning folders
    • Conversion process
    • Exporting
    • Stashing intermediate results
    • Tips, tricks & limitations
  • Styling
    • Styling overview
    • Base styling
    • Numbering
    • Definitions styling
    • Enumerations styling
    • Locale styling
    • References styling
    • Page styling
    • Styling of a Binder versus subdocuments
    • Styling: tips and tricks
    • Advanced styling topics
    • Copying headers and footers from an MS Word file
    • How to: styling
      • Using custom fonts
      • Change bullet styling
  • Special functions
    • Introduction
    • Calculations
    • Concepts
    • Conditions
    • Conjugations
    • Content Control Elements
    • Datafields
    • Dates & durations
    • Languages
    • Lists
    • Numbers
    • References
    • Repeating (looping)
    • Special items
    • Text structure
    • Text modification
    • User
    • Q&A
  • Settings
    • Account
    • Preferences
    • Access bundles
    • Favourites
    • Saved searches
    • Saved datafields
    • Styles
    • Default styles
  • Admin
    • General
    • Users
    • User rights
    • Profiles
    • Groups
    • Styles
    • Default styles
    • Attribute models
    • Usage page
    • Custom homepage
    • Global placeholders
    • Access rights
    • How to: admin
      • Adding a new user
      • Disabling a user
      • Managing group memberships
  • Miscellaneous
    • Advanced tips & tricks
    • Typing special symbols on your keyboard
    • Shortcuts
    • Grammar style guide
    • Inserting MS Word files
    • Globo-panel
    • Creating high-quality documents
    • Excel calculations and lookups
  • Integrations
    • Overview
    • Spreadbases
    • E-signing documents
    • Drag & drop integrations
  • For developers
    • Clause9 API
    • Custom functions
    • Example custom functions
Powered by GitBook
On this page
  • How to get started
  • Technical setup and limitations
  • Accessing custom functions within a clause
  • Using custom functions in the Q&A environment
  • Condition attached to a card, question, predefined answer or change set
  • Customising the properties of a question, card or predefined answer
  • Validating answers
  • Executing actions upon setting an answer
  • Building a custom question interface
  • Centralising custom functions
  • Executing custom functions
  • Accessing Clojure functions
  • Clause9 grammar data structures
  • Other data structures
  • Clause9-specific functions
  • Debugging
  • Data structures
  • Q&A
  • Export of documents
  • Datafields, defined terms and custom function calls
  • Conversion functions
  • String functions
  • Mathematical functions
  • Collection-related additional functions
  • AJAX/get
  • Other functions
  • Building Q&A interfaces using “custom blocks”
  • Tips & Tricks
Export as PDF
  1. For developers

Custom functions

PreviousClause9 APINextExample custom functions

Last updated 14 days ago

While Clause9 provides an array of powerful features to build documents, it can sometimes be useful to resort to a full-fledged programming language.

For such situations, Clause9 allows you to develop functions and conditions in (a subset of) the , and its close sibling .

How to get started

Clojure is one of the most powerful programming languages in the world. Although it is much less popular than the mainstream programming languages — Java, C#, C, Python, etc. — and its LISP-based, functional nature will be quite unfamiliar to many developers, it is actually not so difficult to get started with in the context of Clause9.

  • For a quick introduction, we recommend to read the .

  • For a more in-depth introduction, we recommend to read Chapters 3, 4 and 5 of the excellent (and free) book .

Technical setup and limitations

Clause9 exposes only a subset of the Clojure / ClojureScript, through the . What you write, will run in either a browser environment (for the graphical user interface), or in a server environment (when exporting documents, e.g. to .DOCX). For the sake of conciseness, we will however only refer to Clojure in the remainder of this document.

Most of the core functions of Clojure are supported. In addition, many other Clause9-specific functions are available to you (see below), to make use of the advanced features of Clause9.

The following Clojure features are either irrelevant, or simply not available to you, when developing in Clojure from within Clause9. When reading about Clojure, you can therefore completely skip any discussions about them:

  • namespaces — within Clause9, your functions are hosted in more confined containers that are given a specific name

  • the so-called “REPL” — instead you can get some interactive output through your browser’s development tools

  • multi-threading tools, such as “futures”, “promises”, threads, pmaps, etc.

  • calls to the host environment (the JDK at server side, the DOM/JS environment at browser-side)

  • any asynchronous function calls, with the exception of some HTTP calls in the Q&A mode at browser-side

  • most functions that cause side-effects

Be aware that the invocation of custom functions is handled as a black box. With the exception of the println function for debugging, and a few exceptions in the Q&A environment (e.g., calls to set-answer, focus, and show-error/warning/info), the execution of Clojure-functions cannot modify the Document/Binder/questions, and will not cause side effects.

Accessing custom functions within a clause

If your user profile allows you to do so, you can add one or more custom functions to any clause. A custom function must be given a unique name within the file, and can then be called from the content title, content body or enabled-condition, using either the special function @clj or @cljr.

The only difference between those @functions is that the @cljr will always re-execute upon any recalculation of the underlying Document/Binder, which can be useful if the output of the custom function does not exclusively depend on the other arguments passed to @clj. (Clause9 will automatically recalculate when one of the arguments passed to @clj would change, but since the Clojure-functions act as a kind of “black box” towards the recalculation engine, it cannot know in which other situations it should recalculate — hence the need for @cljr).

  • The optional first argument should be a hashtag that refers to a file. When left out, the invoked custom function will be presumed to be present in the same clause.

  • The second argument should be the name of the custom function.

  • The optional other arguments act as parameters to the custom function.

For example, @clj(#xxx, "alpha", 5, 1 day) would call custom function “alpha” within the file referenced by hashtag #xxx, passing as arguments the number 5 and the duration 1 day to that custom function. Conversely, @clj("beta", 6) would call custom function “beta” in the same clause file, passing argument 6 to that custom function.

Within the custom function (i.e., within the Clojure environment), arguments passed to it can be accessed through the vars $1, $2, $3, etc. If several parameters are used, you probably want to define another var for the sake of clarity — e.g., (def contract-value $1) — and then use Clojure-var contract-value instead of the cryptic $1.

Using custom functions in the Q&A environment

Custom functions can be used in several places in the Q&A environment.

Condition attached to a card, question, predefined answer or change set

Any (sub)condition can refer to a custom function in Clojure. You simply choose “custom function” in the condition type, and insert some Clojure code. This code eventually needs to return a “truthy” value in the traditional Clojure sense (i.e., anything besides false or nil will result in true).

Customising the properties of a question, card or predefined answer

While Clause9 allows you to finetune the properties of a card(e.g., its title, color or indentation), a question (e.g., its title or help text) or predefined answer (e.g., its label or value), it is not possible to change these properties dynamically. Custom functions can provide relief, however.

The customisations, if set, are calculated right before the card/question/predefined-answer is being shown. The Clojure code gets passed the current record (card, question or predefined answer), can optionally modify that record, and return it.

As is customary in Clojure, Clause9 does not hide the fact that there are several other fields present in the record besides the fields mentioned below. Please refrain from changing these properties, as they are internal implementation details that are crucial for a correctly functioning Q&A.

You customise a card by showing the card’s options and clicking on the add card customization button. The Clojure code you insert, should then return a modified version of the predefined card var that is made available to the Clojure runtime.

The following card properties can be changed:

  • :title, an i18-map

  • :separator? to show a dividing line above a card

  • :color to determine the color of the card — :green, :red, :orange, :grey, :black, :brown, :purple, :magenta, :light-blue, or :dark-blue

  • :indent-left, which can be set to 1, 2 or 3 to indent the card

Similarly, you can customise a question by showing the question’s options and clicking on the add question customization button. The Clojure code you insert should then return a modified version of the predefined question var that is made available to the Clojure runtime.

The following question properties can be changed:

  • :title and :help are i18-maps

  • (number questions only) the :min-value and :max-value integer properties

  • (text questions only) the :textarea? boolean property, which corresponds to the show a large answer box setting of a question

  • :storage-only? hides a question, even if it is active (i.e., its own condition and the condition of its card, if any, is met)

  • :disallow-free-answering? can be used to disable free answers, allowing only predefined answers

  • :on-apply-service-instance is a function that takes the following arguments: the question from which a service-integration (e.g., spreadbase) is being launched; the row-index (zero-based, nil if the question is not a table-based answer), the col-index (also zero-based and nil if the question is not a table-based answer) and a map with data that is selected by the end-user.

    If this custom function returns a truthy value, the normal distribution of the selected value will follow its course (i.e., copying the relevant value to those questions that have an appropriate receive tag). If instead a non-truthy value is returned, the normal distribution is skipped.

In addition, the following properties can be set for table-based questions:

  • :force-direction can be set to :horizontal or :vertical to force the table to be shown in a horizontal or vertical manner. Please note that when you set :column-pred-fn or :column-caption-fn, the direction will be automatically forced to :vertical.

  • :column-pred-fn is a function that receives as arguments a question, row-index (integer), column-index (integer) and answers-map (map from row-index to (map of col-index to value)). It should return true/false to indicate whether the column should be shown.

  • :column-caption-fn is a function that receives as arguments a question, row-index (integer), column-index (integer), answers-map (map from row-index to (map of col-index to value)), the default caption, and the language (keyword) in which the questions are currently displayed. It should return a string.

  • :on-add-row-fn is triggered when the user clicks the green “+” button to add a new row. It receives as arguments the current question, the new number of rows (i.e., the old number of rows + 1) and the answers-map. It is expected to return a (probably updated) version of the answers map in which, presumable, at least one new row is inserted.

You can customise a predefined answer by clicking on the customisation icon at the right side. The Clojure code you insert, should then return a modified version of the predefined predef var that is made available to the Clojure runtime.

The following properties can be set:

  • the :value field, for which the datatype depends on the type of question (boolean, integer, FloatValue, CurrencyValue, string, i18-map, Date or Duration)

  • the :title, which is an i18-map. Note that if no :title is available, Clause9 will use a string-representation of the value as the title (label) to be presented towards the end-user.

Instead of customising an already-defined predefined answer, you can also dynamically change the list of predefined answers that should be shown. This is done at the level of the question, by setting the :predef-values|labels-fn field of the question var that is passed to the Clojure runtime.

  • The arguments it receives are the Question, row-index (integer), column-index (integer), answers-map (map from row-index to (map of col-index to value)), default predefined values and default predefined labels.

  • It should return a tuple of [predefined-values, predefined-labels].

Validating answers

It is possible to validate “free text” answers of by setting the :validate-fn field of a question. When set, this function receives as its arguments the question and the new value. It should then return a “truthy” value to indicate whether or not the value should be accepted.

If the value is not accepted, it is probably also a good idea to invoke the (show-error …) or (show-warning …) functions discussed below, to alert the user that the value was rejected.

Executing actions upon setting an answer

When an answer is set (optionally preceded by a validation phase), it is possible to execute a custom function. The typical scenario is that some other answers should also be changed, e.g. to avoid inconsistency.

Such additional action can be triggered by setting the :action-fn field of a question. The arguments it receives are the question that was answered, as well as the value that was set. After the function has executed, recalculation is automatically performed, so it is not necessary to invoke (recalc) manually.

Building a custom question interface

You can build custom interfaces for questions through a custom interface function.

Centralising custom functions

When a custom function should be invoked by different parts of a Q&A, it is probably a good idea to store it centrally, to avoid having to manually change the same function in multiple locations.

Custom functions are centrally managed in the repository pane, subsection custom functions.

Once centrally stored, custom functions can be invoked through (call ...). For example, to invoke the custom function called “custom-function2” in the screen shot above, with arguments 5 and 33, you would use (call "custom-function2", 5, 33).

Executing custom functions

Within the custom function you can write Clojure functions however you like — within the limitations set forth above. The result of the custom function should be one of the following:

  • in the Q&A environment for conditions: a “truthy” value in the traditional Clojure sense (i.e., anything besides false or nil will result in true)

  • in the Q&A environment for a custom interface: a Hiccup-style vector (see explanations below)

  • in the clause environment:

    • nil

    • a string

    • an IntValue, FloatValue or CurrencyValue

    • Clojure integer or double

    • a Date or Duration

    • any of the Clause9 grammar data structures (see below)

    • a vector of any of the above

Accessing Clojure functions

  • All clojure.core functions are accessible without a namespace alias.

  • The clojure.string functions are aliased as str.

  • Printing output to the browser’s console is done using pprint.

In addition, see the specific Clause9 functions described below.

Clause9 grammar data structures

The following data structures for constructing grammars are available, to allow you to programmatically define paragraphs, bullets, tokens etc. — as if you would be constructing them using the regular Clause9 grammar style.

Terminology for understanding the functions below: a “token” is a part of a sentence, such as a defined term or some plain text, while a “record” is any kind of element that can be created using the functions below.

  • paragraph construction

    • ->Paragraph creates a plain paragraph block without a number or bullet. Parameters: one or more tokens.

    • ->Paragraphs creates a collection of (plain, numbered or bulleted) paragraphs. Parameters: one or more paragraphs.

    • ->NrParagraph creates a numbered paragraph. Parameters:

      • either a number, or a collection of numbers

      • one or more tokens

    • ->BulletParagraph creates a bulleted paragraph. Parameters:

      • the bullet-level (integer)

      • one or more tokens

    • ->Snippet creates a snippet, which is a container for various records. The parameters consist of one or more tokens.

    • ->InsertionGroup is a container for various other records. Parameters:

      • a vector of elements

    • ->Endnote and ->Footnote create an endnote or footnote, respectively. The sole parameter is an insertion-group.

  • token constructionin general

    • ->Alert creates an alert record, e.g. for warnings. Its sole parameter is the body of the alert (string).

    • ->DynamicNr creates a dynamic number, i.e. a number that is either printed as a number or spell out in full. The sole argument is an integer.

    • ->ImageFile creates an image token that refers to an existing file. The sole parameter is a defined-term that links to that file (see term below).

    • ->ImageURL creates an image token that refers to some URL on the internet. The sole parameter is a string with the URL.

  • references creation

    • ->ConceptImplReference and ->ConceptDefReference create a reference, respectively to a Concept’s implementation clause or definition. Parameters:

      • a defined term, retrieved through term

      • the capitalization — either :none, :initial, :all-words or :all

    • ->MainBodyReference creates a reference to the main body of a Binder. No parameter.

    • ->TagReference creates a reference to a clause that implements a tag. The sole parameter is this tag (string).

  • token modification

    • ->Capitalizator modifies the capitalisation of the text within its element. Parameters:

      • some record

      • one of the following keywords: :none, :initial, :all-words or :all

    • ->Bold, ->Italic and ->Underlined creates a special element in MS Word. The parameters should consist of one or more tokens.

  • tableconstruction

    • ->Table constructs a table. It takes one or more table-rows as its parameters.

    • ->Row constructs a table-row. It takes one or more cells as its parameters.

    • ->Cell constructs a table cell. It takes one or more tokens as its parameters.

  • predefined answers in Q&A

    • The following functions create predefined answers, for pre-populating a question. For each of those functions, the ID parameter must be a negative integer between 0 and -100000, and this ID must be unique among the predefined values of that question.

    • ->BoolAnswer takes an ID and true/false

    • ->IntAnswer takes an ID and an integer

    • ->FloatAnswer takes an ID and a FloatValue

    • ->CurrencyAnswer takes an ID and a CurrencyValue

    • ->StringAnswer takes an ID and a string

    • ->i18StringAnswer takes an ID and an i18-map

    • ->DateAnswer takes an ID and a Date value

    • ->DurationAnswer takes an ID and a Duration value

Other data structures

  • ->IntValue creates an IntValue. The sole parameter is an integer.

  • ->FloatValue creates a FloatValue. The sole parameter is an integer that represents the floating value with 4 decimals — e.g. 123456 represents 123.456

  • ->CurrencyValue creates a CurrencyValue. The parameters are:

    • an integer that represents the value with 4 decimals — e.g. 123456 represents 123.456

    • a keyword that refers to the currency — currently :EUR, :GBP, :USD, :JPY, :INR or :CAD

  • ->Duration creates a Duration value. The parameters are:

    • an amount, a positive integer

    • a time-unit: either :year, :month, :day, :week or :quarter

  • ->Date creates a Date value. The parameters are three integers, respectively for year / month / day.

  • an “i18-map” is a Clojure map in which strings are stored for multiple languages. The key is a two-letter keyword: depending on the subdomain on which Clause9 is hosted, this will be :en, :fr, :nl, :de, :es or :lt

Clause9-specific functions

Debugging

  • ? prints its argument to the browser console, and then returns its argument

  • Clojure’s standard println function is also available for printing to the browser’s console

Data structures

  • check for identity with any of the following functions:

    • Alert?

    • Bold?

    • BoolAnswer?

    • BulletReference?

    • Capitalizator?

    • Cell?

    • ConceptDefReference?

    • ConceptImplReference?

    • Conjugation?

    • CurrencyAnswer?

    • CurrencyValue?

    • Date?

    • DateAnswer?

    • DefinedTerm?

    • Duration?

    • DurationAnswer?

    • DynamicNr?

    • Enumeration?

    • EnumSnippet?

    • FloatAnswer?

    • FloatValue?

    • i18StringAnswer?

    • Image?

    • IntAnswer?

    • IntValue?

    • Italic?

    • LineBreak?

    • MainBodyReference?

    • NrsetReference?

    • Paragraph?

    • PlainText?

    • Row?

    • Snippet?

    • StringAnswer?

    • Superfluous?

    • Tab?

    • Table?

    • TagReference?

    • TermField?

    • ThisArticleReference?

    • Underlined?

Q&A

  • card? checks whether the given identifier refers to an enabled card

  • question? checks whether the given identifier refers to an enabled question

  • change-set? checks whether the given identifier refers to an enabled change-set

  • answer results in the current value (answer) of the question referred to by the given identifier. Note that the answer to a table-based question is structured as {row-index {col-index value}}.

  • set-answer allows you to modify the change the answer to a given question. The first argument should be the question-identifier (string), the second parameter either a value or an update-function on the current value. You should also invoke recalc once you are done changing answers (except if the set-answer was invoked as part of an :action-fn, because action-fn automatically invokes recalc).

  • show-error, show-warning and show-info allow you to show alerts at the bottom of the browser screen, in different styles. Their sole argument is the message (string).

  • focus (sole argument is the question’s identifier) allows you to highlight and then focus on a specific question

  • gui-lang and output-lang return the current language used in the cards or output, respectively

In the bullets above, “identifier” refers to the optional identifier that can be given to cards and questions (accessible through the card’s or question’s options). The identifier should be a unique string that allows to immediately pinpoint a certain card or question.

Export of documents

  • export-docx (no arguments) renders the document at the server and then downloads the document as DOCX in the user's browser

  • export-pdf (no arguments) renders the document at the server and then downloads the document as PDF in the user's browser

  • export-docx-in-browser (no arguments) renders the document within the browser and downloads the document as DOCX in the user's browser

  • export-pdf-in-browser (no arguments) renders the document within the browser downloads the document as PDF in the user's browser

  • export-post renders the document at the server and subsequently sends the document to some webhook. The following arguments must be passed in a map:

    • :url mandatory, the URL where to perform the POST

    • :format mandatory: either :docx or :pdf or :html

    • :additional-data optional, a map with data with some custom internal information that will be passed on to the webhook. You may, for example, want to pass on some internal identifier as a string, or perhaps a map with numbers, or ... Note that the exportIdentifier that you can include when creating a magic-link through the API (called the identifier for reporting purposes in the GUI dialog box for creating a magic-link) will automatically be included as the :export-identifier in this map)

    • :on-success optional, a function (no params required) that will be executed client-side when the rendering & submission is successful

    • :sign-positions? optional: if set to true, then a map with sign-positions will be included describing the positions of all @sign invocations

Example of how you may want to use export-post:

The POST-call that will be performed, will have the following structure:

  • document contains the Base64-version of the document (PDF or DOCX)

  • additional-data contains the additional data described above

  • qna-id contains the file-ID of the Q&A

  • sign-positions (only present if :sign-positions? was set to true) will be a JSON dictionary containing keys:

    • page-width and page-height, both measurements in points

    • for each identifier passed to @sign, a sub-dictionary containing keys x, y, width, height (all measurements in points) and page

    For example, when @sign("licensee") and @sign("licensor") were used somewhere in the document, the following example output could follow:

Datafields, defined terms and custom function calls

  • call allows you to call another custom Clojure-function. The first argument should be a simple argument (see below), the rest of the arguments are the actual parameters for the called function.

  • datafield (only available within the environment of a clause) allows you to retrieve a certain datafield. Its argument should be a compound argument.

  • value (with, as its sole argument, a datafield obtained through datafield) allows you to retrieve the current value of a datafield

  • term (only available within the environment of a clause) allows you to refer to a certain defined term. Its argument should be a string with the defined term.

  • simple and compound arguments are always Clojure strings

    • in the environment of a clause:

      • a simple argument should refer to one of the custom functions defined in the same file — for example, (call "xxx", :alpha) would call custom function “xxx” (defined in the same file) with as its single argument the keyword :alpha.

      • a compound argument should consist of the name of the referenced file, a slash (/) or circonflex (^), and the name of the custom function or datafield within the referenced file. For example, (call "alpha/xxx", :alpha) would call the custom function “xxx”, with the single argument keyword :alpha.

    • in the Q&A environment:

      • a simple argument should refer to one of the custom functions defined in the Q&A’s repository pane

      • a compound argument should consist of the name of the file (as defined in the file links of the Q&A’s repository pane), a slash (/) or circonflex (^) and the name of the custom function within that file. For example, "alpha/beta" would refer to the custom function called “beta” within file “alpha”, whereby file “alpha” would be present in the file links repository.

Conversion functions

The following functions are part of namespace convert:

  • Duration->months, Duration->days, Duration->weeks, Duration->quarters and Duration->years take a Duration value, and return the number of months/days/weeks/quarters/years as an integer.

  • FloatValue->int, FloatValue->IntValue and FloatValue->float convert a FloatValue.

  • float->FloatValue, IntValue->FloatValue, Amount->FloatValue convert to a FloatValue. (An Amount can be either a FloatValue or an IntValue).

  • int->CurrencyValue, IntValue->CurrencyValue, CurrencyValue->float, float->CurrencyValue, CurrencyValue->FloatValue, CurrencyValue->int and CurrencyValue->IntValue convert to/from a CurrencyValue.

String functions

The following functions are part of namespace ustr:

  • quoted takes a string and returns that string surrounded by double (straight) quotes.

  • one-else takes nr, one-string and else-string als parameters. Returns one-string if nr is equal to 1, otherwise the else-string.

  • one-else-nr takes nr, one-string and else-string als parameters. Returns nr concatenated by either one-string (if nr is equal to 1), otherwise the else-string.

  • sub takes a string, a start (integer) and an optional end (also integer). Return substring. Ignores indexes out of bounds.

  • upper-case? returns true if the string parameter is entirely in upper-case.

  • initial-cap? returns true if string has an initial cap, but is not fully capitalized (e.g., ‘Dog’ but not ‘DOG’).

  • initial-cap puts an initial cap on s. Note that all letters after the first one are left untouched, so that if those contain uppercase letters, they will remain in the result.

  • capitalize-words capitalize each of the separate words

  • capitalization returns whether string is not capitalized in any way (:none), or has initial caps (:initial), or is all-caps (:all).

  • apply-capitalization takes a capitalization keyword (:none, :initial, :all-words or :all) and applies that capitalization style to the second parameter (string).

  • uncapitalize returns a string with the first letter forced to lower (the rest is untouched).

  • clean-curly-quotes replaces curly quotes and guillemets by straight ones.

  • str->int converts a string to an integer.

  • str->float converts a string to a float.

  • float->str converts a float to a string. The arguments are a float, metric style? (boolean) and the decimals count (positive integer).

  • str->bool converts ‘true’, ‘false’, ‘1’ or ‘0’ strings to boolean. nil if some other input was given.

  • extract-int extracts the first occurrence of some integer from anywhere in the string.

  • int->str takes as arguments an integer and a minimum-numbers-count. Convert the integer to a string, padding the result with zeros up to the minimum-numbers-count. Note that no thousands-grouping is applied.

  • comma-join takes as arguments a combiner string and a collection of strings. Joins the different elements of the collection as strings with each other, separated by comma’s. The last element is joined with the specified combiner argument.

  • group-by-thousands takes either an integer or a string, as well as a thousands-separator character. It returns a string in which the thousand-separators are added.

  • month->string takes the number of the month (integer) and a language keyword, and returns the month in the specified language.

  • weekday->string takes a weekday-number (integer: Monday is 1, Sunday is 7) and a language keyword, and returns the weekday as a string in the specified language.

  • date->str takes year (integer), month (integer), day (integer), a date-style-kw and a language keyword, and returns the formatted date in the specified language. The date-style-kw can be any of the following: :d-m-yyyy :slashed-d-m-yyyy :m-d-yyyy :slashed-m-d-yyyy :dd-mm-yyyy :slashed-dd-mm-yyyy :mm-dd-yyyy :slashed-mm-dd-yyyy :dd-mm-yy :slashed-dd-mm-yy :mm-dd-yy :slashed-mm-dd-yy :yyyy-mm-dd :slashed-yyyy-mm-dd :d-mmmm-yyyy :mmmm-d-yyyy :mmmm-d-comma-yyyy :dd-mmmm-yyyy :wwww-dd-mmmm-yyyy :wwww-d-mmmm-yyyy :wwww-comma-d-mmmm-yyyy :wwww-mmmm-dd-yyyy.

  • ellipsis takes two arguments (string input and a max integer) and truncates string with … after specified number of characters.

  • drop-last-character drops the last character of a string. Returns empty string if nothing is left in the string.

  • drop-surrounding-characters drops the characters surrounding a string. Returns empty string if nothing is left in the string.

  • drop-last-n-chars takes an input string and a number of characters (integer). Returns empty string if nothing is left in the string, or n is simply too high.

  • get-last-n-chars takes an input string and a number n (integer). Returns string with last N characters.

  • trim-if-string returns its sole argument if it is not a string, otherwise returns the trimmed argument.

  • lower-if-string returns its sole argument if it is not a string, otherwise returns the lower-cased argument.

  • trim|lower-if-string returns its sole argument if it is not a string, otherwise returns the lower-cased & trimmed argument.

  • when-s returns the string if it is not empty, else return nil.

Mathematical functions

The following functions are part of namespace math:

  • sqrt returns the square root

  • log returns the logarithm

  • E returns the constant e

  • acos returns the arc cosine of a value

  • asin returns the arc sine of a value

  • atan returns the arc tangent of a value

  • atan2 returns the angle theta from the conversion of rectangular coordinates to polar coordinates

  • cos returns the trigonometric cosine of an angle

  • sin returns the trigonometric sine of an angle

  • tan returns the trigonometric tangent of an angle

  • exp returns Euler’s number e raised to the power of a

  • PI returns the constant pi

  • pow returns the value of the first argument raised to the power of the second argument

  • random returns a random float value between 0 and 1. (You will probably want to invoke the host function using @cljr, otherwise the value will not be updated).

Collection-related additional functions

The following functions are part of namespace coll:

  • i18sv takes an i18-map and a language-keyword, and extracts the string value for the requested language. If the requested language is not available, then the next successive language is tried (hence the name: -sv refers to successive). Only returns a string if it is non-empty.

  • find-first takes a predicate and a collection, and returns the first item that meets the predice. Essentially it is an optimized version of (first (filter ...))

  • intersects? takes two collections and returns true if there is at least one common element between the two collections.

  • keepv is similar to Clojure core’s keep function, but returns a vector.

  • indexed returns a collection that consists of [index, item] of the original coll.

  • index-of returns index of first item that meets the predicate. Nil if not found.

  • index-of-value returns first index of value within the collection. Nil if not found.

  • present? takes a value and a collection, and returns true if value is present in the collection. Otherwise false.

  • singleton?returns true if collection contains exactly one item.

  • dissoc-in (which takes a map and a collection of keys) dissociates an entry from a nested associative structure returning a new nested structure. Note that entire branches may get wiped out. For example, (dissoc-in {:a {:b {:c {:d 5}}}} [:a :b :c :d]) results in {}.

  • dissoc-in-leafis like dissoc-in, but only wipes out the “leaf”, preventing intermediary branches from being removed. For example, (dissoc-in {:a {:b {:c {:d 5}}}} [:a :b :c :d]) results in {:a {:b {:c {}}}}

  • remove-element-v takes a vector and an item. It removes item from vector, and returns updated vector.

  • remove-elements-v takes a vector and one or more items. Removes those items from vector. Returns updated vector.

  • remove-atindex-v takes a collection and a position. Removes the item with the specified index from the vector. Returns updated vector.

  • insert-v takes a vector, a position and an item. It inserts item into vector at position. If the position is invalid, then the item is simply conj’ed. Returns updated vector.

  • cons-v is like Clojure core’s cons, but returns a vector.

  • concat-v is like Clojure core’s concat, but returns a vector.

  • items-before-pred takes a predicate and a collection. Returns the vector of all items positioned before the first item that meets the pred (excluding the pred-meeting item itself). If pred is never met, then the entire coll is returned.

  • items-before-or-at-pred takes a predicate and a collection. Returns the vector of all items positioned before the first item that meets the pred, including the pred-meeting item itself. If pred is never met, then the entire coll is returned.

  • items-after-pred takes a predicate and a collection. Returns the vector of all items positioned after the last item that meets the pred (excluding the pred-meeting item itself). If pred is never met, then the entire coll is returned.

  • items-at-or-after-pred takes a predicate and a collection. Returns the vector of all items positioned after the last item that meets the pred, including the pred-meeting item itself. If pred is never met, then the entire coll is returned.

  • split-around takes a predicate and a collection. Splits collection into a tuple of three vectors, where:

    • the first vector contains all elements up to (but not including) the first element that matches pred;

    • the second item is the matching element itself;

    • the third item is a vector that contains all elements after the matching element (which may contain other matching elements, but this is not checked).

    • If no match exists, then the second item and third item will be nil.

  • split-in-two-vectors takes a predicate and a collection. Splits collection into a tuple of two vectors:

    • the first vector contains all elements up to (but not including) the first element that matches pred;

    • the second vector is the rest of the items.

    • If no match exists, then the second item will be nil.

  • remove-fields takes a predicate and a map. Remove those map fields for which (pred value) meets the pred. If no keys are left, an empty map is returned.

  • de-nil takes a map. Remove keys with nil-values from the map. If no keys are left, nil is returned.

  • de-nil-map takes a map. Remove keys with nil-values from the map. If no keys are left, empty map is returned.

  • nnmerge takes one or more maps. Merges the first map with the de-nil’ed other maps.

  • merge-vectors takes vec1, vec2 and extract-identity-fn. Merge elements of two vectors. If two values conflict, then the latter vector’s element will take precedence. extract-identity-fn should be a function that takes one parameter, and extracts the ‘identity’ of a value, to determine conflicts.

  • merge-missing takes two maps. Merge those fields of map2 that are missing in map1, into map1

  • keep-first takes a function and a collection. Like keep/keepv, but immediately stops upon the first hit, and returns that one. Returns nil when no match.

  • mapcat-v is like Clojure core’s mapcat, but returns a vector.

  • flatten-one flattens the given collection one level deep.

  • update-el takes v, pred and f. Updates the first el of the vector that matches pred (leaves the rest untouched).

  • replace-el takes v, pred and new-el. Replaces the first el of the vector that matches pred, with the new element.

  • replace-or-add takes v, pred and replacement-el. Replaces the first element of the vector that matches pred, by the specified replacement. If pred does not match anywhere, then conj the replacement to the vector.

  • any-field-equal? takes a map, fields (collection of keywords) and ref-value. Returns true if any of the specified fields in map m is equal to ref-value.

  • any-field? takes a map m, fields (collection of keywords) and a predicate. Returns logical true if (pred field) is true for any of the specified fields in map m.

  • drop-until-nth-pred takes n (integer), pred (function) and a collection. Drop items until pred is met for the nth time.If successful, the first item of the returned coll will meet n. Returns nil if pred is not met at least n times.

  • index-of-subcoll takes a large collection and a sub-collection. Returns the position (0-based) of the sub-coll within big-coll. nil if not found.

  • initial-subvec? takes a vector and a subvector. Returns true if subv is a subcollection of v, at index 0. Note that if v and subv are equal, the result will also be true.

  • distinct-by takes a function and a collection. Returns a lazy sequence of the elements of coll, removing any elements that return duplicate values when passed to a function f.

  • remove-v like Clojure core’s remove, but returns a vector.

  • removev-with-id takes an ID and a collection. Removes all elements that have the specified ID.

  • remove-first-v takes a predicate and a collection. Removes first element from the collection that meets the specified pred. Unlike remove-v, it does not continue looking once a match is found. Always returns a vector.

  • sort-by-other-coll takes a collection to sort, a reference collection and an optional key-function. Sorts the items in the given collection on the basis of the ordering of items in reference collection. If an item is not present in the reference collection, then it will be appended to the end of the sorted-result-so-far. If a key-fn is given, it will be used to extract the key from each item, for use in the ordering vis-à-vis the reference collection. Examples:

    • (sort-by-other-coll [:c :b :a :d] [:a :b :c]) => (:a :b :c :d)

    • (sort-by-other-coll [{:key :c} {:key :a}] [:a :b :c] :key) => ({:key :a} {:key :c})

  • conj-set like Clojure core’s conj, but creates a set if the set-to-add-to is nil.

  • conj-vec like Clojure core’s conj, but creates a vector if the vec-to-add-to is nil.

  • find-first-with-id takes an ID and a collection. Returns the first element from the collection whose :id field matches the given id.

  • find-first-equalto takes an object and a collection. Returns the first element from the collection that is equal to the given object.

  • swap-elements takes a vector, and two indices. Swap the vector’s elements with the two specified indices.

  • submap? takes two maps. Returns true if a is a submap of b, i.e.: a only has fields that are also present in b, and those fields are identical to those of b.

  • update-map-values takes a map and a function. loops through the map, and call f with the key & value on each iteration. The value will then be updated with the result of f.

  • update-mapv takes a map, a field-keyword and a function f. Updates the vector in map m’s field-kw, by calling f on each of its elements.

  • update-kv takes a map, a field-keyword and a function f. Updates the map in map m’s field-kw. f should be a function to be used in a reduce-kv, so taking three elements.

AJAX/get

The ajax/get function allows you to fetch information from external servers, using a GET call for HTTP. Note that, due to the asynchronous nature, it is currently only available in the Q&A environment. The parameters are:

  • a URL (string)

  • a map with the following optional keys:

    • :on-success is a function that is called with the result of the call. If it is nil, then ajax-get will simply return the success-result.

    • :on-error is a function that is passed a map (see cljs-ajax documentation on ‘Error Responses’) and should either return :error, or some other value

Other functions

exec-js internally calls JavaScript's eval function and executes the sole string-parameter that gets passed to it.

Building Q&A interfaces using “custom blocks”

You can build custom mini-interfaces by adding a new “custom block” question in the Q&A, and inserting code. You can develop these interfaces interactively by testing the cards (showing the test cards pane in your design Q&A environment and either hitting the refresh button, or shortcut Ctrl-U).

For example, the following would create a DIV (10 pixel padded, with a 2 pixel blue border and 5 pixel external margin) with a single paragraph with red text, and a button that prints “hello” to the console when clicked:

[:div 
 
 {:style {:padding "10px" :border "2px solid blue" :margin "5px"}}
 
 [:p 
  {:style {:color "red"}} 
  "I am a paragraph"]
 
 [:> Button {:text "I'm a button..." 
             :on-click (fn [event] 
                           (println "I'm clicked!"))}]]

Tips & Tricks

  • You can reformat the Clojure-code you have written by pressing the Ctrl-B shortcut.

  • When developing in the Design Q&A environment, you probably want to show the Test cards pane, to interactively check what your Clojure code is doing.

This custom function is useful to modify how the value that is selected by the end-user through the service)-integration, gets “distributed” (pasted onto) the other answers in the Q&A — e.g., for situations where the standard mechanics are not sufficient for your purposes. In such case, you could create a custom-function that directly performs several set-answer to modify the answer.

answers contains the answers given by the end-user for the non-disabled questions. The structure is equivalent to the QnaValue data structure described in .

:options is a map of options. See the .

The interfaces are built using a subset of the library, combined with ClojureScript’s . The following Blueprint-widgets are available: button, callout, checkbox, menu, menuItem, menu-divider, radio-button, slider, switch, tag, table and tooltip. In addition, you can create any DOM-element you like through the standard Hiccup syntax.

When developing in Assemble Document, you probably want to activate the , so you can interactively see the changes to your Clojure-code in the currently focused clause.

Clojure programming language
ClojureScript
ClojureDoc introduction tutorial
Clojure for the Brave and True
Small Clojure Interpreter
receive tags
Swagger
documentation for ajax-get
Blueprint.js
Hiccup syntax
Focus mode