Actions

ExpressionScript for developers

From LimeSurvey Manual

Revision as of 17:03, 21 September 2011 by TMSWhite (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

This wiki page is meant for the LimeSurvey development team and others wishing to contribute to LimeSurvey.  It provides details about how to work with, test, and extend Expression Manager (EM).

Getting Started

The best way to get started with EM is to:

EM Source Code Organization and  Purpose

EM includes the following source fies:

  1. /application/helpers/admin/expressions/em_core_helper.php
    • ExpressionManager class - which implements a recursive descent parser in PHP and JavaScript which let you create variables and securely expose a specified set of pre-defined functions.
    • Defines the set of available functions within EM ($this->amValidFunctions)
    • exprmgr_*() functions - custom PHP functions exposed to the user via EM.
    • Has built-in test cases:
      • UnitTestStringSplitter() - validates that properly extracts expressions (surrounded by curly braces) from longer strings
      • UnitTestTokenizer() - validates that strings are properly tokenized assigned the right token classification
      • ShowAllowableFunctions() - shows a table of the 70+ avaiable functions and their syntax
      • UnitTestEvaluator() - this should test all EM syntax, operators, and functions in PHP and JavaScript and confirm they generate accurate and identical results.  Currently, it contains 200+ test cases, but does not yet test every function.
  1. /application/helpers/admin/expressions/em_manager_helper.php
    • LimeExpressionManager class - this implements LimeSurvey-specific functions for accessing ExpressionManager. Initially, the goal was to keep the ExpressionManager completely separate  (e.g. de-coupled)from LimeSurvey so that developers would never need to modify the ExpressionManager class itself.  However, there is now some tight coupling between LimeExpressionManager and ExpressionManager
    • It is implemented as a singleton, so all access is via static functions.  Theoretically, this could be loaded as a library within CodeIgniter, but it isn't clear that that provides additional value, and would require significant re-write (since ExpressionManager was initially designed for LimeSurvey 1.91+)
    • Has built-in test cases:
      • UnitTestProcessStringContainingExpressions() - validates that accurately evaluates multiple expressions within a string
      • UnitTestRelevance() - implements a 16+ question survey with cascading relevance, making questions and tailoring appaer and disappear based upon answers provided.
      • Lacks a full integration test case - for that, one needs to load and test the ExpressionManager-Demo survey.
  1. /application/views/admin/expressions/test.php
    • Provides access to each of the Test cases, whose views are in /application/views/admin/expressions/test/*.php
      • Available Functions - shows the 70+ functions people can use via EM and their allowable syntax
      • Tokenizer - calls UnitTestTokenizer()
      • Unit Tests- calls UnitTestEvaluator() to test core EM functionality.  Cells in Green are correct.  Cells in Red are errors (except for dynamic functions, like rand() and date().
      • String Splitter - calls UnitTestStringSplitter()
      • Integration Tests - calls UnitTestProcessStringContainingExpressions()
      • Unit Test Dynamic Relevance Processing - calls UnitTestRelevance()
      • Running Log - Source Data - shows a color-coded dump of the current instrument definition (e.g. $fieldmap[])
      • Running Log - Transactions on this Page - shows all of the EM-related translation requests on the current survey page (pretty-printed), and their results
  1. /scripts/admin/expressions/em_javascript.js
    • LEMval() - provides access to internal LimeSurvey variable values and attributes.  Allowable attributes are:
      • .shown - the answer as displayed to the user
      • .qid - the question ID
      • .mandatory - whether the question is mandatory
      • .question - the text of the question
      • .relevance - the relevance equation for the question
      • .relevanceStatus - whether or not the question is currently relevant
      • .type - the question type (the one character code)
      • .code (or no suffix) - the internal code value for the answer
      • .NAOK - the internal code value for the answer
    • Fix for Tab-based navigation
      • Purpose:  browsers do now properly handle tabs if form elements appear or disappear.  Without this fix, if a change to one question makes another question appear immediately after it, the built-in tab functionality will tab past those new questions to whatever question happened to be next before those new questions were inserted.  Users should expect that if new questions appear, that the browser will tab directly to them and not skip over them.
      • LEMsetTabIndexes()
        • sets tabindex for all potentially visible form elements
        • binds a keydown listener to each of them for managing TAB and SHIFT-TAB
          • calls ExprMgr_process_relevance_and_tailoring() to update question visibility and tailoring)
          • calls LEMmoveNextTabIndex() to moves to next relevant form element.
          • cancels the default processing of TAB or SHIFT-TAB
      • LEMmoveNextTabIndex()
        • For TAB, uses complex JQuery to get the tabindexes for the set of relevant and active form elements following the current element:
          • Find all questions that have tabindexes greater than the current tabindex (will include current question if there are other visible elements available within the question)
          • Fiters that set to only include relevant questions (the question's displaySGQA node has value="on")
          • Finds all relevant tabindexes within that set
          • Adds enabled button and submit buttons to that set
        • Iterates through that set to find the first relevant tabindex after the current tabindex
        • Cycles through the navigation buttons, and loops back to the top of the page if needed (skipping the browser search field)
        • For SHIFT-TAB, does the equivalent, but to support movement backwards.
    • JavaScript implementations of exposed EM functions, built to exactly mirror the server-side (PHP) functionality
      • LEM*() - functions built by LimeSurvey team - these are typically functions that are not natively supported within PHP
      • Javascript equivalent of existing PHP functions (so uses php names.  These are from phpjs.org.