From 28023176cc62f9132ccadbb0898fc815ec01fbbf Mon Sep 17 00:00:00 2001 From: Daniel Fabian Date: Thu, 31 May 2018 12:46:28 +0200 Subject: [PATCH] Fix build process and merge internal updates. --- client_side_only_impl/vsaq_main.js | 8 +-- compiler.flags | 4 +- do.sh | 32 +++++---- download-libs.sh | 65 ++++++++++++++----- vsaq/static/qpage_base.js | 15 ----- vsaq/static/questionnaire/blockitems.js | 2 +- vsaq/static/questionnaire/boxitem.js | 10 +-- vsaq/static/questionnaire/boxitem_test.js | 18 +++++ vsaq/static/questionnaire/checkitem.js | 13 ++-- vsaq/static/questionnaire/groupitem.js | 35 ++++++---- vsaq/static/questionnaire/groupitem_test.js | 43 ++++++++++++ vsaq/static/questionnaire/items.js | 18 +++-- vsaq/static/questionnaire/lineitem.js | 10 +-- vsaq/static/questionnaire/lineitem_test.js | 26 ++++++++ vsaq/static/questionnaire/questionnaire.js | 34 +++++----- .../questionnaire/questionnaire_editor.js | 14 ++-- .../questionnaire/questionnaire_test.js | 12 ++-- vsaq/static/questionnaire/radioitem.js | 12 ++-- vsaq/static/questionnaire/templates.soy | 18 ++--- vsaq/static/questionnaire/tipitem.js | 15 +++-- vsaq/static/questionnaire/tipitem_test.js | 22 +++++++ vsaq/static/questionnaire/uploaditem.js | 11 ++-- vsaq/static/questionnaire/uploaditem_test.js | 14 ++++ vsaq/static/questionnaire/yesnoitem.js | 13 ++-- vsaq/static/questionnaire/yesnoitem_test.js | 21 ++++++ vsaq/static/utils.js | 3 +- 26 files changed, 347 insertions(+), 141 deletions(-) diff --git a/client_side_only_impl/vsaq_main.js b/client_side_only_impl/vsaq_main.js index 42456fa..c7afbf5 100644 --- a/client_side_only_impl/vsaq_main.js +++ b/client_side_only_impl/vsaq_main.js @@ -159,7 +159,7 @@ vsaq.Qpage.prototype.updateStorage_ = function(data) { var newStorageData = null; var storageData = this.readStorage_(); if (storageData) { - storageData = JSON.parse(storageData); + storageData = /** @type {Object|null} */ (JSON.parse(storageData)); goog.object.extend(storageData, data); newStorageData = goog.json.serialize(storageData); } else { @@ -259,7 +259,7 @@ vsaq.Qpage.prototype.loadExtensionThenQuestionnaire = function( text = vsaq.utils.vsaqonToJson(text); var extension = {}; try { - extension = JSON.parse(text); + extension = /** @type {Object|null} */ (JSON.parse(text)); } catch (err) { alert('Loading the extension failed. It does not appear to be ' + 'valid json'); @@ -297,7 +297,7 @@ vsaq.Qpage.prototype.loadQuestionnaire = function(opt_path, opt_extension) { text = vsaq.utils.vsaqonToJson(text); var template = {}; try { - template = JSON.parse(text); + template = /** @type {Object|null} */ (JSON.parse(text)); } catch (err) { alert('Loading the template failed. It does not appear to be ' + 'valid json'); @@ -328,7 +328,7 @@ vsaq.Qpage.prototype.loadQuestionnaire = function(opt_path, opt_extension) { this.questionnaire.listen( goog.events.EventType.CHANGE, goog.bind(function(e) { - goog.structs.forEach(e.changedValues, function(val, key) { + goog.object.forEach(e.changedValues, function(val, key) { this.changes[key] = val; }, this); if (goog.structs.getCount(this.changes) > 0) { diff --git a/compiler.flags b/compiler.flags index c0e0a05..1b5e751 100644 --- a/compiler.flags +++ b/compiler.flags @@ -7,7 +7,7 @@ --jscomp_error=checkVars --jscomp_error=const --jscomp_error=constantProperty ---jscomp_error=deprecated +--jscomp_warning=deprecated --jscomp_error=duplicate --jscomp_error=duplicateMessage --jscomp_error=es5Strict @@ -24,8 +24,6 @@ --jscomp_error=unknownDefines --jscomp_error=uselessCode --jscomp_error=visibility ---externs=third_party/closure-compiler/contrib/externs/chrome_extensions.js --only_closure_dependencies --manage_closure_dependencies ---js third_party/closure-library/closure/goog/deps.js --js build/deps.js diff --git a/do.sh b/do.sh index 613442a..9ba20f9 100755 --- a/do.sh +++ b/do.sh @@ -17,7 +17,7 @@ # PYTHON_CMD="python" -JSCOMPILE_CMD="java -jar third_party/closure-compiler/build/compiler.jar --flagfile=compiler.flags" +JSCOMPILE_CMD="java -jar third_party/closure-compiler/target/closure-compiler-1.0-SNAPSHOT.jar --flagfile=compiler.flags" CKSUM_CMD="cksum" # chosen because it's available on most Linux/OS X installations BUILD_DIR="build" BUILD_TPL_DIR="$BUILD_DIR/templates" @@ -35,10 +35,9 @@ vsaq_assert_dependencies() { fi # Check if required files are present. files=(third_party/closure-library \ - third_party/closure-templates-compiler \ - third_party/closure-stylesheets/build/closure-stylesheets.jar \ - third_party/closure-compiler/build/compiler.jar \ - third_party/closure-compiler/contrib/externs/chrome_extensions.js \ + third_party/closure-templates/target \ + third_party/closure-stylesheets/target/closure-stylesheets-1.5.0-SNAPSHOT-jar-with-dependencies.jar \ + third_party/closure-compiler/target/closure-compiler-1.0-SNAPSHOT.jar \ ) for var in "${files[@]}" do @@ -62,13 +61,16 @@ vsaq_build_templates() { set -e mkdir -p "$BUILD_TPL_DIR" rm -rf "$BUILD_TPL_DIR/*" + mkdir "$BUILD_TPL_DIR/proto" + # Compile safe html type proto to JS + third_party/protoc/bin/protoc --js_out $BUILD_TPL_DIR/proto \ + ./third_party/safe-html-types/proto/src/main/protobuf/webutil/html/types/html.proto # Compile soy templates echo "Compiling Soy templates..." rm -f "$BUILD_TPL_DIR/cksum" vsaq_get_file_cksum '*.soy' > "$BUILD_TPL_DIR/cksum" - find "vsaq" -name '*.soy' -exec java -jar third_party/closure-templates-compiler/SoyToJsSrcCompiler.jar \ - --shouldProvideRequireSoyNamespaces --shouldGenerateJsdoc --shouldDeclareTopLevelNamespaces --srcs {} \ - --outputPathFormat "$BUILD_TPL_DIR/{INPUT_DIRECTORY}{INPUT_FILE_NAME}.js" \; + find "vsaq" -name '*.soy' -exec java -jar third_party/closure-templates/target/soy-2018-03-14-SoyToJsSrcCompiler.jar \ + --srcs {} --outputPathFormat "$BUILD_TPL_DIR/{INPUT_DIRECTORY}{INPUT_FILE_NAME}.js" \; echo "Done." } @@ -110,9 +112,10 @@ vsaq_build_closure_lib_() { SRC_DIRS=( \ vsaq \ client_side_only_impl \ + third_party/closure-templates/target \ third_party/closure-library/closure/goog \ third_party/closure-library/third_party/closure/goog \ - third_party/closure-templates-compiler ) + third_party/protoc/protobuf-3.5.1/js/binary ) if [ -d "$3" ]; then SRC_DIRS+=("$3") fi @@ -122,13 +125,16 @@ vsaq_build_closure_lib_() { jscompile_vsaq+=" --js='$var/**.js' --js='!$var/**_test.js' --js='!$var/**_perf.js'" done jscompile_vsaq+=" --js='!third_party/closure-library/closure/goog/demos/**.js'" + jscompile_vsaq+=" --js='!third_party/closure-templates/javascript/examples/**.js'" if [ "$4" == "debug" ]; then jscompile_vsaq+=" --debug --formatting=PRETTY_PRINT -O WHITESPACE_ONLY" elif [ "$4" == "optimized" ]; then jscompile_vsaq+=" -O ADVANCED" fi + cmd="$jscompile_vsaq --closure_entry_point "$ENTRY_POINT" --js_output_file "$FNAME"" + echo $cmd echo -n "." - $jscompile_vsaq --closure_entry_point "$ENTRY_POINT" --js_output_file "$FNAME" + $cmd } vsaq_build_jsmodule() { @@ -161,7 +167,7 @@ vsaq_build() { BUILD_DIR_STATIC="$BUILD_DIR/static" mkdir -p "$BUILD_DIR_STATIC" - csscompile_vsaq="java -jar third_party/closure-stylesheets/build/closure-stylesheets.jar --allowed-non-standard-function color-stop" + csscompile_vsaq="java -jar third_party/closure-stylesheets/target/closure-stylesheets-1.5.0-SNAPSHOT-jar-with-dependencies.jar --allowed-non-standard-function color-stop" echo "Compiling CSS files..." $csscompile_vsaq "vsaq/static/vsaq_base.css" "vsaq/static/vsaq.css" > "$BUILD_DIR_STATIC/vsaq.css" echo "Copying remaining static files..." @@ -203,7 +209,7 @@ vsaq_generate_jsdeps() { $PYTHON_CMD third_party/closure-library/closure/bin/build/depswriter.py \ --root_with_prefix="build/templates/ build/templates/" \ --root_with_prefix="vsaq/ vsaq/" \ - --root_with_prefix="third_party/closure-templates-compiler/ third_party/closure-templates-compiler/" \ + --root_with_prefix="third_party/closure-templates/javascript third_party/closure-templates/javascript/" \ > "$BUILD_DIR/deps.js" } @@ -215,7 +221,7 @@ vsaq_run() { $PYTHON_CMD third_party/closure-library/closure/bin/build/depswriter.py \ --root_with_prefix="build/templates/ ../../../build/templates/" \ --root_with_prefix="vsaq/ ../vsaq/" \ - --root_with_prefix="third_party/closure-templates-compiler/ ../../../../third_party/closure-templates-compiler/" \ + --root_with_prefix="third_party/closure-templates/javascript/ ../../../../third_party/closure-templates/javascript/" \ > "$BUILD_DIR/deps-runfiles.js" rm -f "$BUILD_DIR/all_tests.js" diff --git a/download-libs.sh b/download-libs.sh index 9394c19..97c6aaf 100755 --- a/download-libs.sh +++ b/download-libs.sh @@ -19,10 +19,24 @@ export JAVA_TOOL_OPTIONS=-Dfile.encoding=UTF8 THIRD_PARTY_DIRECTORY="third_party" + + +type unzip >/dev/null 2>&1 || { + echo >&2 "Unzip is required to build VSAQ dependencies." + exit 1 +} +type wget >/dev/null 2>&1 || { + echo >&2 "Wget is required to build VSAQ dependencies." + exit 1 +} type ant >/dev/null 2>&1 || { echo >&2 "Ant is required to build VSAQ dependencies." exit 1 } +type mvn >/dev/null 2>&1 || { + echo >&2 "Apache Maven is required to build VSAQ dependencies." + exit 1 +} type javac >/dev/null 2>&1 || { echo >&2 "Java compiler is required to build VSAQ dependencies." exit 1 @@ -52,6 +66,7 @@ if [ ! -d .git ]; then rm -rf $THIRD_PARTY_DIRECTORY/closure-library rm -rf $THIRD_PARTY_DIRECTORY/closure-stylesheets rm -rf $THIRD_PARTY_DIRECTORY/js-dossier + rm -rf $THIRD_PARTY_DIRECTORY/closure-templates fi if [ ! -d $THIRD_PARTY_DIRECTORY ]; then @@ -62,41 +77,50 @@ cd $THIRD_PARTY_DIRECTORY git submodule add -f https://github.com/google/closure-compiler closure-compiler git submodule add -f https://github.com/google/closure-library closure-library git submodule add -f https://github.com/google/closure-stylesheets closure-stylesheets +git submodule add -f https://github.com/google/closure-templates closure-templates git submodule add -f https://github.com/jleyba/js-dossier js-dossier +git submodule add -f https://github.com/google/safe-html-types safe-html-types git submodule init git submodule update # Pin submodules to particular commits cd closure-compiler -git checkout -b 59b42c9fc8fc752b3ff3aabe04ad89a96f9a7bf7 59b42c9fc8fc752b3ff3aabe04ad89a96f9a7bf7 +git checkout -b 0441c526dc7ed322034d4f708062c00802184e8f 0441c526dc7ed322034d4f708062c00802184e8f cd .. cd closure-library -git checkout -b dc369cde87d7ef6dfb46d3b873f872ebee7d07cd dc369cde87d7ef6dfb46d3b873f872ebee7d07cd +git checkout -b 26de3253e443d36f64c2ea380faee879dfcf1c54 26de3253e443d36f64c2ea380faee879dfcf1c54 cd .. cd js-dossier -git checkout -b 6f2d09ee26925b7417f9f6bd1547dffe700ab60f 6f2d09ee26925b7417f9f6bd1547dffe700ab60f +git checkout -b e6e55806ea97a4fcf4157661ee809eb8b48fe848 e6e55806ea97a4fcf4157661ee809eb8b48fe848 +cd .. +cd closure-templates +git checkout -b 17dad0f13db94ca43a2e4c436658682a0403ced1 17dad0f13db94ca43a2e4c436658682a0403ced1 +cd .. +cd safe-html-types +git checkout -b 8507735457ea41a37dfa027fb176d49d5783c4ba 8507735457ea41a37dfa027fb176d49d5783c4ba cd .. # build closure compiler if [ ! -f closure-compiler/build/compiler.jar ] && [ -d closure-compiler ]; then cd closure-compiler - ant clean - ant jar +# ant clean +# ant jar + mvn -DskipTests -pl externs/pom.xml,pom-main.xml,pom-main-shaded.xml cd .. fi -# checkout closure templates compiler -if [ ! -d closure-templates-compiler ]; then - curl https://dl.google.com/closure-templates/closure-templates-for-javascript-latest.zip -O - unzip closure-templates-for-javascript-latest.zip -d closure-templates-compiler - rm closure-templates-for-javascript-latest.zip +# build closure templates compiler +if [ -d closure-templates ] && [ ! -d closure-templates/target ]; then + cd closure-templates + mvn -DskipTests package + cd .. fi # build css compiler -if [ ! -f closure-stylesheets/build/closure-stylesheets.jar ]; then +if [ ! -f closure-stylesheets/target/closure-stylesheets-1.5.0-SNAPSHOT-jar-with-dependencies.jar ]; then cd closure-stylesheets - ant + mvn compile assembly:single cd .. fi @@ -104,12 +128,21 @@ if [ -f chrome_extensions.js ]; then rm -f chrome_extensions.js fi +mkdir protoc; cd protoc +wget https://github.com/google/protobuf/releases/download/v3.5.1/protoc-3.5.1-linux-x86_64.zip +unzip protoc-3.5.1-linux-x86_64.zip +rm protoc-3.5.1-linux-x86_64.zip +wget https://github.com/google/protobuf/releases/download/v3.5.1/protobuf-js-3.5.1.zip +unzip protobuf-js-3.5.1.zip +rm protobuf-js-3.5.1.zip +cd .. + # Temporary fix # Soy file bundled with the compiler does not compile with strict settings: # lib/closure-templates-compiler/soyutils_usegoog.js:1762: ERROR - element JS_STR_CHARS does not exist on this enum -cd closure-templates-compiler -echo $PWD -curl https://raw.githubusercontent.com/google/closure-templates/0cbc8543c34d3f7727dd83a2d1938672f16d5c20/javascript/soyutils_usegoog.js -O -cd .. +#cd closure-templates/javascript +#echo $PWD +#curl https://raw.githubusercontent.com/google/closure-templates/0cbc8543c34d3f7727dd83a2d1938672f16d5c20/javascript/soyutils_usegoog.js -O +#cd ../.. cd .. diff --git a/vsaq/static/qpage_base.js b/vsaq/static/qpage_base.js index 6ea551f..9b8bdf6 100644 --- a/vsaq/static/qpage_base.js +++ b/vsaq/static/qpage_base.js @@ -28,7 +28,6 @@ goog.require('goog.events.EventType'); goog.require('goog.structs'); goog.require('goog.ui.Tooltip'); goog.require('vsaq.Questionnaire'); -goog.require('vsaq.utils'); @@ -52,10 +51,6 @@ vsaq.QpageBase = function() { goog.dom.createDom(goog.dom.TagName.SPAN); this.questionnaire.setReadOnlyMode(this.isReadOnly); - vsaq.utils.initClickables({ - 'eh-edit': goog.bind(this.makeEditable, this) - }); - goog.events.listen(window, [goog.events.EventType.BEFOREUNLOAD], function() { if (vsaq.qpageObject_ && vsaq.qpageObject_.unsavedChanges()) @@ -122,16 +117,6 @@ vsaq.QpageBase.prototype.isReadOnly; vsaq.QpageBase.prototype.statusIndicator; -/** - * Make questionnaire editable. - */ -vsaq.QpageBase.prototype.makeEditable = function() { - this.isReadOnly = false; - this.questionnaire.setReadOnlyMode(this.isReadOnly); - this.questionnaire.render(); -}; - - /** * Attempts to keep track of updates that were done to the current * questionnaire. diff --git a/vsaq/static/questionnaire/blockitems.js b/vsaq/static/questionnaire/blockitems.js index 092e773..5e6a94f 100644 --- a/vsaq/static/questionnaire/blockitems.js +++ b/vsaq/static/questionnaire/blockitems.js @@ -43,7 +43,7 @@ goog.require('vsaq.questionnaire.templates'); * for the item to be visible to the user. * @param {?string} caption The caption of the block. * @param {?string=} opt_auth The needed authorization to get an item displayed. - * The auth param on {@code vsaq.questionnaire.items.BlockItem} only + * The auth param on `vsaq.questionnaire.items.BlockItem` only * prevents that items are displayed to the user (hidden by display=none). * @param {?string=} opt_className Name of a CSS class to add to the block. * @extends {vsaq.questionnaire.items.ContainerItem} diff --git a/vsaq/static/questionnaire/boxitem.js b/vsaq/static/questionnaire/boxitem.js index 4753133..506fb0f 100644 --- a/vsaq/static/questionnaire/boxitem.js +++ b/vsaq/static/questionnaire/boxitem.js @@ -49,14 +49,15 @@ goog.require('vsaq.questionnaire.utils'); * @param {number=} opt_maxlength HTML maxlength attribute value for the input * field. See {@link * https://html.spec.whatwg.org/multipage/forms.html#attr-fe-maxlength} + * @param {string=} opt_auth If "readonly", this ValueItem cannot be modified. * @extends {vsaq.questionnaire.items.ValueItem} * @constructor */ vsaq.questionnaire.items.BoxItem = function(id, conditions, caption, opt_placeholder, opt_inputPattern, opt_inputTitle, opt_isRequired, - opt_maxlength) { + opt_maxlength, opt_auth) { goog.base(this, id, conditions, caption, opt_placeholder, opt_inputPattern, - opt_inputTitle, opt_isRequired, opt_maxlength); + opt_inputTitle, opt_isRequired, opt_maxlength, opt_auth); /** * The text area where the user can provide an answer. @@ -122,13 +123,14 @@ vsaq.questionnaire.items.BoxItem.parse = function(questionStack) { return new vsaq.questionnaire.items.BoxItem(item.id, item.cond, item.text, item.placeholder, item.inputPattern, item.inputTitle, item.required, - item.maxlength); + item.maxlength, item.auth); }; /** @inheritDoc */ vsaq.questionnaire.items.BoxItem.prototype.setReadOnly = function(readOnly) { - this.textArea_.readOnly = readOnly; + // if item marked readonly, always keep it readonly + this.textArea_.readOnly = this.auth == 'readonly' ? true : readOnly; }; diff --git a/vsaq/static/questionnaire/boxitem_test.js b/vsaq/static/questionnaire/boxitem_test.js index f7129fc..02352ee 100644 --- a/vsaq/static/questionnaire/boxitem_test.js +++ b/vsaq/static/questionnaire/boxitem_test.js @@ -88,4 +88,22 @@ function testBoxItemParse() { assertEquals('placeholder', box.placeholder); assertTrue(box.required); assertEquals(0, testStack.length); + assertTrue(box.auth != 'readonly'); + + testStack = [{ + 'type': 'box', + 'text': CAPTION, + 'id': ID, + 'required' : true, + 'placeholder': 'placeholder', + 'auth': 'readonly' + }]; + box = vsaq.questionnaire.items.BoxItem.parse(testStack); + assert(box instanceof vsaq.questionnaire.items.BoxItem); + assertEquals(ID, box.id); + assertEquals(CAPTION, box.text); + assertEquals('placeholder', box.placeholder); + assertTrue(box.required); + assertEquals(0, testStack.length); + assertEquals('readonly', box.auth); } diff --git a/vsaq/static/questionnaire/checkitem.js b/vsaq/static/questionnaire/checkitem.js index 19ace81..0b4a9b1 100644 --- a/vsaq/static/questionnaire/checkitem.js +++ b/vsaq/static/questionnaire/checkitem.js @@ -37,12 +37,13 @@ goog.require('vsaq.questionnaire.utils'); * @param {?string} conditions A string containing conditions which must be met * for the item to be visible to the user. * @param {string} caption The caption to show next to the checkbox. + * @param {string=} opt_auth If "readonly", this ValueItem cannot be modified. * @extends {vsaq.questionnaire.items.ValueItem} * @constructor */ -vsaq.questionnaire.items.CheckItem = function(id, conditions, caption) { - goog.base(this, id, conditions, caption); - +vsaq.questionnaire.items.CheckItem = function(id, conditions, caption, opt_auth) { + goog.base(this, id, conditions, caption, undefined, undefined, undefined, + undefined, undefined, opt_auth); /** * The checkbox that is the actual control behind this question. * @type {!HTMLInputElement} @@ -104,13 +105,15 @@ vsaq.questionnaire.items.CheckItem.parse = function(questionStack) { if (item.type != vsaq.questionnaire.items.CheckItem.TYPE) throw new vsaq.questionnaire.items.ParseError('Wrong parser chosen.'); - return new vsaq.questionnaire.items.CheckItem(item.id, item.cond, item.text); + return new vsaq.questionnaire.items.CheckItem(item.id, item.cond, item.text, + item.auth); }; /** @inheritDoc */ vsaq.questionnaire.items.CheckItem.prototype.setReadOnly = function(readOnly) { - this.checkBox_.disabled = readOnly; + // if item marked readonly, always keep it readonly + this.checkBox_.disabled = this.auth == 'readonly' ? true : readOnly; }; diff --git a/vsaq/static/questionnaire/groupitem.js b/vsaq/static/questionnaire/groupitem.js index 06c5235..5663c07 100644 --- a/vsaq/static/questionnaire/groupitem.js +++ b/vsaq/static/questionnaire/groupitem.js @@ -51,11 +51,12 @@ goog.require('vsaq.questionnaire.templates'); * choices in form of dictionaries. * @param {?Array.>} choicesConds An array that * contains all possible conditions in form of dictionaries. + * @param {string=} opt_auth If "readonly", this ValueItem cannot be modified. * @extends {vsaq.questionnaire.items.ContainerItem} * @constructor */ vsaq.questionnaire.items.GroupItem = function(id, conditions, caption, - defaultChoice, choices, choicesConds) { + defaultChoice, choices, choicesConds, opt_auth) { goog.base(this, id, conditions); /** @@ -96,6 +97,10 @@ vsaq.questionnaire.items.GroupItem = function(id, conditions, caption, */ this.defaultChoiceItem = null; + // items of group obtain readonly status from their parents + if (opt_auth == 'readonly') + this.auth = opt_auth; + // Iterate over all choices and create valid questionnaire items for them. goog.array.forEach(choices, function(choice) { // choice is supposed to be a dictionary with exactly one entry. @@ -118,7 +123,8 @@ vsaq.questionnaire.items.GroupItem = function(id, conditions, caption, } var appendNewItem = - this.createSingleItem(choiceId, choiceCondition, choiceText); + this.createSingleItem(choiceId, choiceCondition, choiceText, + this.auth); appendNewItem.parentItemSet(this); goog.events.listen(appendNewItem.eventDispatcher, @@ -141,7 +147,7 @@ vsaq.questionnaire.items.GroupItem.prototype.setDefaultChoice = function() { if (this.defaultChoice && !this.defaultChoiceItem) { var defaultChoiceId = goog.string.createUniqueString(); this.defaultChoiceItem = this.createSingleItem(defaultChoiceId, null, - vsaq.questionnaire.items.GroupItem.DEFAULT_CHOICE); + vsaq.questionnaire.items.GroupItem.DEFAULT_CHOICE, this.auth); this.defaultChoiceItem.parentItemSet(this); goog.events.listen(this.defaultChoiceItem.eventDispatcher, vsaq.questionnaire.items.Item.CHANGED, @@ -214,6 +220,8 @@ vsaq.questionnaire.items.GroupItem.prototype.answerChanged_ = function(ev) { * @param {string} choiceId The choice id * @param {?string} choiceCondition The choice condition * @param {string} choiceText The choice text + * @param {string=} opt_auth If 'readonly', GroupItem's individual ValueItems + * are immutable. * @return {vsaq.questionnaire.items.Item} The choice item */ vsaq.questionnaire.items.GroupItem.prototype.createSingleItem = @@ -255,9 +263,9 @@ vsaq.questionnaire.items.GroupItem.prototype.exportItem = function() { * @constructor * */ vsaq.questionnaire.items.RadiogroupItem = function(id, conditions, caption, - defaultChoice, choices, choicesConds) { + defaultChoice, choices, choicesConds, opt_auth) { goog.base(this, id, conditions, caption, defaultChoice, choices, - choicesConds); + choicesConds, opt_auth); /** @inheritDoc */ this.groupItemType = vsaq.questionnaire.items.RadioItem.TYPE; @@ -268,10 +276,10 @@ goog.inherits(vsaq.questionnaire.items.RadiogroupItem, /** @inheritDoc */ vsaq.questionnaire.items.RadiogroupItem.prototype.createSingleItem = function( - choiceId, choiceCondition, choiceText) { + choiceId, choiceCondition, choiceText, auth) { var newRadioItem = new vsaq.questionnaire.items.RadioItem(choiceId, choiceCondition, - choiceText); + choiceText, auth); // GroupItem passes readonly to ValueItem newRadioItem.type = vsaq.questionnaire.items.RadioItem.TYPE; return newRadioItem; }; @@ -292,7 +300,7 @@ vsaq.questionnaire.items.RadiogroupItem.parse = function(questionStack) { return new vsaq.questionnaire.items.RadiogroupItem( item.id, item.cond, item.text, item.defaultChoice, item.choices, - item.choicesConds); + item.choicesConds, item.auth); }; @@ -312,9 +320,9 @@ vsaq.questionnaire.items.RadiogroupItem.TYPE = 'radiogroup'; * @constructor * */ vsaq.questionnaire.items.CheckgroupItem = function(id, conditions, caption, - defaultChoice, choices, choicesConds) { + defaultChoice, choices, choicesConds, opt_auth) { goog.base(this, id, conditions, caption, defaultChoice, choices, - choicesConds); + choicesConds, opt_auth); /** @inheritDoc */ this.groupItemType = vsaq.questionnaire.items.CheckItem.TYPE; @@ -325,10 +333,11 @@ goog.inherits(vsaq.questionnaire.items.CheckgroupItem, /** @inheritDoc */ vsaq.questionnaire.items.CheckgroupItem.prototype.createSingleItem = function( - choiceId, choiceCondition, choiceText) { + choiceId, choiceCondition, choiceText, opt_auth) { var newCheckItem = new vsaq.questionnaire.items.CheckItem(choiceId, choiceCondition, - choiceText); + choiceText, opt_auth); // GroupItem passes readonly to ValueItem + newCheckItem.type = vsaq.questionnaire.items.CheckItem.TYPE; return newCheckItem; }; @@ -358,5 +367,5 @@ vsaq.questionnaire.items.CheckgroupItem.parse = function(questionStack) { return new vsaq.questionnaire.items.CheckgroupItem( item.id, item.cond, item.text, item.defaultChoice, item.choices, - item.choicesConds); + item.choicesConds, item.auth); }; diff --git a/vsaq/static/questionnaire/groupitem_test.js b/vsaq/static/questionnaire/groupitem_test.js index d614cf5..97d584d 100644 --- a/vsaq/static/questionnaire/groupitem_test.js +++ b/vsaq/static/questionnaire/groupitem_test.js @@ -124,10 +124,53 @@ function testGroupItemParse() { var testStack = [TEST_CHECKGROUP]; checkgroupItem = vsaq.questionnaire.items.CheckgroupItem.parse(testStack); assert(checkgroupItem instanceof vsaq.questionnaire.items.CheckgroupItem); + for (var i = 0; i < checkgroupItem.containerItems.length; ++i) + assertFalse('readonly' == checkgroupItem.containerItems[i].auth); testStack = [TEST_RADIOGROUP]; radiogroupItem = vsaq.questionnaire.items.RadiogroupItem.parse(testStack); assert(radiogroupItem instanceof vsaq.questionnaire.items.RadiogroupItem); + for (var i = 0; i < radiogroupItem.containerItems.length; ++i) + assertFalse('readonly' == radiogroupItem.containerItems[i].auth); + + // Verify all items once again + testGroupItem(); + + // testing readonly functionality + var TEST_CHECKGROUP2 = { + "type": "checkgroup", + "defaultChoice": true, + "text": "caption", + "choices": [ + {"choice_id_1": "Text 1"}, + {"choice_id_2": "Text 2"} + ], + "choicesConds": [], + "auth": "readonly" + }; + + var TEST_RADIOGROUP2 = { + 'type': 'radiogroup', + 'text': 'caption', + 'defaultChoice': false, + 'choices': [ + {'choice_id_1': 'Text 1'}, + {'choice_id_2': 'Text 2'} + ], + 'choicesConds': [], + 'auth': 'readonly' + }; + testStack = [TEST_CHECKGROUP2]; + checkgroupItem = vsaq.questionnaire.items.CheckgroupItem.parse(testStack); + assert(checkgroupItem instanceof vsaq.questionnaire.items.CheckgroupItem); + for (var i = 0; i < checkgroupItem.containerItems.length; ++i) + assertEquals('readonly', checkgroupItem.containerItems[i].auth); + + testStack = [TEST_RADIOGROUP2]; + radiogroupItem = vsaq.questionnaire.items.RadiogroupItem.parse(testStack); + assert(radiogroupItem instanceof vsaq.questionnaire.items.RadiogroupItem); + for (var i = 0; i < radiogroupItem.containerItems.length; ++i) + assertEquals('readonly', radiogroupItem.containerItems[i].auth); // Verify all items once again testGroupItem(); diff --git a/vsaq/static/questionnaire/items.js b/vsaq/static/questionnaire/items.js index 8c9d782..c1e96de 100644 --- a/vsaq/static/questionnaire/items.js +++ b/vsaq/static/questionnaire/items.js @@ -64,7 +64,7 @@ goog.inherits(vsaq.questionnaire.items.ParseError, goog.debug.Error); /** * Base class for all questionnaire items. *

All items derived from this base class need to have a property - * {@code TYPE}, which holds the identifier that is used for the particular item + * `TYPE`, which holds the identifier that is used for the particular item * kind in the serialized format.

* @param {?string} id An ID uniquely identifying the question. * @param {?string} conditions A string containing conditions which must be met @@ -358,7 +358,7 @@ vsaq.questionnaire.items.Item.TYPE; * a Json structure. This function takes the first element from the passed * array of serialized questionnaire items, and returns the parsed item. * If an error is encountered parsing the top item, the function will throw a - * {@code vsaq.questionnaire.items.ParseError}. + * `vsaq.questionnaire.items.ParseError`. * @param {!Array. | !Array.} questionStack * Array of serialized questionnaire items. * @return {!vsaq.questionnaire.items.Item} A single parsed item. @@ -482,16 +482,17 @@ vsaq.questionnaire.items.Item.prototype.exportItem = function() { * @param {string=} opt_inputTitle HTML5 title attribute value for the input * field. See {@link * https://html.spec.whatwg.org/multipage/forms.html#attr-input-title}. - * @param {boolean=} opt_isRequired Iff true, the item value is required. + * @param {boolean=} opt_isRequired If true, the item value is required. * @param {number=} opt_maxlength HTML maxlength attribute value for the input * field. See {@link * https://html.spec.whatwg.org/multipage/forms.html#attr-fe-maxlength} + * @param {string=} opt_auth If "readonly", this ValueItem cannot be modified. * @extends {vsaq.questionnaire.items.Item} * @constructor */ vsaq.questionnaire.items.ValueItem = function(id, conditions, text, opt_placeholder, opt_inputPattern, opt_inputTitle, opt_isRequired, - opt_maxlength) { + opt_maxlength, opt_auth) { if (!id) throw new vsaq.questionnaire.items.ParseError('ValueItem must have an ID.'); @@ -538,6 +539,13 @@ vsaq.questionnaire.items.ValueItem = function(id, conditions, text, * @type {number|undefined} */ this.maxlength = opt_maxlength; + + if (opt_auth == 'readonly') + /** + * If 'readonly', the ValueItem cannot be modified. + * @type {string|undefined} + */ + this.auth = 'readonly'; }; goog.inherits(vsaq.questionnaire.items.ValueItem, vsaq.questionnaire.items.Item); @@ -545,7 +553,7 @@ goog.inherits(vsaq.questionnaire.items.ValueItem, /** * Handles changes to the item and dispatches an - * {@code vsaq.questionnaire.items.Item.CHANGED} event for this item. + * `vsaq.questionnaire.items.Item.CHANGED` event for this item. * @protected */ vsaq.questionnaire.items.ValueItem.prototype.answerChanged = function() { diff --git a/vsaq/static/questionnaire/lineitem.js b/vsaq/static/questionnaire/lineitem.js index f47040e..9a5c069 100644 --- a/vsaq/static/questionnaire/lineitem.js +++ b/vsaq/static/questionnaire/lineitem.js @@ -50,14 +50,15 @@ goog.require('vsaq.questionnaire.utils'); * @param {number=} opt_maxlength HTML maxlength attribute value for the input * field. See {@link * https://html.spec.whatwg.org/multipage/forms.html#attr-fe-maxlength} + * @param {string=} opt_auth If "readonly", this ValueItem cannot be modified. * @extends {vsaq.questionnaire.items.ValueItem} * @constructor */ vsaq.questionnaire.items.LineItem = function(id, conditions, caption, opt_inputType, opt_placeholder, opt_inputPattern, opt_inputTitle, - opt_isRequired, opt_maxlength) { + opt_isRequired, opt_maxlength, opt_auth) { goog.base(this, id, conditions, caption, opt_placeholder, opt_inputPattern, - opt_inputTitle, opt_isRequired, opt_maxlength); + opt_inputTitle, opt_isRequired, opt_maxlength, opt_auth); /** * The html input element where the user can answer the question. @@ -131,13 +132,14 @@ vsaq.questionnaire.items.LineItem.parse = function(questionStack) { return new vsaq.questionnaire.items.LineItem(item.id, item.cond, item.text, item.inputType, item.placeholder, item.inputPattern, item.inputTitle, - item.required, item.maxlength); + item.required, item.maxlength, item.auth); }; /** @inheritDoc */ vsaq.questionnaire.items.LineItem.prototype.setReadOnly = function(readOnly) { - this.textBox_.readOnly = readOnly; + // if item marked readonly, always keep it readonly + this.textBox_.readOnly = this.auth == 'readonly' ? true : readOnly; }; diff --git a/vsaq/static/questionnaire/lineitem_test.js b/vsaq/static/questionnaire/lineitem_test.js index da43420..6744bf5 100644 --- a/vsaq/static/questionnaire/lineitem_test.js +++ b/vsaq/static/questionnaire/lineitem_test.js @@ -93,4 +93,30 @@ function testLineItemParse() { assertEquals('.*', line.inputPattern); assertEquals('date', line.inputType); assertTrue(line.required); + assertTrue(line.auth != 'readonly'); + + testStack = [{ + 'type': 'line', + 'text': CAPTION, + 'id': ID, + 'required' : true, + 'inputPattern': '.*', + 'inputTitle': 'input_title', + 'inputType': 'date', + 'placeholder': 'placeholder', + 'maxlength': 100, + 'auth': 'readonly', + }]; + line = vsaq.questionnaire.items.LineItem.parse(testStack); + assert(line instanceof vsaq.questionnaire.items.LineItem); + assertEquals(ID, line.id); + assertEquals(CAPTION, line.text); + assertEquals(0, testStack.length); + assertEquals('input_title', line.inputTitle); + assertEquals('placeholder', line.placeholder); + assertEquals(100, line.maxlength); + assertEquals('.*', line.inputPattern); + assertEquals('date', line.inputType); + assertTrue(line.required); + assertEquals('readonly', line.auth); } diff --git a/vsaq/static/questionnaire/questionnaire.js b/vsaq/static/questionnaire/questionnaire.js index 8b64071..90d04f5 100644 --- a/vsaq/static/questionnaire/questionnaire.js +++ b/vsaq/static/questionnaire/questionnaire.js @@ -53,7 +53,6 @@ goog.require('goog.json'); goog.require('goog.log'); goog.require('goog.object'); goog.require('goog.soy'); -goog.require('goog.structs'); goog.require('vsaq.questionnaire.items.BlockItem'); goog.require('vsaq.questionnaire.items.BoxItem'); goog.require('vsaq.questionnaire.items.CheckItem'); @@ -92,19 +91,19 @@ goog.inherits(vsaq.questionnaire.QuestionnaireError, goog.debug.Error); * An interactive questionnaire. * * This class allows to display an interactive questionnaire to the user, under - * the {@code Element} passed as a parameter to the constructor. + * the `Element` passed as a parameter to the constructor. * *

After instantiation, the questions and structure of the questionnaire need - * to be provided by setting an Array of {@code vsaq.questionnaire.items.Item}s - * with {@code setTemplate}. If previous answers have been recorded, those can - * be loaded with {@code setValues}.

+ * to be provided by setting an Array of `vsaq.questionnaire.items.Item`s + * with `setTemplate`. If previous answers have been recorded, those can + * be loaded with `setValues`.

* - *

Calling {@code render} will display the questionnaire to the user. It + *

Calling `render` will display the questionnaire to the user. It * should be called only after setting a template.

* *

Any changes the user makes to the questionnaire cause an {@code * goog.events.EventType.CHANGE} event to be raised. At any given time, the - * currently selected answers can be exported through {@code getValuesAsJson} + * currently selected answers can be exported through `getValuesAsJson` * in JSON format.

* * @constructor @@ -119,7 +118,7 @@ vsaq.Questionnaire = function(rootElement) { /** * A dictionary of all items, where their ID is the key. - * @type {!Object.} + * @type {!Object.} * @private */ this.items_ = {}; @@ -133,7 +132,7 @@ vsaq.Questionnaire = function(rootElement) { /** * Stores the values of the answers to the questions in a dictionary where - * the keys are the items' ids (see {@code vsaq.questionnaire.items.Item.id}), + * the keys are the items' ids (see `vsaq.questionnaire.items.Item.id`), * and the values are the answers. * @type {!Object.} * @private @@ -149,7 +148,7 @@ vsaq.Questionnaire = function(rootElement) { /** * Whether events are captured or not. This is because while the questionnaire - * is updated through {@code setValues} no events must be sent. + * is updated through `setValues` no events must be sent. * @type {boolean} * @private */ @@ -320,7 +319,8 @@ vsaq.Questionnaire.prototype.reevaluateConditions_ = function() { var todos = []; var todoStatus = {}; // Show and hide items in the questionnaire - goog.structs.forEach(this.items_, function(item, id, items) { + goog.object.forEach(this.items_, function(item, id, items) { + if (!items) return; if (item instanceof vsaq.questionnaire.items.ValueItem) item.setReadOnly(this.readonlyMode_); @@ -487,7 +487,7 @@ vsaq.Questionnaire.prototype.done = function() { /** * Sets the template object for the questionnaire. If the template is not - * in a valid format, a {@code vsaq.questionnaire.items.ParseError} is thrown. + * in a valid format, a `vsaq.questionnaire.items.ParseError` is thrown. * @param {!vsaq.questionnaire.items.ItemArray} template The template for * questionnaire. * @throws {vsaq.questionnaire.items.ParseError} @@ -525,7 +525,7 @@ vsaq.Questionnaire.prototype.setTemplate = function(template) { * * Note: The order in which template extension are passed matters. * If the extension templates is not in a valid format, a - * {@code vsaq.questionnaire.QuestionnaireError} is thrown. + * `vsaq.questionnaire.QuestionnaireError` is thrown. * * @param {!Object} baseTemplate The base template for the questionnaire. * @param {...!Object} var_args Template extensions that extend the baseTemplate @@ -569,7 +569,7 @@ vsaq.Questionnaire.prototype.setMultipleTemplates = function( * Inserts all items of an extension template into the specified position of the * baseTemplate. * If extension templates are not in a valid format, a - * {@code vsaq.questionnaire.QuestionnaireError} is thrown. + * `vsaq.questionnaire.QuestionnaireError` is thrown. * @param {!Object} baseTemplate The base template where items will be insteted. * @param {!Object} extensionTemplate Template that extends the baseTemplate * (e.g. company specific questions). @@ -644,12 +644,12 @@ vsaq.Questionnaire.prototype.insertItemIntoTemplate_ = function( // We need to go deeper. if (item.hasOwnProperty('items')) { - success |= this.insertItemIntoTemplate_( + success = success || this.insertItemIntoTemplate_( item['items'], newItem, targetItemId, opt_insertAfter); } } - return success; + return !!success; }; @@ -691,7 +691,7 @@ vsaq.Questionnaire.prototype.setValues = function(values, opt_scrollThere) { // Set the new values. We need to disable events during that time, as // we don't want to dispatch CHANGE events for change *to* the desired state. this.isCapturingEvents_ = false; - goog.structs.forEach(this.values_, function(value, id) { + goog.object.forEach(this.values_, function(value, id) { var item = this.items_[id]; if (!item) { this.logger_.warning( diff --git a/vsaq/static/questionnaire/questionnaire_editor.js b/vsaq/static/questionnaire/questionnaire_editor.js index 28badca..8e920a3 100644 --- a/vsaq/static/questionnaire/questionnaire_editor.js +++ b/vsaq/static/questionnaire/questionnaire_editor.js @@ -1183,14 +1183,12 @@ vsaq.QuestionnaireEditor.prototype.addItemEditabilityPrefix = function( // Remove all properties that are mandatory and can already be modified within // the rendered item. - for (var key in knownPropertiesKeys) { - if (!knownPropertiesKeys.hasOwnProperty(key)) - continue; - if (knownPropertiesValues[key].mandatory && - !knownPropertiesValues[key].metadata && - !knownPropertiesValues[key].defaultValues) { - delete knownPropertiesKeys[key]; - delete knownPropertiesValues[key]; + for (var i=0; inone {/if} - {foreach $defaultTypeKey in keys($propertyInformation.defaultValues)} + {for $defaultTypeKey in keys($propertyInformation.defaultValues)} - {/foreach} + {/for} {else} {($propertyInformation.value or 'none')} @@ -104,7 +104,7 @@ {template .editablePrefix}
- {foreach $knownPropertyIndex in keys($knownPropertiesKeys)} + {for $knownPropertyIndex in keys($knownPropertiesKeys)} - {/foreach} + {/for}
{$knownPropertiesKeys[$knownPropertyIndex]} @@ -121,7 +121,7 @@ {/if}
{/template} @@ -140,9 +140,9 @@ Add     {if $defaultType!='block' and $defaultType!='radiogroup' and $defaultType!='checkgroup'} @@ -408,14 +408,14 @@

Based on your selections, we have compiled the following todo list for you:

    - {foreach $todo in $todoListItems} + {for $todo in $todoListItems}
  • - {/foreach} + {/for}
{else}

Great news, you're all set. There is no todo.

diff --git a/vsaq/static/questionnaire/tipitem.js b/vsaq/static/questionnaire/tipitem.js index 00ad65d..0471a56 100644 --- a/vsaq/static/questionnaire/tipitem.js +++ b/vsaq/static/questionnaire/tipitem.js @@ -33,7 +33,7 @@ goog.require('vsaq.questionnaire.utils'); /** - * A tip that provides advice to the user. If {@code clarification} is set to + * A tip that provides advice to the user. If `clarification` is set to * true, additionally a text area is shown to the user where they can provide * additional information. * @param {string} id An ID uniquely identifying the tip. @@ -48,12 +48,15 @@ goog.require('vsaq.questionnaire.utils'); * @param {string=} opt_name Name of the issue. * @param {string=} opt_todo Todo list entry associated with the tip. * @param {string=} opt_customTitle Title of bubble defined in JSON. + * @param {string=} opt_auth If "readonly", this ValueItem cannot be modified. * @extends {vsaq.questionnaire.items.ValueItem} * @constructor */ vsaq.questionnaire.items.TipItem = function(id, conditions, text, warn, - opt_severity, opt_clarification, opt_name, opt_todo, opt_customTitle) { - goog.base(this, id, conditions, text); + opt_severity, opt_clarification, opt_name, opt_todo, opt_customTitle, + opt_auth) { + goog.base(this, id, conditions, text, undefined, undefined, undefined, + undefined, undefined, opt_auth); /** * Indicating whether the tip is a warning or only informational. @@ -192,14 +195,16 @@ vsaq.questionnaire.items.TipItem.parse = function(questionStack) { var isWarn = goog.string.makeSafe(item.warn) == 'yes'; return new vsaq.questionnaire.items.TipItem(item.id, item.cond, item.text, - isWarn, item.severity, item.why, item.name, item.todo, item.customTitle); + isWarn, item.severity, item.why, item.name, item.todo, item.customTitle, + item.auth); }; /** @inheritDoc */ vsaq.questionnaire.items.TipItem.prototype.setReadOnly = function(readOnly) { if (this.textArea_) - this.textArea_.readOnly = readOnly; + // if item marked readonly, always keep it readonly + this.textArea_.readOnly = this.readonly ? true : readOnly; }; diff --git a/vsaq/static/questionnaire/tipitem_test.js b/vsaq/static/questionnaire/tipitem_test.js index cea2e10..37fac04 100644 --- a/vsaq/static/questionnaire/tipitem_test.js +++ b/vsaq/static/questionnaire/tipitem_test.js @@ -133,5 +133,27 @@ function testTipItemParse() { assertEquals(0, testStack.length); assertEquals(TODO, tip.todo); assertEquals(CUSTOMTITLE_TEXT, tip.customTitle); + assertTrue(tip.auth != 'readonly'); + + testStack = [{ + 'type': 'tip', + 'text': CAPTION, + 'id': TIP_ID, + 'warn': 'yes', + 'why': WHY_TEXT, + 'todo': TODO, + 'customTitle' : CUSTOMTITLE_TEXT, + 'auth': 'readonly', + }]; + tip = vsaq.questionnaire.items.TipItem.parse(testStack); + assert(tip instanceof vsaq.questionnaire.items.TipItem); + assertEquals(TIP_ID, tip.id); + assertEquals(CAPTION, tip.text); + assertEquals(true, tip.warn); + assertEquals(WHY_TEXT, tip.clarification); + assertEquals(0, testStack.length); + assertEquals(TODO, tip.todo); + assertEquals(CUSTOMTITLE_TEXT, tip.customTitle); + assertEquals('readonly', tip.auth); } diff --git a/vsaq/static/questionnaire/uploaditem.js b/vsaq/static/questionnaire/uploaditem.js index 8f78e0b..dca947e 100644 --- a/vsaq/static/questionnaire/uploaditem.js +++ b/vsaq/static/questionnaire/uploaditem.js @@ -44,11 +44,13 @@ goog.require('vsaq.questionnaire.utils'); * @param {?string} conditions A string containing conditions which must be met * for the item to be visible to the user. * @param {string} caption The caption to show above the file upload. + * @param {string=} opt_auth If "readonly", this ValueItem cannot be modified. * @extends {vsaq.questionnaire.items.ValueItem} * @constructor */ -vsaq.questionnaire.items.UploadItem = function(id, conditions, caption) { - goog.base(this, id, conditions, caption); +vsaq.questionnaire.items.UploadItem = function(id, conditions, caption, opt_auth) { + goog.base(this, id, conditions, caption, undefined, undefined, undefined, + undefined, undefined, opt_auth); /** * The form through which the file is uploaded. @@ -258,13 +260,14 @@ vsaq.questionnaire.items.UploadItem.parse = function(questionStack) { if (item.type != vsaq.questionnaire.items.UploadItem.TYPE) throw new vsaq.questionnaire.items.ParseError('Wrong parser chosen.'); - return new vsaq.questionnaire.items.UploadItem(item.id, item.cond, item.text); + return new vsaq.questionnaire.items.UploadItem(item.id, item.cond, item.text, item.auth); }; /** @inheritDoc */ vsaq.questionnaire.items.UploadItem.prototype.setReadOnly = function(readOnly) { - this.fileInput_.readOnly = readOnly; + // if item marked readonly, always keep it readonly + this.fileInput_.readOnly = this.auth == 'readonly' ? true : readOnly; }; diff --git a/vsaq/static/questionnaire/uploaditem_test.js b/vsaq/static/questionnaire/uploaditem_test.js index fc9f461..02dbfec 100644 --- a/vsaq/static/questionnaire/uploaditem_test.js +++ b/vsaq/static/questionnaire/uploaditem_test.js @@ -132,6 +132,20 @@ function testUploadItemParse() { assertEquals(ID, upload.id); assertEquals(CAPTION, upload.text); assertEquals(0, testStack.length); + assertTrue(upload.auth != 'readonly'); + + testStack = [{ + 'type': 'upload', + 'text': CAPTION, + 'id': ID, + 'auth': 'readonly' + }]; + upload = vsaq.questionnaire.items.UploadItem.parse(testStack); + assert(upload instanceof vsaq.questionnaire.items.UploadItem); + assertEquals(ID, upload.id); + assertEquals(CAPTION, upload.text); + assertEquals(0, testStack.length); + assertEquals('readonly', upload.auth); } diff --git a/vsaq/static/questionnaire/yesnoitem.js b/vsaq/static/questionnaire/yesnoitem.js index a0b644a..30d4c5f 100644 --- a/vsaq/static/questionnaire/yesnoitem.js +++ b/vsaq/static/questionnaire/yesnoitem.js @@ -40,12 +40,14 @@ goog.require('vsaq.questionnaire.utils'); * @param {string} caption The caption to show next to the radio button. * @param {string} yes String shown as label for the first option. * @param {string} no String shown as label for the second option. + * @param {string=} opt_auth If "readonly", this ValueItem cannot be modified. * @extends {vsaq.questionnaire.items.ValueItem} * @constructor */ vsaq.questionnaire.items.YesNoItem = function(id, conditions, caption, yes, - no) { - goog.base(this, id, conditions, caption); + no, opt_auth) { + goog.base(this, id, conditions, caption, undefined, undefined, undefined, + undefined, undefined, opt_auth); /** * The text for the yes choice. @@ -151,14 +153,15 @@ vsaq.questionnaire.items.YesNoItem.parse = function(questionStack) { throw new vsaq.questionnaire.items.ParseError('Wrong parser chosen.'); return new vsaq.questionnaire.items.YesNoItem(item.id, item.cond, item.text, - item.yes, item.no); + item.yes, item.no, item.auth); }; /** @inheritDoc */ vsaq.questionnaire.items.YesNoItem.prototype.setReadOnly = function(readOnly) { - this.yesRadio_.disabled = readOnly; - this.noRadio_.disabled = readOnly; + // if item marked readonly, always keep it readonly + this.yesRadio_.disabled = this.auth == 'readonly' ? true : readOnly; + this.noRadio_.disabled = this.auth == 'readonly' ? true : readOnly; }; diff --git a/vsaq/static/questionnaire/yesnoitem_test.js b/vsaq/static/questionnaire/yesnoitem_test.js index d911232..6d21c9c 100644 --- a/vsaq/static/questionnaire/yesnoitem_test.js +++ b/vsaq/static/questionnaire/yesnoitem_test.js @@ -100,6 +100,27 @@ function testYesNoItemParse() { assertEquals(YES, yesno.yes); assertEquals(NO, yesno.no); assertEquals(0, testStack.length); + assertTrue(yesno.auth != 'readonly'); + + assert(yesno.yesRadio_ instanceof HTMLInputElement); + assert(yesno.noRadio_ instanceof HTMLInputElement); + + var testStack2 = [{ + 'type': 'yesno', + 'text': CAPTION, + 'id': ID, + 'yes': YES, + 'no': NO, + 'auth': 'readonly' + }]; + yesno = vsaq.questionnaire.items.YesNoItem.parse(testStack2); + assert(yesno instanceof vsaq.questionnaire.items.YesNoItem); + assertEquals(ID, yesno.id); + assertEquals(CAPTION, yesno.text); + assertEquals(YES, yesno.yes); + assertEquals(NO, yesno.no); + assertEquals(0, testStack.length); + assertEquals('readonly', yesno.auth); assert(yesno.yesRadio_ instanceof HTMLInputElement); assert(yesno.noRadio_ instanceof HTMLInputElement); diff --git a/vsaq/static/utils.js b/vsaq/static/utils.js index 4098d5b..1f0b604 100644 --- a/vsaq/static/utils.js +++ b/vsaq/static/utils.js @@ -29,7 +29,6 @@ goog.require('goog.events.EventType'); goog.require('goog.object'); goog.require('goog.string'); goog.require('goog.string.format'); -goog.require('goog.structs'); /** @@ -62,7 +61,7 @@ vsaq.utils.addClickHandler = function(elementId, callback) { vsaq.utils.initClickables = function(handlers) { goog.events.listen(goog.dom.getDocument(), [goog.events.EventType.CLICK], function(e) { - goog.structs.forEach(handlers, function(handler, className) { + goog.object.forEach(handlers, function(handler, className) { var clickable = goog.dom.getAncestorByClass(e.target, className); if (clickable && !goog.dom.classlist.contains(clickable, 'maia-button-disabled')) {