Quick Start

Get a Pascal compiler widget running on your page in under a minute. Copy the HTML below, adjust the src to point at your hosted Pas2JS compiler, and open it in a browser.

<!doctype html>
<html>
<head><title>My Pascal Page</title></head>
<body>
  <h1>Try Pascal</h1>
  <iframe src="index.html?mode=embed"
          style="width:100%; height:500px; border:1px solid #ccc;">
  </iframe>
</body>
</html>

The ?mode=embed parameter activates embed mode, which shows a minimal UI with just the code editor and Compile/Run buttons. The toolbar, sidebar, tabs, and console panel are hidden.

For the iframe src, use the URL where your Pas2JS compiler is hosted.

Creating Assignments

Assignments let you create graded exercises. Students see instructions, write code, and get automatic feedback. There are three ways to deliver an assignment:

  1. URL parameter — host a JSON file and link to it:
    index.html?assignment=https://example.com/hello.json
  2. Hash parameter — base64-encode the JSON directly in the URL:
    index.html#assignment=eyJ0aXRsZSI6Ikhl...
    See the worked example in the API Reference.
  3. postMessage — for embedded widgets, send assignment fields via p2js_configure.

Assignment JSON Example

{
  "title": "Hello World",
  "description": "<p>Write a program that prints <strong>Hello, World!</strong></p>",
  "source": "program Hello;\nuses browserconsole;\nbegin\n  { your code here }\nend.",
  "captureDelay": 100,
  "hints": [
    "Use the WriteLn procedure to print text to the console.",
    "The string must be exactly 'Hello, World!' \u2014 check capitalization and punctuation.",
    {"solution": "program Hello;\nuses browserconsole;\nbegin\n  WriteLn('Hello, World!');\nend."}
  ],
  "validation": [
    {
      "target": "console",
      "type": "contains",
      "value": "Hello, World!",
      "message": "Output must contain 'Hello, World!'"
    }
  ]
}

See the Assignment JSON Schema for a complete field reference.

What the Student Sees

When an assignment is loaded, the student experience changes in several ways:

  • An assignment panel appears above the editor, showing the title and description (which can contain HTML).
  • The starter code from the source field is loaded into the editor automatically.

Hints

A “Need a hint? (N remaining)” button reveals hints one at a time:

  • Text hints appear in yellow notification boxes with guidance on how to proceed.
  • Solution hints appear in green notification boxes and include a Load Solution button that replaces the editor content with a working solution.

Arrow buttons navigate between revealed hints with a “Hint M of N” counter. If the next hint is a solution, the button text changes to “Show Solution”.

Validation

After running the program, output is automatically checked against the assignment’s validation rules:

  • A green “All checks passed!” notification means the solution is correct.
  • A red “N of M checks passed” notification lists which checks failed with their messages.

Results can be dismissed so the student can keep iterating.

The captureDelay field controls how many milliseconds to wait before running validation after program output is captured. This is useful when the program modifies the DOM and you need to wait for rendering.

Writing Tutorials

For multi-exercise tutorial pages, embed multiple compiler widgets and configure each one via the postMessage API.

Flow

  1. Embed one or more iframes with ?mode=embed.
  2. Listen for the p2js_ready event from each widget.
  3. Send p2js_configure to each with its exercise data.
  4. Optionally listen for p2js_compiled, p2js_runComplete, and p2js_validationResult.

Complete Example

<!doctype html>
<html>
<head>
  <title>Pascal Tutorial</title>
</head>
<body>
  <h1>Lesson 1: Hello World</h1>
  <p>Write a program that prints a greeting.</p>
  <iframe id="exercise-1" src="index.html?mode=embed"
          style="width:100%; height:450px; border:1px solid #ccc;"></iframe>

  <h1>Lesson 2: Variables</h1>
  <p>Declare a variable and print its value.</p>
  <iframe id="exercise-2" src="index.html?mode=embed"
          style="width:100%; height:450px; border:1px solid #ccc;"></iframe>

  <script>
  var exercises = [
    {
      widgetId: 'exercise-1',
      config: {
        command: 'p2js_configure',
        title: 'Hello World',
        description: '<p>Print <strong>Hello!</strong></p>',
        source: "program Ex1;\nuses browserconsole;\nbegin\n\nend.",
        validation: [
          {target: 'console', type: 'contains',
           value: 'Hello!', message: 'Must print Hello!'}
        ]
      }
    },
    {
      widgetId: 'exercise-2',
      config: {
        command: 'p2js_configure',
        title: 'Variables',
        description: '<p>Declare an integer, assign 42, print it.</p>',
        source: "program Ex2;\nuses browserconsole;\nvar\n  X: Integer;\nbegin\n\nend.",
        validation: [
          {target: 'console', type: 'contains',
           value: '42', message: 'Must print 42'}
        ]
      }
    }
  ];

  window.addEventListener('message', function(e) {
    if (!e.data || e.data.command !== 'p2js_ready') return;
    exercises.forEach(function(ex) {
      var frame = document.getElementById(ex.widgetId);
      if (frame && frame.contentWindow === e.source) {
        frame.contentWindow.postMessage(ex.config, '*');
      }
    });
  });
  </script>
</body>
</html>

See tutorial-sample.html and test-embed.html for complete working examples.

Custom Examples

The Examples dropdown in the standalone compiler loads from example-manifest.json by default. You can customize this file to provide your own sample programs, or use the ?examples=URL query parameter to load an external manifest:

index.html?examples=https://example.com/my-examples.json
In embed mode, the ?examples= parameter is silently ignored (the Examples dropdown is hidden). This feature is for standalone (non-embedded) deployments only.
External manifest URLs must support CORS. If the server does not send appropriate Access-Control-Allow-Origin headers, the fetch will fail silently and the Examples dropdown will be hidden.

The file is a JSON array of objects. Each object represents one example in the dropdown:

FieldTypeRequiredDescription
titleStringYesDisplay name in the dropdown
descriptionStringNoBrief description shown below the title
sourceStringNoInline Pascal source code
urlStringNoURL to fetch source code from

source and url are mutually exclusive. If source is present and non-empty, it is used directly (no network request). Otherwise url is fetched via HTTP GET. If both are empty, the entry still appears in the dropdown but selecting it does nothing.

Example

[
  {
    "title": "Hello World",
    "description": "Minimal program with console output",
    "source": "program HelloWorld;\nuses browserconsole;\nbegin\n  WriteLn('Hello, World!');\nend."
  },
  {
    "title": "Advanced Example",
    "description": "Loaded from an external file",
    "url": "https://example.com/advanced.pas"
  }
]

See Examples Manifest in the API Reference for the complete field reference.

Custom Snippets

The Snippets sidebar panel and Ctrl+J picker load from snippet-manifest.json by default. You can customize this file to provide your own code templates, or use the ?snippets=URL query parameter to load an external manifest:

index.html?snippets=https://example.com/my-snippets.json

Snippets in Embed Mode

Unlike examples, snippets work in embed mode. When a custom snippets manifest is loaded (via ?snippets=URL or p2js_configure with snippetsUrl), a Snippets button appears in the embed toolbar. Clicking it opens a dropdown listing all available snippets. The Ctrl+J picker also works in embed mode.

<!-- Embed with custom snippets -->
<iframe src="index.html?mode=embed&snippets=https://example.com/snippets.json"
        style="width:100%; height:500px; border:1px solid #ccc;">
</iframe>
External manifest URLs must support CORS. If the server does not send appropriate Access-Control-Allow-Origin headers, the fetch will fail silently and the Snippets button will not appear.
FieldTypeRequiredDescription
nameStringYesTrigger name — matched by Ctrl+J prefix (case-insensitive)
categoryStringYesGroup name for categorized view
descriptionStringYesHelp text shown in the snippet list
sourceStringYesPascal code to insert; | marks cursor position

Cursor Marker

Place a | character in the source where you want the cursor to land after insertion. The | is removed from the inserted code. If no | is present, the cursor stays at the end.

Ctrl+J Matching

When the user types a prefix in the editor and presses Ctrl+J (Cmd+J on Mac), snippets whose name starts with that prefix (case-insensitive) are matched:

  • One match — inserts directly.
  • Multiple matches — shows a picker popup.
  • No matches — nothing happens.

Example

[
  {
    "name": "for",
    "category": "Control Flow",
    "description": "For loop with counter",
    "source": "for I := 0 to 9 do\nbegin\n  |\nend;"
  },
  {
    "name": "writeln",
    "category": "Console I/O",
    "description": "WriteLn to console",
    "source": "WriteLn('|');"
  }
]

See Snippet Manifest in the API Reference for the complete field reference.

URL Parameters

These parameters control the compiler’s behavior when loaded via URL.

ParameterLocationValueDescription
mode Query string embed Enables embed mode — minimal UI with editor and Compile/Run buttons only
assignment Query string URL Loads assignment JSON from the given URL
examples Query string URL Loads example manifest from the given URL instead of the default. Silently ignored in embed mode.
snippets Query string URL Loads snippet manifest from the given URL instead of the default. Works in both normal and embed mode.
storage Query string indexeddb Forces IndexedDB storage instead of localStorage
#assignment= Hash Base64 string Base64-encoded assignment JSON; used only if the assignment query parameter is absent
#code= Hash URI-encoded string Pre-fills the editor with shared code (generated by the Share button, encoded via encodeURIComponent)
The #assignment= and #code= hash parameters are mutually exclusive — the URL hash can only start with one prefix. If you need both an assignment and pre-filled code, use the ?assignment=URL query parameter for the assignment and send the source via p2js_configure after the widget is ready.

Worked Example: Base64 Assignment URL

Start with a minimal assignment JSON:

{
  "title": "Quick Test",
  "source": "program T;\nbegin\nend."
}

Encode it in JavaScript:

var json = JSON.stringify({
  title: 'Quick Test',
  source: 'program T;\nbegin\nend.'
});
var url = 'index.html#assignment=' + btoa(json);
// Result: index.html#assignment=eyJ0aXRsZSI6Ik...

For larger assignments, prefer the ?assignment=URL approach with a hosted JSON file.

In JSON strings, use \n for newlines (not \\n, which is a literal backslash followed by n).

postMessage Commands

Send commands from your parent page to the embedded compiler widget. All commands are sent via iframe.contentWindow.postMessage(payload, '*').

Important: Always wait for the p2js_ready event before sending any commands. Messages sent before the widget is ready are silently ignored.
CommandRequired FieldsDescription
p2js_configure (see below) Configure JIT settings, load an assignment, and/or set a custom snippets URL
p2js_setSource source: String Replace editor content
p2js_reset (none) Reset editor to last loaded source
p2js_setTheme theme: 'light' | 'dark' Switch editor theme
p2js_runValidation (none) Trigger validation of captured program output

p2js_configure — JIT Configuration Only

widget.contentWindow.postMessage({
  command: 'p2js_configure',
  jitCompile: true,
  jitDebounce: 5
}, '*');

jitCompile enables background syntax checking as the student types. jitDebounce is the delay in seconds after the last keystroke (minimum 3). If omitted, it defaults to 3. Values below 3 are silently ignored.

p2js_configure — Assignment Loading Only

widget.contentWindow.postMessage({
  command: 'p2js_configure',
  title: 'Hello World',
  description: '<p>Print a greeting.</p>',
  source: "program Hello;\nuses browserconsole;\nbegin\n\nend.",
  hints: ['Use WriteLn()'],
  validation: [{
    target: 'console', type: 'contains',
    value: 'Hello', message: 'Must say Hello'
  }],
  captureDelay: 100
}, '*');

p2js_configure — Combined

widget.contentWindow.postMessage({
  command: 'p2js_configure',
  jitCompile: true,
  jitDebounce: 3,
  title: 'Exercise 1',
  description: '<p>...</p>',
  source: '...'
}, '*');

p2js_configure — Custom Snippets

widget.contentWindow.postMessage({
  command: 'p2js_configure',
  snippetsUrl: 'https://example.com/my-snippets.json'
}, '*');

snippetsUrl dynamically loads a snippet manifest from the given URL. The Snippets button in the embed toolbar is disabled during the fetch and re-enabled on success. If the URL is invalid or the server does not support CORS, the button remains disabled. Sending a new snippetsUrl replaces any previously loaded snippets.

Other Commands

// Set source
widget.contentWindow.postMessage({
  command: 'p2js_setSource',
  source: "program Test;\nbegin\nend."
}, '*');

// Reset
widget.contentWindow.postMessage({command: 'p2js_reset'}, '*');

// Set theme
widget.contentWindow.postMessage({
  command: 'p2js_setTheme', theme: 'dark'
}, '*');

// Run validation
widget.contentWindow.postMessage({
  command: 'p2js_runValidation'
}, '*');

Security & Notes

In embed mode, the widget only accepts messages whose source matches window.parent (i.e. the embedding page). Messages from other origins are ignored. Outbound events (p2js_ready, p2js_compiled, etc.) are only sent when the widget is running in embed mode.

p2js_programOutput is an internal command used by the run iframe to deliver captured program output back to the widget for validation. It is not intended for direct use by the embedding page.

postMessage Events

The widget sends events to the parent page via window.parent.postMessage(). Listen for them with window.addEventListener('message', ...).

EventFieldsFired When
p2js_ready version: Integer Widget finished loading — safe to send commands
p2js_compiled success: Boolean Compilation completed (no error details included)
p2js_runComplete consoleOutput: Array, html: String Program execution finished
p2js_validationResult results: Array Validation rules have been checked

Field Details

consoleOutput — array of objects, each with:

  • stream: 'log', 'warn', or 'error' (corresponding to console.log, console.warn, console.error)
  • text: the output line text

html — string containing the document.body.innerHTML snapshot from the run iframe. This is the HTML that validation rules with target: "html" are checked against.

results — array of objects, each with:

  • Passed: Boolean — whether the check passed
  • Rule: object with Message, Pattern, Target, and RuleType fields

Complete Listener Example

window.addEventListener('message', function(e) {
  if (!e.data || !e.data.command) return;

  switch (e.data.command) {
    case 'p2js_ready':
      console.log('Widget ready, protocol v' + e.data.version);
      // Now safe to send commands
      break;
    case 'p2js_compiled':
      console.log('Compiled: ' + (e.data.success ? 'OK' : 'FAILED'));
      break;
    case 'p2js_runComplete':
      e.data.consoleOutput.forEach(function(entry) {
        console.log('[' + entry.stream + '] ' + entry.text);
      });
      if (e.data.html) {
        console.log('HTML length: ' + e.data.html.length);
      }
      break;
    case 'p2js_validationResult':
      e.data.results.forEach(function(r) {
        console.log((r.Passed ? 'PASS' : 'FAIL') + ': ' + r.Rule.Message);
      });
      break;
  }
});

Assignment JSON Schema

Complete reference for the assignment JSON format used with ?assignment=URL, #assignment=base64, or the p2js_configure postMessage command.

Top-Level Fields

FieldTypeRequiredDescription
titleStringNoAssignment title displayed above the editor
descriptionStringNoInstructions shown to the student (HTML allowed)
sourceStringNoStarter code loaded into the editor
captureDelayIntegerNoMilliseconds to wait before running validation (default: 0)
hintsArrayNoArray of hints — strings or solution objects
validationArrayNoArray of validation rules

Hint Entries

  • String: "Use WriteLn to print" — displayed as a yellow notification.
  • Solution object: {"solution": "program..."} — displayed as a green notification with a “Load Solution” button.

Validation Rules

FieldTypeRequiredDescription
targetStringNo"console" (default) or "html"
typeStringNo"contains" (default) or "match"
valueStringNoSubstring to search for (used when type is "contains")
patternStringNoRegular expression pattern (used when type is "match")
messageStringNoFeedback text shown in results

Console text is assembled from all output entries. Non-breaking spaces are normalized to regular spaces before matching.

Examples Manifest

The example-manifest.json file defines the sample programs shown in the Examples dropdown.

FieldTypeRequiredDescription
titleStringYesDisplay name
descriptionStringNoShown below title in dropdown
sourceStringNoInline Pascal code
urlStringNoURL to fetch code from

source takes priority over url. If both are empty, the entry still appears in the dropdown but selecting it does nothing.

Snippet Manifest

The snippet-manifest.json file defines code templates for the Snippets sidebar and Ctrl+J picker.

FieldTypeRequiredDescription
nameStringYesTrigger name for Ctrl+J matching
categoryStringYesGroup name
descriptionStringYesHelp text
sourceStringYesCode to insert; | marks cursor position

Matching is case-insensitive prefix-based. Snippets are displayed grouped by category (default) or flat alphabetically.