├── .gitignore ├── vsaq └── static │ ├── loading.gif │ ├── mode_edit.png │ ├── vsaq.css │ ├── questionnaire │ ├── questionnaire_editor_init.js │ ├── questionnaire_test_dom.html │ ├── items_test_dom.html │ ├── utils_test_dom.html │ ├── boxitem_test_dom.html │ ├── tipitem_test_dom.html │ ├── blockitems_test_dom.html │ ├── checkitem_test_dom.html │ ├── groupitem_test_dom.html │ ├── infoitem_test_dom.html │ ├── lineitem_test_dom.html │ ├── radioitem_test_dom.html │ ├── spaceritem_test_dom.html │ ├── uploaditem_test_dom.html │ ├── yesnoitem_test_dom.html │ ├── containeritem_test_dom.html │ ├── externs.js │ ├── questionnaire_editor_test_dom.html │ ├── infoitem_test.js │ ├── spaceritem_test.js │ ├── containeritem_test.js │ ├── boxitem_test.js │ ├── lineitem_test.js │ ├── checkitem_test.js │ ├── spaceritem.js │ ├── yesnoitem_test.js │ ├── infoitem.js │ ├── radioitem_test.js │ ├── blockitems_test.js │ ├── groupitem_test.js │ ├── tipitem_test.js │ ├── checkitem.js │ ├── boxitem.js │ ├── lineitem.js │ ├── utils_test.js │ ├── containeritem.js │ ├── blockitems.js │ ├── radioitem.js │ ├── yesnoitem.js │ ├── tipitem.js │ ├── uploaditem_test.js │ └── uploaditem.js │ ├── dialoghelper.js │ ├── utils.js │ ├── qpage_base.js │ └── vsaq_base.css ├── questionnaires ├── test_template_extension.json └── test_template.json ├── third_party └── docs-build │ └── dossier-config.json ├── .gitmodules ├── AUTHORS ├── compiler.flags ├── CONTRIBUTING ├── client_side_only_impl ├── example.html ├── index.html ├── vsaq.html ├── vsaq_editor.html └── all_tests.html ├── download-libs.sh ├── vsaq_server.py ├── README.md └── do.sh /.gitignore: -------------------------------------------------------------------------------- 1 | # Files generated during the build process 2 | build/ 3 | -------------------------------------------------------------------------------- /vsaq/static/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tes/vsaq/master/vsaq/static/loading.gif -------------------------------------------------------------------------------- /vsaq/static/mode_edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tes/vsaq/master/vsaq/static/mode_edit.png -------------------------------------------------------------------------------- /questionnaires/test_template_extension.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "namespace": "mycompany", 4 | "extensions": [ 5 | { 6 | "type": "block", 7 | "insertAfter": "network", 8 | "text": "Questions specific to my company come here.", 9 | "items": [ 10 | { 11 | "type": "line", 12 | "id": "company_specific", 13 | "text": "Company specific question" 14 | } 15 | ] 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /third_party/docs-build/dossier-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "output": "docs/api/", 3 | "sources": ["vsaq", "client_side_only_impl", "build/templates", "third_party/closure-templates-compiler/soyutils_usegoog.js"], 4 | "externs" : ["third_party/closure-compiler/contrib/externs/chrome_extensions.js"], 5 | "closureLibraryDir": "third_party/closure-library/closure/goog/", 6 | "excludes": ["vsaq/**_test.js"], 7 | "typeFilters": [ 8 | "^goog", 9 | "^soy", 10 | "^soydata" 11 | ], 12 | "readme": "README.md" 13 | } 14 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "third_party/closure-compiler"] 2 | path = third_party/closure-compiler 3 | url = https://github.com/google/closure-compiler 4 | [submodule "third_party/closure-library"] 5 | path = third_party/closure-library 6 | url = https://github.com/google/closure-library 7 | [submodule "third_party/closure-stylesheets"] 8 | path = third_party/closure-stylesheets 9 | url = https://github.com/google/closure-stylesheets 10 | [submodule "third_party/js-dossier"] 11 | path = third_party/js-dossier 12 | url = https://github.com/jleyba/js-dossier 13 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | # This file lists all individuals having contributed content to the repository. 2 | # This file is distinct from the CONTRIBUTING files. 3 | # See the latter for an explanation. 4 | 5 | # Names should be added to this file as: 6 | # email address (Name or Organization) 7 | # The email address is not required for organizations. 8 | 9 | dfabian@google.com (Daniel Fabian) 10 | lwe@google.com (Lukas Weichselbaum) 11 | lcamtuf@google.com (Michal Zalewski) 12 | ruslanh@google.com (Ruslan Habalov) 13 | koto@google.com (Krzysztof Kotowicz) 14 | carstein@google.com (Michal Melewski) 15 | cri@google.com (Chris John Riley) -------------------------------------------------------------------------------- /vsaq/static/vsaq.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2016 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * Any custom VSAQ (core) css will be contained here. 19 | */ 20 | -------------------------------------------------------------------------------- /vsaq/static/questionnaire/questionnaire_editor_init.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright 2016 Google Inc. All rights reserved. 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @fileoverview Initialize the questionnaire editor here. 19 | */ 20 | 21 | goog.require('vsaq.QuestionnaireEditor'); 22 | 23 | // Initialize the VSAQ WYSIWYG editor. 24 | if (!goog.getObjectByName('goog.testing.TestRunner')) 25 | new vsaq.QuestionnaireEditor(); 26 | -------------------------------------------------------------------------------- /vsaq/static/questionnaire/questionnaire_test_dom.html: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | Unit Test of Closure-Using Code 19 | 21 | 22 | 25 | 26 | 27 |
28 | 29 | 30 | -------------------------------------------------------------------------------- /compiler.flags: -------------------------------------------------------------------------------- 1 | --language_in=ECMASCRIPT5 2 | --jscomp_error=accessControls 3 | --jscomp_error=ambiguousFunctionDecl 4 | --jscomp_error=checkDebuggerStatement 5 | --jscomp_error=checkEventfulObjectDisposal 6 | --jscomp_error=checkRegExp 7 | --jscomp_error=checkTypes 8 | --jscomp_error=checkVars 9 | --jscomp_error=const 10 | --jscomp_error=constantProperty 11 | --jscomp_error=deprecated 12 | --jscomp_error=duplicate 13 | --jscomp_error=duplicateMessage 14 | --jscomp_error=es5Strict 15 | --jscomp_error=es3 16 | --jscomp_error=externsValidation 17 | --jscomp_error=fileoverviewTags 18 | --jscomp_error=globalThis 19 | --jscomp_error=internetExplorerChecks 20 | --jscomp_error=invalidCasts 21 | --jscomp_error=missingProperties 22 | --jscomp_error=nonStandardJsDocs 23 | --jscomp_error=strictModuleDepCheck 24 | --jscomp_error=undefinedNames 25 | --jscomp_error=undefinedVars 26 | --jscomp_error=unknownDefines 27 | --jscomp_error=uselessCode 28 | --jscomp_error=visibility 29 | --externs=third_party/closure-compiler/contrib/externs/chrome_extensions.js 30 | --only_closure_dependencies 31 | --manage_closure_dependencies 32 | --js third_party/closure-library/closure/goog/deps.js 33 | --js build/deps.js 34 | -------------------------------------------------------------------------------- /vsaq/static/questionnaire/items_test_dom.html: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | Unit Test of Closure-Using Code 19 | 21 | 22 | 25 | 26 | 27 |
28 | 29 | 30 | -------------------------------------------------------------------------------- /vsaq/static/questionnaire/utils_test_dom.html: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | Unit Test of Closure-Using Code 19 | 21 | 22 | 25 | 26 | 27 |
28 | 29 | 30 | -------------------------------------------------------------------------------- /vsaq/static/questionnaire/boxitem_test_dom.html: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | Unit Test of Closure-Using Code 19 | 21 | 22 | 25 | 26 | 27 |
28 | 29 | 30 | -------------------------------------------------------------------------------- /vsaq/static/questionnaire/tipitem_test_dom.html: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | Unit Test of Closure-Using Code 19 | 21 | 22 | 25 | 26 | 27 |
28 | 29 | 30 | -------------------------------------------------------------------------------- /vsaq/static/questionnaire/blockitems_test_dom.html: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | Unit Test of Closure-Using Code 19 | 21 | 22 | 25 | 26 | 27 |
28 | 29 | 30 | -------------------------------------------------------------------------------- /vsaq/static/questionnaire/checkitem_test_dom.html: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | Unit Test of Closure-Using Code 19 | 21 | 22 | 25 | 26 | 27 |
28 | 29 | 30 | -------------------------------------------------------------------------------- /vsaq/static/questionnaire/groupitem_test_dom.html: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | Unit Test of Closure-Using Code 19 | 21 | 22 | 25 | 26 | 27 |
28 | 29 | 30 | -------------------------------------------------------------------------------- /vsaq/static/questionnaire/infoitem_test_dom.html: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | Unit Test of Closure-Using Code 19 | 21 | 22 | 25 | 26 | 27 |
28 | 29 | 30 | -------------------------------------------------------------------------------- /vsaq/static/questionnaire/lineitem_test_dom.html: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | Unit Test of Closure-Using Code 19 | 21 | 22 | 25 | 26 | 27 |
28 | 29 | 30 | -------------------------------------------------------------------------------- /vsaq/static/questionnaire/radioitem_test_dom.html: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | Unit Test of Closure-Using Code 19 | 21 | 22 | 25 | 26 | 27 |
28 | 29 | 30 | -------------------------------------------------------------------------------- /vsaq/static/questionnaire/spaceritem_test_dom.html: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | Unit Test of Closure-Using Code 19 | 21 | 22 | 25 | 26 | 27 |
28 | 29 | 30 | -------------------------------------------------------------------------------- /vsaq/static/questionnaire/uploaditem_test_dom.html: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | Unit Test of Closure-Using Code 19 | 21 | 22 | 25 | 26 | 27 |
28 | 29 | 30 | -------------------------------------------------------------------------------- /vsaq/static/questionnaire/yesnoitem_test_dom.html: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | Unit Test of Closure-Using Code 19 | 21 | 22 | 25 | 26 | 27 |
28 | 29 | 30 | -------------------------------------------------------------------------------- /vsaq/static/questionnaire/containeritem_test_dom.html: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | Unit Test of Closure-Using Code 19 | 21 | 22 | 25 | 26 | 27 |
28 | 29 | 30 | -------------------------------------------------------------------------------- /CONTRIBUTING: -------------------------------------------------------------------------------- 1 | Want to contribute? Great! First, read this page (including the small print at the end). 2 | 3 | ### Before you contribute 4 | Before we can use your code, you must sign the 5 | [Google Individual Contributor License Agreement](https://cla.developers.google.com/about/google-individual) 6 | (CLA), which you can do online. The CLA is necessary mainly because you own the 7 | copyright to your changes, even after your contribution becomes part of our 8 | codebase, so we need your permission to use and distribute your code. We also 9 | need to be sure of various other things—for instance that you'll tell us if you 10 | know that your code infringes on other people's patents. You don't have to sign 11 | the CLA until after you've submitted your code for review and a member has 12 | approved it, but you must do it before we can put your code into our codebase. 13 | Before you start working on a larger contribution, you should get in touch with 14 | us first through the issue tracker with your idea so that we can help out and 15 | possibly guide you. Coordinating up front makes it much easier to avoid 16 | frustration later on. 17 | 18 | ### Code reviews 19 | All submissions, including submissions by project members, require review. We 20 | use Github pull requests for this purpose. 21 | 22 | ### The small print 23 | Contributions made by corporations are covered by a different agreement than 24 | the one above, the 25 | [Software Grant and Corporate Contributor License Agreement](https://cla.developers.google.com/about/google-corporate). -------------------------------------------------------------------------------- /vsaq/static/questionnaire/externs.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * @license 4 | * Copyright 2016 Google Inc. All rights reserved. 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | /** 19 | * @fileoverview Externs for questionnaire. 20 | * @externs 21 | */ 22 | 23 | 24 | /** @type {Object} */ 25 | var qjson = {}; 26 | 27 | 28 | /** 29 | * @typedef {{ 30 | * auth: string, 31 | * choices: Object., 32 | * choicesConds: Object., 33 | * className: string, 34 | * cond: string, 35 | * customTitle: string, 36 | * default: string, 37 | * defaultChoice: boolean, 38 | * id: string, 39 | * inputType: string, 40 | * inputPattern: string, 41 | * inputTitle: string, 42 | * name: string, 43 | * no: string, 44 | * placeholder: string, 45 | * required: boolean, 46 | * severity: string, 47 | * style: string, 48 | * text: string, 49 | * todo: string, 50 | * type: string, 51 | * warn: string, 52 | * why: string, 53 | * yes: string 54 | * }} 55 | */ 56 | qjson.QuestionnaireItem; 57 | -------------------------------------------------------------------------------- /vsaq/static/questionnaire/questionnaire_editor_test_dom.html: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | Unit Test of Closure-Using Code 19 | 21 | 22 | 25 | 26 | 27 | 28 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /questionnaires/test_template.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "questionnaire": [ 4 | { 5 | "type": "block", 6 | "text": "My questionnaire", 7 | "items": [ 8 | { 9 | "type": "block", 10 | "id": "network", 11 | "text": "Network section", 12 | "items": [ 13 | { 14 | "type": "info", 15 | "text": "What security standards do you apply?" 16 | }, 17 | { 18 | "type": "checkgroup", 19 | "defaultChoice": false, 20 | "choices": [ 21 | {"standard_1": "Standard 1"}, 22 | {"standard_2": "Standard 2"} 23 | ] 24 | } 25 | ] 26 | }, 27 | { 28 | "type": "block", 29 | "text": "Data section", 30 | "items": [ 31 | { 32 | "type": "check", 33 | "id": "data_warning", 34 | "text": "Click me to trigger a warning." 35 | }, 36 | { 37 | "type": "tip", 38 | "id": "data_tip", 39 | "cond": "data_warning/yes", 40 | "text": "You shouldn't click the box above.", 41 | "warn": "yes", 42 | "severity": "critical" 43 | } 44 | ] 45 | }, 46 | { 47 | "type": "block", 48 | "text": "Final section", 49 | "items": [ 50 | { 51 | "type": "check", 52 | "id": "declaration", 53 | "text": "I hereby declare that the above answers are true to the best of my knowledge and belief." 54 | } 55 | ] 56 | } 57 | ] 58 | } 59 | ] 60 | } 61 | -------------------------------------------------------------------------------- /vsaq/static/questionnaire/infoitem_test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright 2016 Google Inc. All rights reserved. 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @fileoverview Tests for vsaq.questionnaire.items.InfoItem. 19 | */ 20 | 21 | goog.provide('vsaq.questionnaire.items.InfoItemTests'); 22 | goog.setTestOnly('vsaq.questionnaire.items.InfoItemTests'); 23 | 24 | goog.require('goog.dom'); 25 | goog.require('goog.dom.TagName'); 26 | goog.require('goog.testing.jsunit'); 27 | goog.require('vsaq.questionnaire.items.InfoItem'); 28 | 29 | var CAPTION = 'infoitem_caption'; 30 | var ID = 'infoitem_id'; 31 | 32 | var info; 33 | 34 | 35 | /** 36 | * Initializes variables used by all tests. 37 | */ 38 | function setUp() { 39 | info = new vsaq.questionnaire.items.InfoItem(ID, [], CAPTION); 40 | } 41 | 42 | 43 | /** 44 | * Tests whether info items are rendered correctly. 45 | */ 46 | function testInfoItem() { 47 | var el = info.container; 48 | 49 | assertEquals(goog.dom.TagName.DIV, el.tagName); 50 | assertEquals(ID, el.id); 51 | assertEquals(CAPTION, goog.dom.getTextContent(el)); 52 | } 53 | 54 | 55 | /** 56 | * Tests parsing of InfoItems. 57 | */ 58 | function testInfoItemParse() { 59 | var testStack = [{ 60 | 'type': 'info', 61 | 'text': CAPTION, 62 | 'id': ID 63 | }]; 64 | info = vsaq.questionnaire.items.InfoItem.parse(testStack); 65 | assert(info instanceof vsaq.questionnaire.items.InfoItem); 66 | assertEquals(ID, info.id); 67 | assertEquals(CAPTION, info.info); 68 | assertEquals(0, testStack.length); 69 | } 70 | 71 | -------------------------------------------------------------------------------- /vsaq/static/questionnaire/spaceritem_test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright 2016 Google Inc. All rights reserved. 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @fileoverview Tests for vsaq.questionnaire.items.SpacerItem. 19 | */ 20 | 21 | goog.provide('vsaq.questionnaire.items.SpacerItemTests'); 22 | goog.setTestOnly('vsaq.questionnaire.items.SpacerItemTests'); 23 | 24 | goog.require('goog.dom.TagName'); 25 | goog.require('goog.dom.classlist'); 26 | goog.require('goog.testing.asserts'); 27 | goog.require('goog.testing.jsunit'); 28 | goog.require('vsaq.questionnaire.items.SpacerItem'); 29 | 30 | var ID = 'spaceritem_id'; 31 | 32 | var spacer; 33 | 34 | 35 | /** 36 | * Initializes variables used by all tests. 37 | */ 38 | function setUp() { 39 | spacer = new vsaq.questionnaire.items.SpacerItem(ID, []); 40 | } 41 | 42 | 43 | /** 44 | * Tests whether spacer items are rendered correctly. 45 | */ 46 | function testSpacerItem() { 47 | var el = spacer.container; 48 | 49 | assertEquals(goog.dom.TagName.DIV, el.tagName); 50 | assertNotUndefined(el.firstChild); 51 | assertEquals(goog.dom.TagName.HR, el.firstChild.tagName); 52 | assertTrue(goog.dom.classlist.contains(el.firstChild, 'vsaq-spacer')); 53 | } 54 | 55 | 56 | /** 57 | * Tests parsing of SpacerItems. 58 | */ 59 | function testSpacerItemParse() { 60 | var testStack = [{ 'type': 'spacer' }]; 61 | spacer = vsaq.questionnaire.items.SpacerItem.parse(testStack); 62 | assert(spacer instanceof vsaq.questionnaire.items.SpacerItem); 63 | assertEquals(0, testStack.length); 64 | } 65 | 66 | -------------------------------------------------------------------------------- /vsaq/static/questionnaire/containeritem_test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright 2016 Google Inc. All rights reserved. 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @fileoverview Tests for vsaq.questionnaire.items.ContainerItem 19 | */ 20 | 21 | goog.provide('vsaq.questionnaire.items.ContainerItemTests'); 22 | goog.setTestOnly('vsaq.questionnaire.items.ContainerItemTests'); 23 | 24 | goog.require('goog.array'); 25 | goog.require('goog.testing.jsunit'); 26 | goog.require('vsaq.questionnaire.items.CheckItem'); 27 | goog.require('vsaq.questionnaire.items.ContainerItem'); 28 | 29 | var CAPTION = 'test_caption'; 30 | var ID = 'test_id'; 31 | var ADMIN = 'admin'; 32 | 33 | var NUMBER_CONTAINER_ITEMS = 5; 34 | 35 | var container; 36 | 37 | 38 | /** 39 | * Initializes variables used by all tests. 40 | */ 41 | function setUp() { 42 | container = new vsaq.questionnaire.items.ContainerItem(ID, [], ADMIN, null); 43 | for (var i = 0; i < NUMBER_CONTAINER_ITEMS; i++) { 44 | var checkItem = new vsaq.questionnaire.items.CheckItem(ID + i, [], 45 | CAPTION + i); 46 | container.addItem(checkItem); 47 | } 48 | } 49 | 50 | 51 | /** 52 | * Tests whether containers are storing items correctly. 53 | */ 54 | function testContainerItem() { 55 | assertEquals(container.containerItems.length, NUMBER_CONTAINER_ITEMS); 56 | // Test if after deletion each gap was correctly closed again. 57 | var containedItems = []; 58 | goog.array.forEach(container.containerItems, function(item) { 59 | containedItems.push(item); 60 | }); 61 | for (var i = 0; i < NUMBER_CONTAINER_ITEMS; i++) 62 | container.deleteItem(containedItems[i]); 63 | assertEquals(container.containerItems.length, 0); 64 | } 65 | -------------------------------------------------------------------------------- /vsaq/static/dialoghelper.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright 2016 Google Inc. All rights reserved. 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @fileoverview A helper to handle dialogs. The reason goog.ui.dialog is not 19 | * used is that it makes it fairly hard to apply custom styling. 20 | */ 21 | 22 | goog.provide('vsaq.helpers.Dialog'); 23 | 24 | goog.require('goog.dom'); 25 | goog.require('goog.events'); 26 | goog.require('goog.events.EventType'); 27 | goog.require('goog.soy'); 28 | 29 | 30 | 31 | /** 32 | * Constructor for dialogs. 33 | * @param {!Element} place Element where the dialog can be inserted into the 34 | * DOM. 35 | * @param {Function} template A soy template for the dialog. 36 | * @param {Object=} opt_parameters Parameters passed to the soy template. 37 | * @param {Function=} opt_clickCallback Function called when anything in the 38 | * dialog is clicked. 39 | * @constructor 40 | */ 41 | vsaq.helpers.Dialog = function(place, template, opt_parameters, 42 | opt_clickCallback) { 43 | this.element_ = goog.soy.renderAsElement(template, opt_parameters); 44 | this.backdrop_ = goog.dom.createDom('div', 'vsaq-overlay-backdrop'); 45 | goog.dom.appendChild(place, this.element_); 46 | goog.dom.appendChild(place, this.backdrop_); 47 | if (opt_clickCallback) 48 | goog.events.listen(this.element_, [goog.events.EventType.CLICK], 49 | opt_clickCallback); 50 | }; 51 | 52 | 53 | /** 54 | * Shows or hides the dialog. 55 | * @param {boolean} isVisible Whether the dialog should be made visible or 56 | * invisible. 57 | */ 58 | vsaq.helpers.Dialog.prototype.setVisible = function(isVisible) { 59 | this.element_.style.display = isVisible ? 'block' : 'none'; 60 | this.backdrop_.style.display = isVisible ? 'block' : 'none'; 61 | this.element_.style.marginTop = -this.element_.clientHeight / 2 + 'px'; 62 | this.element_.style.marginLeft = -this.element_.clientHeight / 2 + 'px'; 63 | }; 64 | 65 | 66 | /** 67 | * Disposes the dialog. 68 | */ 69 | vsaq.helpers.Dialog.prototype.dispose = function() { 70 | this.setVisible(false); 71 | goog.dom.removeNode(this.element_); 72 | goog.dom.removeNode(this.backdrop_); 73 | }; 74 | -------------------------------------------------------------------------------- /client_side_only_impl/example.html: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | VSAQ - Security Assessment Questionnaires 22 | 23 | 24 | 25 | 26 | 34 | 35 | 41 | 42 |
43 |
44 |
45 |
46 |
47 |

Example Questionnaires

48 | 52 | 53 |

Tests

54 | 57 |
58 |
59 |
60 |
61 | 62 |
63 |
64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /vsaq/static/questionnaire/boxitem_test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright 2016 Google Inc. All rights reserved. 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @fileoverview Tests for vsaq.questionnaire.items.BoxItem. 19 | */ 20 | 21 | goog.provide('vsaq.questionnaire.items.BoxItemTests'); 22 | goog.setTestOnly('vsaq.questionnaire.items.BoxItemTests'); 23 | 24 | goog.require('goog.dom'); 25 | goog.require('goog.dom.TagName'); 26 | goog.require('goog.dom.classlist'); 27 | goog.require('goog.testing.asserts'); 28 | goog.require('goog.testing.jsunit'); 29 | goog.require('vsaq.questionnaire.items.BoxItem'); 30 | 31 | var CAPTION = 'boxitem_caption'; 32 | var ID = 'boxitem_id'; 33 | var VALUE = 'boxitem_value'; 34 | 35 | var box; 36 | 37 | 38 | /** 39 | * Initializes variables used by all tests. 40 | */ 41 | function setUp() { 42 | box = new vsaq.questionnaire.items.BoxItem(ID, [], CAPTION); 43 | } 44 | 45 | 46 | /** 47 | * Tests whether box items are rendered correctly. 48 | */ 49 | function testBoxItem() { 50 | var el = box.container; 51 | 52 | assertEquals(goog.dom.TagName.DIV, el.tagName); 53 | var desc = goog.dom.getFirstElementChild(el); 54 | assertTrue('Class not set', 55 | goog.dom.classlist.contains(desc, 'vsaq-question-title')); 56 | assertEquals(CAPTION, goog.dom.getTextContent(desc)); 57 | var area = goog.dom.getNextElementSibling(desc); 58 | assertEquals(ID, area.id); 59 | assertEquals('', area.value); 60 | } 61 | 62 | 63 | /** 64 | * Tests setting and retrieving the value of the item. 65 | */ 66 | function testBoxItemSetGetValue() { 67 | assertEquals('', box.getValue()); 68 | box.setValue(VALUE); 69 | assertEquals(VALUE, box.getValue()); 70 | } 71 | 72 | 73 | /** 74 | * Tests parsing of BoxItems. 75 | */ 76 | function testBoxItemParse() { 77 | var testStack = [{ 78 | 'type': 'box', 79 | 'text': CAPTION, 80 | 'id': ID, 81 | 'required' : true, 82 | 'placeholder': 'placeholder' 83 | }]; 84 | box = vsaq.questionnaire.items.BoxItem.parse(testStack); 85 | assert(box instanceof vsaq.questionnaire.items.BoxItem); 86 | assertEquals(ID, box.id); 87 | assertEquals(CAPTION, box.text); 88 | assertEquals('placeholder', box.placeholder); 89 | assertTrue(box.required); 90 | assertEquals(0, testStack.length); 91 | } 92 | -------------------------------------------------------------------------------- /vsaq/static/questionnaire/lineitem_test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright 2016 Google Inc. All rights reserved. 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @fileoverview Tests for vsaq.questionnaire.items.LineItem. 19 | */ 20 | 21 | goog.provide('vsaq.questionnaire.items.LineItemTests'); 22 | goog.setTestOnly('vsaq.questionnaire.items.LineItemTests'); 23 | 24 | goog.require('goog.dom'); 25 | goog.require('goog.dom.TagName'); 26 | goog.require('goog.testing.jsunit'); 27 | goog.require('vsaq.questionnaire.items.LineItem'); 28 | 29 | var CAPTION = 'lineitem_caption'; 30 | var ID = 'lineitem_id'; 31 | var VALUE = 'lineitem_value'; 32 | 33 | var line; 34 | 35 | 36 | /** 37 | * Initializes variables used by all tests. 38 | */ 39 | function setUp() { 40 | line = new vsaq.questionnaire.items.LineItem(ID, [], CAPTION); 41 | } 42 | 43 | 44 | /** 45 | * Tests whether line items are rendered correctly. 46 | */ 47 | function testLineItem() { 48 | var el = line.container; 49 | 50 | assertEquals(goog.dom.TagName.DIV, el.tagName); 51 | var desc = goog.dom.getFirstElementChild(el); 52 | assertEquals(CAPTION, goog.dom.getTextContent(desc)); 53 | var input = goog.dom.getNextElementSibling(desc); 54 | assertEquals(goog.dom.TagName.INPUT, input.tagName); 55 | assertEquals('', input.value); 56 | assertEquals(ID, input.id); 57 | } 58 | 59 | 60 | /** 61 | * Tests setting and retrieving the value of the item. 62 | */ 63 | function testLineItemSetGetValue() { 64 | assertEquals('', line.getValue()); 65 | line.setValue(VALUE); 66 | assertEquals(VALUE, line.getValue()); 67 | } 68 | 69 | 70 | /** 71 | * Tests parsing of LineItems. 72 | */ 73 | function testLineItemParse() { 74 | var testStack = [{ 75 | 'type': 'line', 76 | 'text': CAPTION, 77 | 'id': ID, 78 | 'required' : true, 79 | 'inputPattern': '.*', 80 | 'inputTitle': 'input_title', 81 | 'inputType': 'date', 82 | 'placeholder': 'placeholder', 83 | }]; 84 | line = vsaq.questionnaire.items.LineItem.parse(testStack); 85 | assert(line instanceof vsaq.questionnaire.items.LineItem); 86 | assertEquals(ID, line.id); 87 | assertEquals(CAPTION, line.text); 88 | assertEquals(0, testStack.length); 89 | assertEquals('input_title', line.inputTitle); 90 | assertEquals('placeholder', line.placeholder); 91 | assertEquals('.*', line.inputPattern); 92 | assertEquals('date', line.inputType); 93 | assertTrue(line.required); 94 | } 95 | -------------------------------------------------------------------------------- /vsaq/static/questionnaire/checkitem_test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright 2016 Google Inc. All rights reserved. 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @fileoverview Tests for vsaq.questionnaire.items.CheckItem. 19 | */ 20 | 21 | goog.provide('vsaq.questionnaire.items.CheckItemTests'); 22 | goog.setTestOnly('vsaq.questionnaire.items.CheckItemTests'); 23 | 24 | goog.require('goog.dom'); 25 | goog.require('goog.dom.TagName'); 26 | goog.require('goog.testing.asserts'); 27 | goog.require('goog.testing.jsunit'); 28 | goog.require('vsaq.questionnaire.items.CheckItem'); 29 | 30 | var CAPTION = 'checkitem_caption'; 31 | var ID = 'checkitem_id'; 32 | var VALUE = 'checked'; 33 | 34 | var check; 35 | 36 | 37 | /** 38 | * Tests whether blocks are rendered correctly. 39 | */ 40 | function setUp() { 41 | check = new vsaq.questionnaire.items.CheckItem(ID, [], CAPTION); 42 | } 43 | 44 | 45 | /** 46 | * Helper function that returns true if string has suffix. 47 | * @param {String} str String that should be checked for the suffix. 48 | * @param {String} suffix The suffix the string should be checked for. 49 | * @return {bool} True if the string has the given suffix. 50 | */ 51 | function endsWith(str, suffix) { 52 | return str.indexOf(suffix, str.length - suffix.length) !== -1; 53 | } 54 | 55 | 56 | /** 57 | * Tests whether check items are rendered correctly. 58 | */ 59 | function testCheckItem() { 60 | var el = check.container; 61 | 62 | assertEquals(goog.dom.TagName.DIV, el.tagName); 63 | assertContains('vsaq-checkbox-item', el.className); 64 | assert(check.checkBox_ instanceof HTMLInputElement); 65 | var label = goog.dom.getLastElementChild(el); 66 | assert(endsWith(goog.dom.getTextContent(label), CAPTION)); 67 | } 68 | 69 | 70 | /** 71 | * Tests setting and retrieving the value of the item. 72 | */ 73 | function testCheckItemSetGetValue() { 74 | assertEquals('', check.getValue()); 75 | check.setValue(VALUE); 76 | assertEquals(VALUE, check.getValue()); 77 | } 78 | 79 | 80 | /** 81 | * Tests parsing of CheckItems. 82 | */ 83 | function testCheckItemParse() { 84 | var testStack = [{ 85 | 'type': 'check', 86 | 'text': CAPTION, 87 | 'id': ID 88 | }]; 89 | check = vsaq.questionnaire.items.CheckItem.parse(testStack); 90 | assert(check instanceof vsaq.questionnaire.items.CheckItem); 91 | assertEquals(ID, check.id); 92 | assertEquals(CAPTION, check.text); 93 | assertEquals(0, testStack.length); 94 | } 95 | -------------------------------------------------------------------------------- /vsaq/static/questionnaire/spaceritem.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright 2016 Google Inc. All rights reserved. 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @fileoverview Questionnaire informational items. 19 | */ 20 | 21 | goog.provide('vsaq.questionnaire.items.SpacerItem'); 22 | 23 | goog.require('goog.dom'); 24 | goog.require('goog.soy'); 25 | goog.require('vsaq.questionnaire.items.Item'); 26 | goog.require('vsaq.questionnaire.items.ParseError'); 27 | goog.require('vsaq.questionnaire.templates'); 28 | 29 | 30 | 31 | /** 32 | * An item that shows some information to the user. The information may contain 33 | * HTML. 34 | * @param {?string} id An ID uniquely identifying the item. 35 | * @param {?string} conditions A string containing conditions which must be met 36 | * for the item to be visible to the user. 37 | * @extends {vsaq.questionnaire.items.Item} 38 | * @constructor 39 | */ 40 | vsaq.questionnaire.items.SpacerItem = function(id, conditions) { 41 | goog.base(this, id, conditions); 42 | 43 | this.render(); 44 | }; 45 | goog.inherits(vsaq.questionnaire.items.SpacerItem, 46 | vsaq.questionnaire.items.Item); 47 | 48 | 49 | /** 50 | * Render the HTML for this item. 51 | */ 52 | vsaq.questionnaire.items.SpacerItem.prototype.render = function() { 53 | var oldNode = this.container; 54 | this.container = goog.soy.renderAsElement(vsaq.questionnaire.templates.spacer, 55 | {id: this.id}); 56 | goog.dom.replaceNode(this.container, oldNode); 57 | }; 58 | 59 | 60 | /** 61 | * Type of the question. This is used to distinguish questions in serialized 62 | * format. 63 | * @type {string} 64 | * @const 65 | */ 66 | vsaq.questionnaire.items.SpacerItem.TYPE = 'spacer'; 67 | 68 | 69 | /** 70 | * Parses SpacerItems. If the topmost item in the passed Array is a 71 | * SpacerItem, it is consumed and a SpacerItem instance is returned. 72 | * If the topmost item is not a SpacerItem, an exception is thrown. 73 | * @param {!Array.} questionStack Array of serialized 74 | * questionnaire Items. 75 | * @return {!vsaq.questionnaire.items.SpacerItem} The parsed SpacerItem. 76 | */ 77 | vsaq.questionnaire.items.SpacerItem.parse = function(questionStack) { 78 | var item = questionStack.shift(); 79 | if (item.type != vsaq.questionnaire.items.SpacerItem.TYPE) 80 | throw new vsaq.questionnaire.items.ParseError('Wrong parser chosen.'); 81 | 82 | return new vsaq.questionnaire.items.SpacerItem(item.id, item.cond); 83 | }; 84 | -------------------------------------------------------------------------------- /vsaq/static/questionnaire/yesnoitem_test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright 2016 Google Inc. All rights reserved. 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @fileoverview Tests for vsaq.questionnaire.items.YesNoItem. 19 | */ 20 | 21 | goog.provide('vsaq.questionnaire.items.YesNoItemTests'); 22 | goog.setTestOnly('vsaq.questionnaire.items.YesNoItemTests'); 23 | 24 | goog.require('goog.dom'); 25 | goog.require('goog.dom.TagName'); 26 | goog.require('goog.dom.classlist'); 27 | goog.require('goog.testing.asserts'); 28 | goog.require('goog.testing.jsunit'); 29 | goog.require('vsaq.questionnaire.items.YesNoItem'); 30 | 31 | var CAPTION = 'yesnoitem_caption'; 32 | var ID = 'yesnoitem_id'; 33 | var VALUE = 'yes'; 34 | var YES = 'yes_caption'; 35 | var NO = 'no_caption'; 36 | 37 | var yesno; 38 | 39 | 40 | /** 41 | * Initializes variables used by all tests. 42 | */ 43 | function setUp() { 44 | yesno = new vsaq.questionnaire.items.YesNoItem(ID, [], CAPTION, YES, NO); 45 | } 46 | 47 | 48 | /** 49 | * Tests whether yesno items are rendered correctly. 50 | */ 51 | function testYesNoItem() { 52 | var el = yesno.container; 53 | 54 | assertEquals(el.tagName, goog.dom.TagName.DIV); 55 | assertTrue(goog.dom.classlist.contains(el, 'vsaq-yesno-block')); 56 | 57 | var desc = goog.dom.getFirstElementChild(el); 58 | assertEquals(goog.dom.TagName.DIV, desc.tagName); 59 | assertTrue(goog.dom.classlist.contains(desc, 'vsaq-question-title')); 60 | assertEquals(CAPTION, goog.dom.getTextContent(desc)); 61 | 62 | var label = yesno.yesRadio_.parentNode.getElementsByTagName('span')[0]; 63 | assertContains(YES, goog.dom.getTextContent(label)); 64 | assertFalse(yesno.yesRadio_.checked); 65 | 66 | label = yesno.noRadio_.parentNode.getElementsByTagName('span')[0]; 67 | assertContains(NO, goog.dom.getTextContent(label)); 68 | assertFalse(yesno.noRadio_.checked); 69 | } 70 | 71 | 72 | /** 73 | * Tests setting and retrieving the value of the item. 74 | */ 75 | function testYesNoItemSetGetValue() { 76 | assertEquals('', yesno.getValue()); 77 | yesno.setValue(VALUE); 78 | assertEquals(VALUE, yesno.getValue()); 79 | assertTrue(yesno.yesRadio_.checked); 80 | assertFalse(yesno.noRadio_.checked); 81 | assertTrue(yesno.isChecked()); 82 | } 83 | 84 | 85 | /** 86 | * Tests parsing of YesNoItems. 87 | */ 88 | function testYesNoItemParse() { 89 | var testStack = [{ 90 | 'type': 'yesno', 91 | 'text': CAPTION, 92 | 'id': ID, 93 | 'yes': YES, 94 | 'no': NO 95 | }]; 96 | yesno = vsaq.questionnaire.items.YesNoItem.parse(testStack); 97 | assert(yesno instanceof vsaq.questionnaire.items.YesNoItem); 98 | assertEquals(ID, yesno.id); 99 | assertEquals(CAPTION, yesno.text); 100 | assertEquals(YES, yesno.yes); 101 | assertEquals(NO, yesno.no); 102 | assertEquals(0, testStack.length); 103 | 104 | assert(yesno.yesRadio_ instanceof HTMLInputElement); 105 | assert(yesno.noRadio_ instanceof HTMLInputElement); 106 | } 107 | 108 | -------------------------------------------------------------------------------- /vsaq/static/questionnaire/infoitem.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright 2016 Google Inc. All rights reserved. 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @fileoverview Questionnaire informational items. 19 | */ 20 | 21 | goog.provide('vsaq.questionnaire.items.InfoItem'); 22 | 23 | goog.require('goog.dom'); 24 | goog.require('goog.soy'); 25 | goog.require('goog.string'); 26 | goog.require('vsaq.questionnaire.items.Item'); 27 | goog.require('vsaq.questionnaire.items.ParseError'); 28 | goog.require('vsaq.questionnaire.templates'); 29 | 30 | 31 | 32 | /** 33 | * An item that shows some information to the user. The information may contain 34 | * HTML. 35 | * @param {?string} id An ID uniquely identifying the item. 36 | * @param {?string} conditions A string containing conditions which must be met 37 | * for the item to be visible to the user. 38 | * @param {?string} info The information that should be shown to the user. 39 | * @extends {vsaq.questionnaire.items.Item} 40 | * @constructor 41 | */ 42 | vsaq.questionnaire.items.InfoItem = function(id, conditions, info) { 43 | goog.base(this, id, conditions); 44 | 45 | /** 46 | * The information that should be shown to the user. 47 | * @type {string} 48 | */ 49 | this.info = goog.string.makeSafe(info); 50 | var propertyInformation = { 51 | nameInClass: 'info', 52 | mandatory: true 53 | }; 54 | this.addPropertyInformation('text', propertyInformation); 55 | 56 | this.render(); 57 | }; 58 | goog.inherits(vsaq.questionnaire.items.InfoItem, 59 | vsaq.questionnaire.items.Item); 60 | 61 | 62 | /** 63 | * Render the HTML for this item. 64 | */ 65 | vsaq.questionnaire.items.InfoItem.prototype.render = function() { 66 | var oldNode = this.container; 67 | this.container = goog.soy.renderAsElement(vsaq.questionnaire.templates.info, 68 | { 69 | id: this.id, 70 | captionHtml: soydata.VERY_UNSAFE.ordainSanitizedHtml(this.info) 71 | }); 72 | goog.dom.replaceNode(this.container, oldNode); 73 | }; 74 | 75 | 76 | /** 77 | * Type of the question. This is used to distinguish questions in serialized 78 | * format. 79 | * @type {string} 80 | * @const 81 | */ 82 | vsaq.questionnaire.items.InfoItem.TYPE = 'info'; 83 | 84 | 85 | /** 86 | * Parses InfoItems. If the topmost item in the passed Array is a 87 | * InfoItem, it is consumed and a InfoItem instance is returned. 88 | * If the topmost item is not a CheckItem, an exception is thrown. 89 | * @param {!Array.} questionStack Array of serialized 90 | * questionnaire Items. 91 | * @return {!vsaq.questionnaire.items.InfoItem} The parsed InfoItem. 92 | */ 93 | vsaq.questionnaire.items.InfoItem.parse = function(questionStack) { 94 | var item = questionStack.shift(); 95 | if (item.type != vsaq.questionnaire.items.InfoItem.TYPE) 96 | throw new vsaq.questionnaire.items.ParseError('Wrong parser chosen.'); 97 | 98 | return new vsaq.questionnaire.items.InfoItem(item.id, item.cond, item.text); 99 | }; 100 | -------------------------------------------------------------------------------- /client_side_only_impl/index.html: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | VSAQ - Security Assessment Questionnaires 22 | 23 | 24 | 25 | 26 | 34 | 35 | 41 | 42 |
43 |
44 |
45 |
46 |
47 |

It works!

48 |

49 | Congratulations, you have successfully launched your own instance 50 | of VSAQ! 51 |

52 |

53 | At Google, questionnaires like the ones below are used to 54 | assess the security programs of third parties. The templates 55 | provided can be used for a variety of purposes, including doing a 56 | self-assessment of your own security program, or simply becoming 57 | familiar with issues affecting the security of web applications. 58 |

59 |

The questionnaires cover the following topics:

60 | 66 |

If you have feedback or questions, or you stumbled upon a bug in 67 | the application, reach out to us on Github.

69 |
70 |
71 |
72 |
73 | 74 |
75 |
76 | 77 | 78 | -------------------------------------------------------------------------------- /client_side_only_impl/vsaq.html: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | Vendor Security Assessment Questionnaire 22 | 23 | 24 | 25 | 26 | 34 | 35 | 41 | 42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 | Load answers from file:
53 |
54 | 55 |
56 | 57 | 58 |
59 |
60 |
61 |

62 | Please reload this page with a qpath parameter.
63 | E.g.: qpath=questionnaires/ven_template.json
64 | Link: Example
65 |

66 |
67 |
68 |

69 | 70 | Status: No changes 71 | 72 |
73 | Download Answers 74 | Reset Questionnaire 75 |

76 |
77 |
78 |
79 |
80 |
81 | 82 | 83 |
84 |
85 | 86 | 87 | -------------------------------------------------------------------------------- /vsaq/static/questionnaire/radioitem_test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright 2016 Google Inc. All rights reserved. 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @fileoverview Tests for vsaq.questionnaire.items.RadioItem. 19 | */ 20 | 21 | goog.provide('vsaq.questionnaire.items.RadioItemTests'); 22 | goog.setTestOnly('vsaq.questionnaire.items.RadioItemTests'); 23 | 24 | goog.require('goog.dom'); 25 | goog.require('goog.dom.TagName'); 26 | goog.require('goog.events'); 27 | goog.require('goog.events.EventType'); 28 | goog.require('goog.testing.asserts'); 29 | goog.require('goog.testing.events'); 30 | goog.require('goog.testing.events.Event'); 31 | goog.require('goog.testing.jsunit'); 32 | goog.require('vsaq.questionnaire.items.BlockItem'); 33 | goog.require('vsaq.questionnaire.items.Item'); 34 | goog.require('vsaq.questionnaire.items.RadioItem'); 35 | 36 | var CAPTION = 'radioitem_caption'; 37 | var ID = 'radioitem_id'; 38 | var ID2 = 'radioitem_id2'; 39 | var VALUE = 'checked'; 40 | 41 | var radio, radio2; 42 | 43 | 44 | /** 45 | * Initializes variables used by all tests. 46 | */ 47 | function setUp() { 48 | var block = new vsaq.questionnaire.items.BlockItem('block_id', [], ''); 49 | radio = new vsaq.questionnaire.items.RadioItem(ID, [], CAPTION); 50 | radio2 = new vsaq.questionnaire.items.RadioItem(ID2, [], CAPTION); 51 | block.addItem(radio); 52 | block.addItem(radio2); 53 | } 54 | 55 | 56 | /** 57 | * Tests whether radio items are rendered correctly. 58 | */ 59 | function testRadioItem() { 60 | var el = radio.container; 61 | 62 | assertEquals(goog.dom.TagName.DIV, el.tagName); 63 | assertContains('vsaq-radio-item', el.className); 64 | assert(radio.radioButton instanceof HTMLInputElement); 65 | assertContains(CAPTION, goog.dom.getTextContent(el.firstChild)); 66 | assertFalse(radio.radioButton.checked); 67 | assertFalse(radio.isChecked()); 68 | } 69 | 70 | 71 | /** 72 | * Tests setting and retrieving the value of the item. 73 | */ 74 | function testRadioItemSetGetValue() { 75 | assertEquals('', radio.getValue()); 76 | radio.setValue(VALUE); 77 | assertEquals(VALUE, radio.getValue()); 78 | } 79 | 80 | 81 | /** 82 | * Tests parsing of RadioItems. 83 | */ 84 | function testRadioItemParse() { 85 | var testStack = [{ 86 | 'type': 'radio', 87 | 'text': CAPTION, 88 | 'id': ID 89 | }]; 90 | radio = vsaq.questionnaire.items.RadioItem.parse(testStack); 91 | assert(radio instanceof vsaq.questionnaire.items.RadioItem); 92 | assertEquals(ID, radio.id); 93 | assertEquals(CAPTION, radio.text); 94 | assertEquals(0, testStack.length); 95 | } 96 | 97 | 98 | /** 99 | * Tests that no two radio items can be selected at the same time. 100 | */ 101 | function testRadioItemChanges() { 102 | radio.setValue(true); 103 | assertEquals(VALUE, radio.getValue()); 104 | assertEquals('', radio2.getValue()); 105 | var e = new goog.testing.events.Event(goog.events.EventType.CHANGE, 106 | radio.radioButton); 107 | var eventSent = false; 108 | goog.events.listen(radio.eventDispatcher, 109 | [vsaq.questionnaire.items.Item.CHANGED], 110 | function(e) { 111 | eventSent = true; 112 | assertEquals(VALUE, e.changes[ID]); 113 | assertEquals('', e.changes[ID2]); 114 | }); 115 | goog.testing.events.fireBrowserEvent(e); 116 | assertEquals(true, eventSent); 117 | } 118 | 119 | -------------------------------------------------------------------------------- /vsaq/static/questionnaire/blockitems_test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright 2016 Google Inc. All rights reserved. 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @fileoverview Tests for vsaq.questionnaire.items.BlockItem. 19 | */ 20 | 21 | goog.provide('vsaq.questionnaire.items.BlockItemsTests'); 22 | goog.setTestOnly('vsaq.questionnaire.items.BlockItemsTests'); 23 | 24 | goog.require('goog.dom'); 25 | goog.require('goog.dom.TagName'); 26 | goog.require('goog.dom.classlist'); 27 | goog.require('goog.testing.MockControl'); 28 | goog.require('goog.testing.events'); 29 | goog.require('goog.testing.jsunit'); 30 | goog.require('goog.testing.mockmatchers.ObjectEquals'); 31 | goog.require('vsaq.questionnaire.items.BlockItem'); 32 | goog.require('vsaq.questionnaire.items.Item'); 33 | goog.require('vsaq.questionnaire.items.RadioItem'); 34 | 35 | var CAPTION = 'block_caption'; 36 | var ID = 'test_id'; 37 | var ADMIN = 'admin'; 38 | 39 | var block; 40 | 41 | 42 | /** 43 | * Initializes variables used by all tests. 44 | */ 45 | function setUp() { 46 | block = new vsaq.questionnaire.items.BlockItem( 47 | ID, [], CAPTION, ADMIN); 48 | } 49 | 50 | 51 | /** 52 | * Tests whether blocks are rendered correctly. 53 | */ 54 | function testBlockItem() { 55 | var el = block.container; 56 | var legend = goog.dom.getFirstElementChild(el); 57 | 58 | assertEquals(goog.dom.TagName.FIELDSET, el.tagName); 59 | assert(goog.dom.classlist.contains(el, 'vsaq-block')); 60 | assertEquals(goog.dom.TagName.LEGEND, legend.tagName); 61 | 62 | var span = goog.dom.getFirstElementChild(legend); 63 | assertEquals(goog.dom.TagName.SPAN, span.tagName); 64 | assertEquals(CAPTION, span.innerHTML); 65 | } 66 | 67 | 68 | /** 69 | * Tests changes to radio buttons within the block. 70 | */ 71 | function testBlockRadioChanges() { 72 | var rad1 = new vsaq.questionnaire.items.RadioItem('radio1_id', [], 'radio1'); 73 | var rad2 = new vsaq.questionnaire.items.RadioItem('radio2_id', [], 'radio2'); 74 | 75 | block.addItem(rad1); 76 | block.addItem(rad2); 77 | 78 | var expectedEvent = { 79 | type: vsaq.questionnaire.items.Item.CHANGED, 80 | source: block, 81 | changes: {'radio1_id': rad1.getValue(), 'radio2_id': ''} 82 | }; 83 | 84 | var mock = new goog.testing.MockControl(); 85 | mock.createFunctionMock(block.eventDispatcher, 'dispatchEvent'); 86 | 87 | block.eventDispatcher.dispatchEvent( 88 | new goog.testing.mockmatchers.ObjectEquals(expectedEvent)); 89 | 90 | mock.$replayAll(); 91 | 92 | // Change the selection. 93 | goog.testing.events.fireClickEvent(rad1.radioButton); 94 | 95 | mock.$verifyAll(); 96 | mock.$tearDown(); 97 | } 98 | 99 | 100 | /** 101 | * Tests adding of containerItems to blocks. 102 | */ 103 | function testBlockAddItem() { 104 | var rad = new vsaq.questionnaire.items.RadioItem('radio1_id', [], 'radio1'); 105 | block.addItem(rad); 106 | assertContains('Item not added.', rad, block.containerItems); 107 | assertEquals(rad.container, block.container.lastChild); 108 | } 109 | 110 | 111 | /** 112 | * Tests parsing of BlockItems. 113 | */ 114 | function testStartBlockParse() { 115 | var testStack = [{ 116 | 'type': 'block', 117 | 'text': CAPTION, 118 | 'id': ID, 119 | 'auth' : ADMIN 120 | }]; 121 | 122 | block = vsaq.questionnaire.items.BlockItem.parse(testStack); 123 | 124 | assert(block instanceof vsaq.questionnaire.items.BlockItem); 125 | assertEquals(ID, block.id); 126 | assertEquals(CAPTION, block.text); 127 | assertEquals(ADMIN, block.auth); 128 | assertEquals(0, testStack.length); 129 | } 130 | -------------------------------------------------------------------------------- /download-libs.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Copyright 2016 Google Inc. All rights reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | # @fileoverview Shell script to download VSAQ build dependencies 17 | # 18 | 19 | export JAVA_TOOL_OPTIONS=-Dfile.encoding=UTF8 20 | THIRD_PARTY_DIRECTORY="third_party" 21 | 22 | type ant >/dev/null 2>&1 || { 23 | echo >&2 "Ant is required to build VSAQ dependencies." 24 | exit 1 25 | } 26 | type javac >/dev/null 2>&1 || { 27 | echo >&2 "Java compiler is required to build VSAQ dependencies." 28 | exit 1 29 | } 30 | type git >/dev/null 2>&1 || { 31 | echo >&2 "Git is required to build VSAQ dependencies." 32 | exit 1 33 | } 34 | type curl >/dev/null 2>&1 || { 35 | echo >&2 "Curl is required to build VSAQ dependencies." 36 | exit 1 37 | } 38 | type unzip >/dev/null 2>&1 || { 39 | echo >&2 "Unzip is required to build VSAQ dependencies." 40 | exit 1 41 | } 42 | jversion=$(java -version 2>&1 | grep version | awk -F '"' '{print $2}') 43 | if [[ $jversion < "1.7" ]]; then 44 | echo "Java 1.7 or higher is required to build VSAQ." 45 | exit 1 46 | fi 47 | 48 | # if repo was downloaded as zip from github, init git and clear submodules 49 | if [ ! -d .git ]; then 50 | git init 51 | rm -rf $THIRD_PARTY_DIRECTORY/closure-compiler 52 | rm -rf $THIRD_PARTY_DIRECTORY/closure-library 53 | rm -rf $THIRD_PARTY_DIRECTORY/closure-stylesheets 54 | rm -rf $THIRD_PARTY_DIRECTORY/js-dossier 55 | fi 56 | 57 | if [ ! -d $THIRD_PARTY_DIRECTORY ]; then 58 | mkdir $THIRD_PARTY_DIRECTORY 59 | fi 60 | cd $THIRD_PARTY_DIRECTORY 61 | 62 | git submodule add -f https://github.com/google/closure-compiler closure-compiler 63 | git submodule add -f https://github.com/google/closure-library closure-library 64 | git submodule add -f https://github.com/google/closure-stylesheets closure-stylesheets 65 | git submodule add -f https://github.com/jleyba/js-dossier js-dossier 66 | 67 | git submodule init 68 | git submodule update 69 | 70 | # Pin submodules to particular commits 71 | cd closure-compiler 72 | git checkout -b 59b42c9fc8fc752b3ff3aabe04ad89a96f9a7bf7 59b42c9fc8fc752b3ff3aabe04ad89a96f9a7bf7 73 | cd .. 74 | cd closure-library 75 | git checkout -b dc369cde87d7ef6dfb46d3b873f872ebee7d07cd dc369cde87d7ef6dfb46d3b873f872ebee7d07cd 76 | cd .. 77 | cd js-dossier 78 | git checkout -b 6f2d09ee26925b7417f9f6bd1547dffe700ab60f 6f2d09ee26925b7417f9f6bd1547dffe700ab60f 79 | cd .. 80 | 81 | # build closure compiler 82 | if [ ! -f closure-compiler/build/compiler.jar ] && [ -d closure-compiler ]; then 83 | cd closure-compiler 84 | ant clean 85 | ant jar 86 | cd .. 87 | fi 88 | 89 | # checkout closure templates compiler 90 | if [ ! -d closure-templates-compiler ]; then 91 | curl https://dl.google.com/closure-templates/closure-templates-for-javascript-latest.zip -O 92 | unzip closure-templates-for-javascript-latest.zip -d closure-templates-compiler 93 | rm closure-templates-for-javascript-latest.zip 94 | fi 95 | 96 | # build css compiler 97 | if [ ! -f closure-stylesheets/build/closure-stylesheets.jar ]; then 98 | cd closure-stylesheets 99 | ant 100 | cd .. 101 | fi 102 | 103 | if [ -f chrome_extensions.js ]; then 104 | rm -f chrome_extensions.js 105 | fi 106 | 107 | # Temporary fix 108 | # Soy file bundled with the compiler does not compile with strict settings: 109 | # lib/closure-templates-compiler/soyutils_usegoog.js:1762: ERROR - element JS_STR_CHARS does not exist on this enum 110 | cd closure-templates-compiler 111 | echo $PWD 112 | curl https://raw.githubusercontent.com/google/closure-templates/0cbc8543c34d3f7727dd83a2d1938672f16d5c20/javascript/soyutils_usegoog.js -O 113 | cd .. 114 | 115 | cd .. 116 | 117 | -------------------------------------------------------------------------------- /vsaq_server.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Google Inc. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """Runs a test server for VSAQ.""" 16 | 17 | 18 | import BaseHTTPServer 19 | import cgi 20 | import fnmatch 21 | import os 22 | import os.path 23 | import re 24 | import SimpleHTTPServer 25 | import sys 26 | 27 | PORT = 9000 28 | if len(sys.argv) > 1: 29 | PORT = int(sys.argv[1]) 30 | 31 | server_address = ("127.0.0.1", PORT) 32 | 33 | #./ do.sh testserver generates the file 34 | DEPS_FILE = "build/deps-runfiles.js" 35 | ALL_JSTESTS_FILE = "build/all_tests.js" 36 | 37 | 38 | class TestServerRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): 39 | """Request handler for VSAQ test server.""" 40 | 41 | DIRECTORY_MAP = { 42 | "/": "build/", 43 | "/vsaq/": "vsaq/", 44 | "/vsaq/static/questionnaire/": "vsaq/static/questionnaire/", 45 | "/javascript/closure/": "third_party/closure-library/closure/goog/", 46 | "/javascript/vsaq/": "vsaq/", 47 | "/third_party/closure/": 48 | "third_party/closure-library/third_party/closure/", 49 | "/third_party/closure-templates-compiler/": 50 | "third_party/closure-templates-compiler/", 51 | "/build/templates/vsaq/static/questionnaire/": 52 | "build/templates/vsaq/static/questionnaire/", 53 | 54 | } 55 | 56 | def get_test_files(self): 57 | test_files = [] 58 | for root, _, files in os.walk("vsaq/"): 59 | for f in fnmatch.filter(files, "*test_dom.html"): 60 | test_files.append(os.path.join(root, f)) 61 | return test_files 62 | 63 | def generate_all_tests_file(self): 64 | if os.path.exists(ALL_JSTESTS_FILE): 65 | return 66 | with open(ALL_JSTESTS_FILE, "wb") as f: 67 | f.write("var _allTests=") 68 | f.write(repr(self.get_test_files())) 69 | f.write(";") 70 | 71 | def show_tests(self): 72 | """Lists only vsaq/**/_test.html files.""" 73 | self.send_response(200) 74 | self.send_header("Content-type", "text/html") 75 | self.end_headers() 76 | test_files = self.get_test_files() 77 | 78 | self.wfile.write("

VSAQ test server

") 79 | self.wfile.write("

Test suite

") 80 | self.wfile.write("%s\n" % ("/all_tests.html", 81 | "all_tests.html")) 82 | self.wfile.write("

Individual tests

") 83 | self.wfile.write("
    ") 84 | for f in test_files: 85 | self.wfile.write("
  • %s\n" % (f, cgi.escape(f))) 86 | self.wfile.write("
") 87 | return 88 | 89 | def do_GET(self): 90 | print self.path 91 | if self.path == "/tests.html" or self.path == "/tests.html/": 92 | self.show_tests() 93 | else: 94 | SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self) 95 | 96 | def translate_path(self, path): 97 | """Serves files from different directories.""" 98 | # Remove all parameters from filenames. 99 | path = re.sub(r"\?.*$", "", path) 100 | 101 | if path.endswith("deps-runfiles.js"): 102 | return DEPS_FILE 103 | if path == "/" + ALL_JSTESTS_FILE: 104 | self.generate_all_tests_file() 105 | return ALL_JSTESTS_FILE 106 | for prefix, dest_dir in TestServerRequestHandler.DIRECTORY_MAP.items(): 107 | print "checking: " + dest_dir + path[len(prefix):] 108 | if path.startswith(prefix) and os.path.isfile( 109 | dest_dir + path[len(prefix):]): 110 | return dest_dir + path[len(prefix):] 111 | return "build/index.html" 112 | 113 | 114 | httpd = BaseHTTPServer.HTTPServer(server_address, TestServerRequestHandler) 115 | print "Starting the VSAQ server at http://%s:%d" % server_address 116 | httpd.serve_forever() 117 | -------------------------------------------------------------------------------- /client_side_only_impl/vsaq_editor.html: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | Questionnaire Editor (alpha) 22 | 23 | 24 | 25 | 26 | 34 | 35 | 41 | 42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 | 54 | 55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 | Edit Options 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 |
Editor
Light-Editor
Unrolled (don't check conditions)
Live edit mode
84 | Template 85 | (→) Export template 86 | Parse template (←)
87 | 99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 | 107 | 108 |
109 |
110 | 111 | 112 | 113 | 114 | -------------------------------------------------------------------------------- /vsaq/static/questionnaire/groupitem_test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright 2016 Google Inc. All rights reserved. 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @fileoverview Tests for vsaq.questionnaire.items.GroupItem. 19 | */ 20 | 21 | goog.provide('vsaq.questionnaire.items.GroupItemTests'); 22 | goog.setTestOnly('vsaq.questionnaire.items.GroupItemTests'); 23 | 24 | goog.require('goog.array'); 25 | goog.require('goog.testing.asserts'); 26 | goog.require('goog.testing.jsunit'); 27 | goog.require('vsaq.questionnaire.items.CheckItem'); 28 | goog.require('vsaq.questionnaire.items.CheckgroupItem'); 29 | goog.require('vsaq.questionnaire.items.GroupItem'); 30 | goog.require('vsaq.questionnaire.items.RadioItem'); 31 | goog.require('vsaq.questionnaire.items.RadiogroupItem'); 32 | 33 | 34 | var TEST_CHECKGROUP = { 35 | 'type': 'checkgroup', 36 | 'defaultChoice': true, 37 | 'text': 'caption', 38 | 'choices': [ 39 | {'choice_id_1': 'Text 1'}, 40 | {'choice_id_2': 'Text 2'} 41 | ], 42 | 'choicesConds': [] 43 | }; 44 | 45 | var TEST_RADIOGROUP = { 46 | 'type': 'radiogroup', 47 | 'text': 'caption', 48 | 'defaultChoice': false, 49 | 'choices': [ 50 | {'choice_id_1': 'Text 1'}, 51 | {'choice_id_2': 'Text 2'} 52 | ], 53 | 'choicesConds': [] 54 | }; 55 | 56 | var checkgroupItem, radiogroupItem; 57 | 58 | 59 | /** 60 | * Initializes variables used by all tests. 61 | */ 62 | function setUp() { 63 | checkgroupItem = new vsaq.questionnaire.items.CheckgroupItem( 64 | null, null, TEST_CHECKGROUP.text, TEST_CHECKGROUP.defaultChoice, 65 | TEST_CHECKGROUP.choices, TEST_CHECKGROUP.choicesConds); 66 | 67 | radiogroupItem = new vsaq.questionnaire.items.RadiogroupItem( 68 | null, null, TEST_RADIOGROUP.text, TEST_RADIOGROUP.defaultChoice, 69 | TEST_RADIOGROUP.choices, TEST_RADIOGROUP.choicesConds); 70 | } 71 | 72 | 73 | /** 74 | * Tests whether all possible GroupItems are initialized correctly. 75 | */ 76 | 77 | function testGroupItem() { 78 | 79 | var defaultChoiceFound; 80 | 81 | // First verify the correctness of check groups. 82 | defaultChoiceFound = false; 83 | 84 | var expectedNumberChoices = TEST_CHECKGROUP.choices.length; 85 | 86 | if (TEST_CHECKGROUP['defaultChoice']) 87 | expectedNumberChoices++; 88 | assert(expectedNumberChoices == checkgroupItem.containerItems.length); 89 | 90 | goog.array.forEach(checkgroupItem.containerItems, function(item) { 91 | assert(item instanceof vsaq.questionnaire.items.CheckItem); 92 | // All choices are supposed to have the containerItem as a parent. 93 | assertNotUndefined(item.parentItem); 94 | 95 | if (item.text == vsaq.questionnaire.items.GroupItem.DEFAULT_CHOICE) 96 | defaultChoiceFound = true; 97 | }); 98 | assert(defaultChoiceFound == TEST_CHECKGROUP['defaultChoice']); 99 | 100 | // Now verify the correctness of radio groups. 101 | defaultChoiceFound = false; 102 | expectedNumberChoices = TEST_RADIOGROUP.choices.length; 103 | if (TEST_RADIOGROUP['defaultChoice']) 104 | expectedNumberChoices++; 105 | assert(expectedNumberChoices == radiogroupItem.containerItems.length); 106 | 107 | goog.array.forEach(radiogroupItem.containerItems, function(item) { 108 | assert(item instanceof vsaq.questionnaire.items.RadioItem); 109 | // All choices are supposed to have the containerItem as a parent. 110 | assertNotUndefined(item.parentItem); 111 | 112 | if (item.text == vsaq.questionnaire.items.GroupItem.DEFAULT_CHOICE) 113 | defaultChoiceFound = true; 114 | }); 115 | assert(defaultChoiceFound == TEST_RADIOGROUP['defaultChoice']); 116 | } 117 | 118 | 119 | /** 120 | * Tests parsing of all possible GroupItems. 121 | */ 122 | function testGroupItemParse() { 123 | 124 | var testStack = [TEST_CHECKGROUP]; 125 | checkgroupItem = vsaq.questionnaire.items.CheckgroupItem.parse(testStack); 126 | assert(checkgroupItem instanceof vsaq.questionnaire.items.CheckgroupItem); 127 | 128 | testStack = [TEST_RADIOGROUP]; 129 | radiogroupItem = vsaq.questionnaire.items.RadiogroupItem.parse(testStack); 130 | assert(radiogroupItem instanceof vsaq.questionnaire.items.RadiogroupItem); 131 | 132 | // Verify all items once again 133 | testGroupItem(); 134 | } 135 | -------------------------------------------------------------------------------- /vsaq/static/questionnaire/tipitem_test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright 2016 Google Inc. All rights reserved. 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @fileoverview Tests for vsaq.questionnaire.items.TipItem. 19 | */ 20 | 21 | goog.provide('vsaq.questionnaire.items.TipItemTests'); 22 | goog.setTestOnly('vsaq.questionnaire.items.TipItemTests'); 23 | 24 | goog.require('goog.dom'); 25 | goog.require('goog.dom.TagName'); 26 | goog.require('goog.testing.asserts'); 27 | goog.require('goog.testing.jsunit'); 28 | goog.require('vsaq.questionnaire.items.TipItem'); 29 | 30 | var CAPTION = 'tipitem_caption'; 31 | var TIP_ID = 'tipitem_id'; 32 | var WARN_ID = 'warntipitem_id'; 33 | var WHY_ID = 'whytipitem_id'; 34 | var NAME_ID = 'namedtipitem_id'; 35 | var TODO_ID = 'todotipitem_id'; 36 | var CUSTOMTITLE_ID = 'customtitletipitem_id'; 37 | var WHY_TEXT = 'whytipitem_text'; 38 | var VALUE = 'whytipitem_value'; 39 | var NAME = 'whytip_name'; 40 | var TODO = 'whytip_todo'; 41 | var CUSTOMTITLE_TEXT = 'customtitle_text'; 42 | 43 | var tip; 44 | var warn_tip; 45 | var why_tip; 46 | 47 | 48 | /** 49 | * Initializes variables used by all tests. 50 | */ 51 | function setUp() { 52 | tip = new vsaq.questionnaire.items.TipItem(TIP_ID, [], CAPTION, false); 53 | warn_tip = new vsaq.questionnaire.items.TipItem(WARN_ID, [], CAPTION, true); 54 | why_tip = new vsaq.questionnaire.items.TipItem( 55 | WHY_ID, [], CAPTION, true, 'medium', WHY_TEXT); 56 | name_tip = new vsaq.questionnaire.items.TipItem( 57 | NAME_ID, [], CAPTION, true, 'medium', null, NAME); 58 | todo_tip = new vsaq.questionnaire.items.TipItem( 59 | TODO_ID, [], CAPTION, true, 'medium', null, null, TODO); 60 | custom_title_tip = new vsaq.questionnaire.items.TipItem( 61 | CUSTOMTITLE_ID, [], CAPTION, true, 'medium', null, null, 62 | null, CUSTOMTITLE_TEXT); 63 | } 64 | 65 | 66 | /** 67 | * Tests whether tip items are rendered correctly. 68 | */ 69 | function testTipItem() { 70 | var el = tip.container; 71 | assertEquals(goog.dom.TagName.DIV, el.tagName); 72 | assertEquals(TIP_ID, tip.id); 73 | assertContains('Tip', goog.dom.getTextContent(el)); 74 | assertContains(CAPTION, goog.dom.getTextContent(el)); 75 | assertNotContains('Warning', goog.dom.getTextContent(el)); 76 | 77 | el = warn_tip.container; 78 | assertEquals(goog.dom.TagName.DIV, el.tagName); 79 | assertEquals(WARN_ID, warn_tip.id); 80 | assertNotContains('Tip', goog.dom.getTextContent(el)); 81 | assertContains(CAPTION, goog.dom.getTextContent(el)); 82 | assertContains('Warning', goog.dom.getTextContent(el)); 83 | assertContains('vsaq-bubble-medium', el.innerHTML); 84 | 85 | el = why_tip.container; 86 | assertEquals(goog.dom.TagName.DIV, el.tagName); 87 | assertEquals(WHY_ID, why_tip.id); 88 | assertNotContains('Tip', goog.dom.getTextContent(el)); 89 | assertContains(CAPTION, goog.dom.getTextContent(el)); 90 | assertContains('Warning', goog.dom.getTextContent(el)); 91 | assertContains('vsaq-bubble-medium', el.innerHTML); 92 | 93 | assertEquals(NAME_ID, name_tip.id); 94 | assertEquals(NAME, name_tip.name); 95 | 96 | assertEquals(TODO_ID, todo_tip.id); 97 | assertEquals(TODO, todo_tip.todo); 98 | 99 | assertEquals(CUSTOMTITLE_ID, custom_title_tip.id); 100 | assertEquals(CUSTOMTITLE_TEXT, custom_title_tip.customTitle); 101 | } 102 | 103 | 104 | /** 105 | * Tests setting and retrieving the value of the item. 106 | */ 107 | function testTipItemSetGetValue() { 108 | assertEquals('', why_tip.getValue()); 109 | why_tip.setValue(VALUE); 110 | assertEquals(VALUE, why_tip.getValue()); 111 | } 112 | 113 | 114 | /** 115 | * Tests parsing of TipItems. 116 | */ 117 | function testTipItemParse() { 118 | var testStack = [{ 119 | 'type': 'tip', 120 | 'text': CAPTION, 121 | 'id': TIP_ID, 122 | 'warn': 'yes', 123 | 'why': WHY_TEXT, 124 | 'todo': TODO, 125 | 'customTitle' : CUSTOMTITLE_TEXT, 126 | }]; 127 | tip = vsaq.questionnaire.items.TipItem.parse(testStack); 128 | assert(tip instanceof vsaq.questionnaire.items.TipItem); 129 | assertEquals(TIP_ID, tip.id); 130 | assertEquals(CAPTION, tip.text); 131 | assertEquals(true, tip.warn); 132 | assertEquals(WHY_TEXT, tip.clarification); 133 | assertEquals(0, testStack.length); 134 | assertEquals(TODO, tip.todo); 135 | assertEquals(CUSTOMTITLE_TEXT, tip.customTitle); 136 | } 137 | 138 | -------------------------------------------------------------------------------- /client_side_only_impl/all_tests.html: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | 19 | VSAQ - All JsUnit Tests 20 | 21 | 22 | 23 | 26 | 27 | 56 | 57 | 58 |

VSAQ - All JsUnit Tests

59 |
60 | 61 |
62 | Settings:
63 | 64 |
65 |
66 | 67 | (use regexp, e.g. "structs.*") 68 |
69 |

Tests:

70 |

 71 | 
154 | 
155 | 
156 | 


--------------------------------------------------------------------------------
/vsaq/static/questionnaire/checkitem.js:
--------------------------------------------------------------------------------
  1 | /**
  2 |  * @license
  3 |  * Copyright 2016 Google Inc. All rights reserved.
  4 |  * Licensed under the Apache License, Version 2.0 (the "License");
  5 |  * you may not use this file except in compliance with the License.
  6 |  * You may obtain a copy of the License at
  7 |  *
  8 |  *   http://www.apache.org/licenses/LICENSE-2.0
  9 |  *
 10 |  * Unless required by applicable law or agreed to in writing, software
 11 |  * distributed under the License is distributed on an "AS IS" BASIS,
 12 |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13 |  * See the License for the specific language governing permissions and
 14 |  * limitations under the License.
 15 |  */
 16 | 
 17 | /**
 18 |  * @fileoverview A questionnaire item with a checkbox.
 19 |  */
 20 | 
 21 | goog.provide('vsaq.questionnaire.items.CheckItem');
 22 | 
 23 | goog.require('goog.dom');
 24 | goog.require('goog.events');
 25 | goog.require('goog.events.EventType');
 26 | goog.require('goog.soy');
 27 | goog.require('vsaq.questionnaire.items.ParseError');
 28 | goog.require('vsaq.questionnaire.items.ValueItem');
 29 | goog.require('vsaq.questionnaire.templates');
 30 | goog.require('vsaq.questionnaire.utils');
 31 | 
 32 | 
 33 | 
 34 | /**
 35 |  * A question that allows the user to answer by ticking a checkbox.
 36 |  * @param {string} id An ID uniquely identifying the question.
 37 |  * @param {?string} conditions A string containing conditions which must be met
 38 |  *     for the item to be visible to the user.
 39 |  * @param {string} caption The caption to show next to the checkbox.
 40 |  * @extends {vsaq.questionnaire.items.ValueItem}
 41 |  * @constructor
 42 |  */
 43 | vsaq.questionnaire.items.CheckItem = function(id, conditions, caption) {
 44 |   goog.base(this, id, conditions, caption);
 45 | 
 46 |   /**
 47 |    * The checkbox that is the actual control behind this question.
 48 |    * @type {!HTMLInputElement}
 49 |    * @private
 50 |    */
 51 |   this.checkBox_;
 52 | 
 53 |   this.render();
 54 | };
 55 | goog.inherits(vsaq.questionnaire.items.CheckItem,
 56 |               vsaq.questionnaire.items.ValueItem);
 57 | 
 58 | 
 59 | /**
 60 |  * Render the HTML for this item.
 61 |  */
 62 | vsaq.questionnaire.items.CheckItem.prototype.render = function() {
 63 |   var oldNode = this.container;
 64 |   this.container = goog.soy.renderAsElement(vsaq.questionnaire.templates.check,
 65 |       {
 66 |         id: this.id,
 67 |         captionHtml: soydata.VERY_UNSAFE.ordainSanitizedHtml(this.text)
 68 |       });
 69 |   goog.dom.replaceNode(this.container, oldNode);
 70 | 
 71 |   this.checkBox_ = /** @type {!HTMLInputElement} */ (
 72 |       vsaq.questionnaire.utils.findById(this.container, this.id));
 73 |   goog.events.listen(this.checkBox_, goog.events.EventType.CHANGE,
 74 |       this.answerChanged, true, this);
 75 | };
 76 | 
 77 | 
 78 | /**
 79 |  * Constant indicating the string value used if the item is selected/checked.
 80 |  * @type {string}
 81 |  */
 82 | vsaq.questionnaire.items.CheckItem.CHECKED_VALUE = 'checked';
 83 | 
 84 | 
 85 | /**
 86 |  * Type of the question. This is used to distinguish questions in serialized
 87 |  * format.
 88 |  * @type {string}
 89 |  * @const
 90 |  */
 91 | vsaq.questionnaire.items.CheckItem.TYPE = 'check';
 92 | 
 93 | 
 94 | /**
 95 |  * Parses CheckItems. If the topmost item in the passed Array is an a
 96 |  * CheckItem, it is consumed and a CheckItem instance is returned.
 97 |  * If the topmost item is not a CheckItem, an exception is thrown.
 98 |  * @param {!Array.} questionStack Array of serialized
 99 |  *     questionnaire Items.
100 |  * @return {!vsaq.questionnaire.items.CheckItem} The parsed CheckItem.
101 |  */
102 | vsaq.questionnaire.items.CheckItem.parse = function(questionStack) {
103 |   var item = questionStack.shift();
104 |   if (item.type != vsaq.questionnaire.items.CheckItem.TYPE)
105 |     throw new vsaq.questionnaire.items.ParseError('Wrong parser chosen.');
106 | 
107 |   return new vsaq.questionnaire.items.CheckItem(item.id, item.cond, item.text);
108 | };
109 | 
110 | 
111 | /** @inheritDoc */
112 | vsaq.questionnaire.items.CheckItem.prototype.setReadOnly = function(readOnly) {
113 |   this.checkBox_.disabled = readOnly;
114 | };
115 | 
116 | 
117 | /** @inheritDoc */
118 | vsaq.questionnaire.items.CheckItem.prototype.isChecked = function(opt_value) {
119 |   return this.checkBox_.checked;
120 | };
121 | 
122 | 
123 | /** @inheritDoc */
124 | vsaq.questionnaire.items.CheckItem.prototype.getValue = function() {
125 |   return this.checkBox_.checked ?
126 |       vsaq.questionnaire.items.CheckItem.CHECKED_VALUE : '';
127 | };
128 | 
129 | 
130 | /** @inheritDoc */
131 | vsaq.questionnaire.items.CheckItem.prototype.setInternalValue =
132 |     function(value) {
133 |   if (goog.isBoolean(value)) {
134 |     this.checkBox_.checked = value;
135 |   } else if (goog.isString(value)) {
136 |     this.checkBox_.checked =
137 |         value == vsaq.questionnaire.items.CheckItem.CHECKED_VALUE;
138 |   }
139 | };
140 | 
141 | 
142 | /** @inheritDoc */
143 | vsaq.questionnaire.items.CheckItem.prototype.isAnswered = function() {
144 |   // Always returns true, since with checkboxes we can't know whether
145 |   // the checkbox is un-answered or intentionally left unchecked.
146 |   return true;
147 | };
148 | 
149 | 


--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
  1 | # VSAQ: Vendor Security Assessment Questionnaire
  2 | 
  3 | ## Introduction
  4 | ----
  5 | Note: VSAQ is not an official Google product (experimental or otherwise);
  6 | it's just code that happens to be owned by Google.
  7 | 
  8 | VSAQ is an interactive questionnaire application. Its initial purpose was
  9 | to support security reviews by facilitating not only the collection of
 10 | information, but also the redisplay of collected data in templated form.
 11 | 
 12 | At Google, questionnaires like the ones in this repository are used to
 13 | assess the security programs of third parties. But the templates provided
 14 | can be used for a variety of purposes, including doing a self-assessment
 15 | of your own security program, or simply becoming familiar with issues
 16 | affecting the security of web applications.
 17 | 
 18 | To test the application without deploying it, go to https://vsaq-demo.withgoogle.com.
 19 | 
 20 | 
 21 | ### Example Third-Party Security Review Workflow
 22 | 1. *Reviewer* sends questionnaire link(s) to *assessment target*
 23 | (e.g., https://vsaq-demo.withgoogle.com/vsaq.html?qpath=questionnaires/webapp.json).
 24 | 1. *Assessment target* completes the questionnaire(s).
 25 | 1. *Assessment target* clicks the Save button at the bottom of the questionnaire
 26 | to export the answers.
 27 | 1. *Assessment target* sends the answers file back to *reviewer*.
 28 | 1. *Reviewer* opens the same questionnaire(s) and loads the answers received
 29 | from *assessment target*.
 30 | 
 31 | 
 32 | ### Project Structure
 33 | 
 34 | * / — top-level directory for common files.
 35 | * /questionnaires — directory for questionnaire templates.
 36 | * /vsaq — directory for the questionnaire rendering engine library.
 37 | * /client_side_only_impl — directory for a client-side reference implementation.
 38 | 
 39 | 
 40 | ## Build Prerequisites
 41 | ----
 42 | These instructions have been tested with the following software:
 43 | 
 44 | * java >= 1.7 — for running the Closure Compiler
 45 | * ant — for building VSAQ dependencies
 46 | * git
 47 | * curl
 48 | * a web server (an optional Python development server is provided)
 49 | * a browser with HTML5 support
 50 | 
 51 | 
 52 | ## VSAQ Setup
 53 | ----
 54 | These instructions assume a working directory of the repository root.
 55 | 
 56 | VSAQ includes an easy-to-use setup script called `do.sh`. It supports the
 57 | following commands:
 58 | 
 59 |  * Setup:   `./do.sh {install_deps|check_deps}`
 60 |  * Build:   `./do.sh {build|build_prod|build_templates|build_docs} [debug]`
 61 |  * Run:     `./do.sh {run}`
 62 |  * Cleanup: `./do.sh {clean|clean_deps}`
 63 |  * Other:   `./do.sh {lint}`
 64 | 
 65 | 
 66 | ### Build
 67 | 
 68 | To build VSAQ, run the following commands:
 69 | 
 70 | 1. `./do.sh install_deps`
 71 | 1. `./do.sh build`
 72 | 
 73 | 
 74 | ### Local Development Server
 75 | To run the VSAQ development server locally, use the `run` command:
 76 | 
 77 | 1. `./do.sh run`
 78 | 
 79 | Note that the development app server uses a snapshot of the code, taken
 80 | at the time you run it. If you make changes to the code, be sure to run the
 81 | appropriate build command again and restart the dev server:
 82 | 
 83 |  * Run `./do.sh build`  to refresh the source code, static files, and templates.
 84 |  * Run `./do.sh build_templates` to rebuild only the Closure Templates. Then
 85 |  run `./do.sh run` to restart the dev server.
 86 | 
 87 | 
 88 | ### Deployment
 89 | The open source version of VSAQ does not require a dedicated back end. This means
 90 | VSAQ can be hosted as a static application on any web server.
 91 | 
 92 | To deploy VSAQ, complete the following steps:
 93 | 
 94 | 1. `./do.sh build_prod` — This will run a normal build, but will also remove
 95 | test files.
 96 | 1. Copy the content of the `build` directory into any directory hosted on your
 97 | web server.
 98 | 1. The questionnaire should now be available under
 99 | https://[yourserver]/vsaq.html?qpath=questionnaires/test_template.json
100 | 
101 | Example: https://vsaq-demo.withgoogle.com/vsaq.html?qpath=questionnaires/test_template.json
102 | 
103 | 
104 | ## Reference Implementation
105 | ----
106 | The reference implementation in the `client_side_only_impl` folder requires no
107 | code to run on a back end. All operations are performed by `vsaq_main.js` in the
108 | browser.
109 | 
110 | Although this makes deployment very easy, you may want to run a custom
111 | server-side component for storing answers and mapping questionnaires
112 | to users. `vsaq_main.js` provides example code for submitting and loading questionnaire
113 | answers to/from a back end:
114 | 
115 |  * `submitQuestionnaireToServer_` Submits questionnaire answers to a back end.
116 |  * `loadAnswersFromServer_` Loads questionnaire answers from a back end.
117 | 
118 | 
119 | ## Notes
120 | ----
121 | JS-Files in `static/` are compiled by the Closure Compiler and placed in
122 | `build/vsaq_binary.js`.
123 | 
124 | Closure Templates are compiled by the Closure Template Compiler
125 | and placed in `build/templates/vsaq/static/questionnaire/templates.soy.js`.
126 | 
127 | The `/questionnaires` directory and parts of the `/static` directories are
128 | replicated in `build/`.
129 | 
130 | Changes to the JSON `/questionnaires` do not require redeployment of the
131 | application code, and can be done on the server if required.
132 | 


--------------------------------------------------------------------------------
/vsaq/static/utils.js:
--------------------------------------------------------------------------------
  1 | /**
  2 |  * @license
  3 |  * Copyright 2016 Google Inc. All rights reserved.
  4 |  * Licensed under the Apache License, Version 2.0 (the "License");
  5 |  * you may not use this file except in compliance with the License.
  6 |  * You may obtain a copy of the License at
  7 |  *
  8 |  *   http://www.apache.org/licenses/LICENSE-2.0
  9 |  *
 10 |  * Unless required by applicable law or agreed to in writing, software
 11 |  * distributed under the License is distributed on an "AS IS" BASIS,
 12 |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13 |  * See the License for the specific language governing permissions and
 14 |  * limitations under the License.
 15 |  */
 16 | 
 17 | /**
 18 |  * @fileoverview Utility functions for vsaq.
 19 |  */
 20 | 
 21 | goog.provide('vsaq.utils');
 22 | 
 23 | goog.require('goog.array');
 24 | goog.require('goog.dom');
 25 | goog.require('goog.dom.classlist');
 26 | goog.require('goog.dom.forms');
 27 | goog.require('goog.events');
 28 | goog.require('goog.events.EventType');
 29 | goog.require('goog.object');
 30 | goog.require('goog.string');
 31 | goog.require('goog.string.format');
 32 | goog.require('goog.structs');
 33 | 
 34 | 
 35 | /**
 36 |  * Adds a listener for clicks to the given element (if it exists).
 37 |  * @param {string} elementId The id of the element that can be clicked.
 38 |  * @param {!Function} callback The function that shall be called when the
 39 |  *    element is clicked.
 40 |  * @return {boolean} Whether the element was found.
 41 |  */
 42 | vsaq.utils.addClickHandler = function(elementId, callback) {
 43 |   var element = goog.dom.getElement(elementId);
 44 |   if (element) {
 45 |     goog.events.listen(element, [goog.events.EventType.CLICK], callback);
 46 |     return true;
 47 |   }
 48 |   return false;
 49 | };
 50 | 
 51 | 
 52 | /**
 53 |  * Adds an onclick handler to the document, and dispatches clicks on clickable
 54 |  * elements (marked by having the a CSS class assigned) to the correct handler.
 55 |  * The called function is passed the clicked element, as well as the original
 56 |  * event.
 57 |  * @param {Object.} handlers An object mapping CSS class names
 58 |  *     to specific functions. If a click on the web page is registered, and one
 59 |  *     of the clicked element's ancestors has the CSS class specified in the
 60 |  *     attribute name assigned, the function passed as the value is called.
 61 |  */
 62 | vsaq.utils.initClickables = function(handlers) {
 63 |   goog.events.listen(goog.dom.getDocument(), [goog.events.EventType.CLICK],
 64 |       function(e) {
 65 |         goog.structs.forEach(handlers, function(handler, className) {
 66 |           var clickable = goog.dom.getAncestorByClass(e.target, className);
 67 |           if (clickable &&
 68 |               !goog.dom.classlist.contains(clickable, 'maia-button-disabled')) {
 69 |             handler(clickable, e);
 70 |           }
 71 |         });
 72 |       });
 73 | };
 74 | 
 75 | 
 76 | /**
 77 |  * Creates a request content out of a format string by URI encoding parameters.
 78 |  * @param {string} format Template string containing % specifiers.
 79 |  * @param {...(string|number)} var_args Values format is to be filled with.
 80 |  * @return {string} Formatted string with URI encoded parameters.
 81 |  */
 82 | vsaq.utils.createContent = function(format, var_args) {
 83 |   var params = goog.array.slice(arguments, 1);
 84 |   var encodedParams = goog.array.map(params, encodeURIComponent);
 85 |   goog.array.insertAt(encodedParams, format, 0);
 86 | 
 87 |   return goog.string.format.apply(null, encodedParams);
 88 | };
 89 | 
 90 | 
 91 | /**
 92 |  * Get the value of an element, or null if the element does not exist or
 93 |  * does not have a value.
 94 |  * @param {string} el Name of element.
 95 |  * @return {(string|undefined)} Value of the element.
 96 |  */
 97 | vsaq.utils.getValue = function(el) {
 98 |   var elt = goog.dom.getElement(el);
 99 |   if (!elt)
100 |     return undefined;
101 |   var value = goog.object.get(elt, 'value');
102 |   if (!value)
103 |     return undefined;
104 |   return value;
105 | };
106 | 
107 | 
108 | /**
109 |  * Ads a change handler to the vendor project selector in the main menu.
110 |  */
111 | vsaq.utils.initProjectSelector = function() {
112 |   var select = goog.dom.getElement('vsaq_vendor_project_selector');
113 |   if (select) {
114 |     goog.events.listen(select, [goog.events.EventType.CHANGE], function(e) {
115 |       var projectUrl = goog.dom.forms.getValue(e.target);
116 |       if (projectUrl) {
117 |         var currentSubPage = window.location.pathname.match('^/.*/');
118 |         document.location = currentSubPage + projectUrl;
119 |       }
120 |     });
121 |   }
122 | };
123 | 
124 | 
125 | /**
126 |  * Takes an argument an removes all VSAQON string concatenations, comments and
127 |  * some more escaped values.
128 |  * @param {string} vsaqon A string in VSAQON that contains non-JSON conform
129 |  *     elements.
130 |  * @return {string} A JSON conform string.
131 |  */
132 | vsaq.utils.vsaqonToJson = function(vsaqon) {
133 |   // Get rid of javascript-style string concatenations
134 |   var text = vsaqon.replace(/" \+[ ]*\r?\n[ ]*"/g, '');
135 |   // Replace all \x escaped values with \u00-style strings
136 |   text = text.replace(/\\x/g, '\\u00');
137 |   // Remove //-styled comments
138 |   text = text.replace(/\n[ ]*\/\/[^\r\n]*/g, '');
139 |   return text;
140 | };
141 | 


--------------------------------------------------------------------------------
/vsaq/static/questionnaire/boxitem.js:
--------------------------------------------------------------------------------
  1 | /**
  2 |  * @license
  3 |  * Copyright 2016 Google Inc. All rights reserved.
  4 |  * Licensed under the Apache License, Version 2.0 (the "License");
  5 |  * you may not use this file except in compliance with the License.
  6 |  * You may obtain a copy of the License at
  7 |  *
  8 |  *   http://www.apache.org/licenses/LICENSE-2.0
  9 |  *
 10 |  * Unless required by applicable law or agreed to in writing, software
 11 |  * distributed under the License is distributed on an "AS IS" BASIS,
 12 |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13 |  * See the License for the specific language governing permissions and
 14 |  * limitations under the License.
 15 |  */
 16 | 
 17 | /**
 18 |  * @fileoverview A questionnaire item with a textarea.
 19 |  */
 20 | 
 21 | goog.provide('vsaq.questionnaire.items.BoxItem');
 22 | 
 23 | goog.require('goog.dom');
 24 | goog.require('goog.events');
 25 | goog.require('goog.events.EventType');
 26 | goog.require('goog.soy');
 27 | goog.require('vsaq.questionnaire.items.ParseError');
 28 | goog.require('vsaq.questionnaire.items.ValueItem');
 29 | goog.require('vsaq.questionnaire.templates');
 30 | goog.require('vsaq.questionnaire.utils');
 31 | 
 32 | 
 33 | 
 34 | /**
 35 |  * A question that allows the user to answer in a textarea box.
 36 |  * @param {string} id An ID uniquely identifying the question.
 37 |  * @param {?string} conditions A string containing conditions which must be met
 38 |  *     for the item to be visible to the user.
 39 |  * @param {string} caption The caption to show above the textarea.
 40 |  * @param {string=} opt_placeholder The placeholder text, displayed in place of
 41 |  *     the value.
 42 |  * @param {string=} opt_inputPattern HTML5 pattern attribute value for the input
 43 |  *     field. See {@link
 44 |  *     https://html.spec.whatwg.org/multipage/forms.html#the-pattern-attribute}.
 45 |  * @param {string=} opt_inputTitle HTML5 title attribute value for the input
 46 |  *     field. See {@link
 47 |  *     https://html.spec.whatwg.org/multipage/forms.html#attr-input-title}.
 48 |  * @param {boolean=} opt_isRequired Iff true, the item value is required.
 49 |  * @extends {vsaq.questionnaire.items.ValueItem}
 50 |  * @constructor
 51 |  */
 52 | vsaq.questionnaire.items.BoxItem = function(id, conditions, caption,
 53 |     opt_placeholder, opt_inputPattern, opt_inputTitle, opt_isRequired) {
 54 |   goog.base(this, id, conditions, caption, opt_placeholder, opt_inputPattern,
 55 |       opt_inputTitle, opt_isRequired);
 56 | 
 57 |   /**
 58 |    * The text area where the user can provide an answer.
 59 |    * @type {!HTMLTextAreaElement}
 60 |    * @private
 61 |    */
 62 |   this.textArea_;
 63 | 
 64 |   this.render();
 65 | };
 66 | goog.inherits(vsaq.questionnaire.items.BoxItem,
 67 |               vsaq.questionnaire.items.ValueItem);
 68 | 
 69 | 
 70 | /**
 71 |  * Render the HTML for this item.
 72 |  */
 73 | vsaq.questionnaire.items.BoxItem.prototype.render = function() {
 74 |   var oldNode = this.container;
 75 |   this.container = goog.soy.renderAsElement(vsaq.questionnaire.templates.box, {
 76 |     id: this.id,
 77 |     captionHtml: soydata.VERY_UNSAFE.ordainSanitizedHtml(this.text),
 78 |     placeholder: this.placeholder,
 79 |     inputPattern: this.inputPattern,
 80 |     inputTitle: this.inputTitle,
 81 |     isRequired: Boolean(this.required)
 82 |   });
 83 |   goog.dom.replaceNode(this.container, oldNode);
 84 | 
 85 |   this.textArea_ = /** @type {!HTMLTextAreaElement} */
 86 |       (vsaq.questionnaire.utils.findById(this.container, this.id));
 87 |   goog.events.listen(
 88 |       this.textArea_,
 89 |       [goog.events.EventType.KEYUP, goog.events.EventType.CHANGE],
 90 |       this.answerChanged,
 91 |       true,
 92 |       this);
 93 | };
 94 | 
 95 | 
 96 | /**
 97 |  * Type of the question. This is used to distinguish questions in serialized
 98 |  * format.
 99 |  * @type {string}
100 |  * @const
101 |  */
102 | vsaq.questionnaire.items.BoxItem.TYPE = 'box';
103 | 
104 | 
105 | /**
106 |  * Parses BoxItems. If the topmost item in the passed Array is an a
107 |  * BoxItem, it is consumed and a BoxItem instance is returned.
108 |  * If the topmost item is not a BoxItem, an exception is thrown.
109 |  * @param {!Array.} questionStack Array of serialized
110 |  *     questionnaire Items.
111 |  * @return {!vsaq.questionnaire.items.BoxItem} The parsed BoxItem.
112 |  */
113 | vsaq.questionnaire.items.BoxItem.parse = function(questionStack) {
114 |   var item = questionStack.shift();
115 |   if (item.type != vsaq.questionnaire.items.BoxItem.TYPE)
116 |     throw new vsaq.questionnaire.items.ParseError('Wrong parser chosen.');
117 | 
118 |   return new vsaq.questionnaire.items.BoxItem(item.id, item.cond, item.text,
119 |       item.placeholder, item.inputPattern, item.inputTitle, item.required);
120 | };
121 | 
122 | 
123 | /** @inheritDoc */
124 | vsaq.questionnaire.items.BoxItem.prototype.setReadOnly = function(readOnly) {
125 |   this.textArea_.readOnly = readOnly;
126 | };
127 | 
128 | 
129 | /** @inheritDoc */
130 | vsaq.questionnaire.items.BoxItem.prototype.getValue = function() {
131 |   return this.textArea_.value;
132 | };
133 | 
134 | 
135 | /** @inheritDoc */
136 | vsaq.questionnaire.items.BoxItem.prototype.setInternalValue =
137 |     function(value) {
138 |   this.textArea_.value = /** @type {string} */ (value);
139 | };
140 | 
141 | 
142 | /** @inheritDoc */
143 | vsaq.questionnaire.items.BoxItem.prototype.isAnswered = function() {
144 |   return this.getValue().length > 0;
145 | };
146 | 
147 | 


--------------------------------------------------------------------------------
/vsaq/static/questionnaire/lineitem.js:
--------------------------------------------------------------------------------
  1 | /**
  2 |  * @license
  3 |  * Copyright 2016 Google Inc. All rights reserved.
  4 |  * Licensed under the Apache License, Version 2.0 (the "License");
  5 |  * you may not use this file except in compliance with the License.
  6 |  * You may obtain a copy of the License at
  7 |  *
  8 |  *   http://www.apache.org/licenses/LICENSE-2.0
  9 |  *
 10 |  * Unless required by applicable law or agreed to in writing, software
 11 |  * distributed under the License is distributed on an "AS IS" BASIS,
 12 |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13 |  * See the License for the specific language governing permissions and
 14 |  * limitations under the License.
 15 |  */
 16 | 
 17 | /**
 18 |  * @fileoverview A questionnaire item with an input field.
 19 |  */
 20 | 
 21 | goog.provide('vsaq.questionnaire.items.LineItem');
 22 | 
 23 | goog.require('goog.dom');
 24 | goog.require('goog.events');
 25 | goog.require('goog.events.EventType');
 26 | goog.require('goog.soy');
 27 | goog.require('vsaq.questionnaire.items.ParseError');
 28 | goog.require('vsaq.questionnaire.items.ValueItem');
 29 | goog.require('vsaq.questionnaire.templates');
 30 | goog.require('vsaq.questionnaire.utils');
 31 | 
 32 | 
 33 | 
 34 | /**
 35 |  * A question that allows the user to answer in an input field.
 36 |  * @param {string} id An ID uniquely identifying the question.
 37 |  * @param {?string} conditions A string containing conditions which must be met
 38 |  *     for the item to be visible to the user.
 39 |  * @param {string} caption The caption to show above the input field.
 40 |  * @param {string=} opt_inputType The type of the stored value.
 41 |  * @param {string=} opt_placeholder The placeholder text, displayed in place of
 42 |  *     the value.
 43 |  * @param {string=} opt_inputPattern HTML5 pattern attribute value for the input
 44 |  *     field. See {@link
 45 |  *     https://html.spec.whatwg.org/multipage/forms.html#the-pattern-attribute}.
 46 |  * @param {string=} opt_inputTitle HTML5 title attribute value for the input
 47 |  *     field. See {@link
 48 |  *     https://html.spec.whatwg.org/multipage/forms.html#attr-input-title}.
 49 |  * @param {boolean=} opt_isRequired Iff true, the item value is required.
 50 |  * @extends {vsaq.questionnaire.items.ValueItem}
 51 |  * @constructor
 52 |  */
 53 | vsaq.questionnaire.items.LineItem = function(id, conditions, caption,
 54 |     opt_inputType, opt_placeholder, opt_inputPattern, opt_inputTitle,
 55 |     opt_isRequired) {
 56 |   goog.base(this, id, conditions, caption, opt_placeholder, opt_inputPattern,
 57 |       opt_inputTitle, opt_isRequired);
 58 | 
 59 |   /**
 60 |    * The html input element where the user can answer the question.
 61 |    * @type {!HTMLInputElement}
 62 |    * @private
 63 |    */
 64 |   this.textBox_;
 65 | 
 66 |   /**
 67 |    * The type of the stored value.
 68 |    * @type {string|undefined}
 69 |    */
 70 |   this.inputType = opt_inputType;
 71 | 
 72 |   this.render();
 73 | };
 74 | goog.inherits(vsaq.questionnaire.items.LineItem,
 75 |               vsaq.questionnaire.items.ValueItem);
 76 | 
 77 | 
 78 | /**
 79 |  * Render the HTML for this item.
 80 |  */
 81 | vsaq.questionnaire.items.LineItem.prototype.render = function() {
 82 |   var oldNode = this.container;
 83 |   this.container = goog.soy.renderAsElement(vsaq.questionnaire.templates.line,
 84 |       {
 85 |         id: this.id,
 86 |         captionHtml: soydata.VERY_UNSAFE.ordainSanitizedHtml(this.text),
 87 |         type: this.inputType,
 88 |         inputPattern: this.inputPattern,
 89 |         inputTitle: this.inputTitle,
 90 |         placeholder: this.placeholder,
 91 |         isRequired: Boolean(this.required)
 92 |       });
 93 |   goog.dom.replaceNode(this.container, oldNode);
 94 | 
 95 |   this.textBox_ = /** @type {!HTMLInputElement} */
 96 |       (vsaq.questionnaire.utils.findById(this.container, this.id));
 97 |   goog.events.listen(
 98 |       this.textBox_,
 99 |       [goog.events.EventType.KEYUP, goog.events.EventType.CHANGE],
100 |       this.answerChanged,
101 |       true,
102 |       this);
103 | };
104 | 
105 | 
106 | /**
107 |  * Type of the question. This is used to distinguish questions in serialized
108 |  * format.
109 |  * @type {string}
110 |  * @const
111 |  */
112 | vsaq.questionnaire.items.LineItem.TYPE = 'line';
113 | 
114 | 
115 | /**
116 |  * Parses LineItems. If the topmost item in the passed Array is an a
117 |  * LineItem, it is consumed and a LineItem instance is returned.
118 |  * If the topmost item is not a LineItem, an exception is thrown.
119 |  * @param {!Array.} questionStack Array of serialized
120 |  *     questionnaire Items.
121 |  * @return {!vsaq.questionnaire.items.LineItem} The parsed LineItem.
122 |  */
123 | vsaq.questionnaire.items.LineItem.parse = function(questionStack) {
124 |   var item = questionStack.shift();
125 |   if (item.type != vsaq.questionnaire.items.LineItem.TYPE)
126 |     throw new vsaq.questionnaire.items.ParseError('Wrong parser chosen.');
127 | 
128 |   return new vsaq.questionnaire.items.LineItem(item.id, item.cond, item.text,
129 |       item.inputType, item.placeholder, item.inputPattern, item.inputTitle,
130 |       item.required);
131 | };
132 | 
133 | 
134 | /** @inheritDoc */
135 | vsaq.questionnaire.items.LineItem.prototype.setReadOnly = function(readOnly) {
136 |   this.textBox_.readOnly = readOnly;
137 | };
138 | 
139 | 
140 | /** @inheritDoc */
141 | vsaq.questionnaire.items.LineItem.prototype.getValue = function() {
142 |   return this.textBox_.value;
143 | };
144 | 
145 | 
146 | /** @inheritDoc */
147 | vsaq.questionnaire.items.LineItem.prototype.setInternalValue =
148 |     function(value) {
149 |   this.textBox_.value = /** @type {string} */ (value);
150 | };
151 | 
152 | 
153 | /** @inheritDoc */
154 | vsaq.questionnaire.items.LineItem.prototype.isAnswered = function() {
155 |   return this.getValue().length > 0;
156 | };
157 | 


--------------------------------------------------------------------------------
/vsaq/static/questionnaire/utils_test.js:
--------------------------------------------------------------------------------
  1 | /**
  2 |  * @license
  3 |  * Copyright 2016 Google Inc. All rights reserved.
  4 |  * Licensed under the Apache License, Version 2.0 (the "License");
  5 |  * you may not use this file except in compliance with the License.
  6 |  * You may obtain a copy of the License at
  7 |  *
  8 |  *   http://www.apache.org/licenses/LICENSE-2.0
  9 |  *
 10 |  * Unless required by applicable law or agreed to in writing, software
 11 |  * distributed under the License is distributed on an "AS IS" BASIS,
 12 |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13 |  * See the License for the specific language governing permissions and
 14 |  * limitations under the License.
 15 |  */
 16 | 
 17 | /**
 18 |  * @fileoverview Tests for vsaq.questionnaire.utils.
 19 |  */
 20 | 
 21 | goog.provide('vsaq.questionnaire.items.UtilsTests');
 22 | goog.setTestOnly('vsaq.questionnaire.items.UtilsTests');
 23 | 
 24 | goog.require('goog.testing.asserts');
 25 | goog.require('goog.testing.jsunit');
 26 | goog.require('vsaq.questionnaire.utils');
 27 | 
 28 | 
 29 | /**
 30 |  * Tests tokenizing expressions.
 31 |  */
 32 | function testTokenizeExpression() {
 33 |   var exp1 = 'a&&b';
 34 |   var exp1b = 'a &&b';
 35 |   var exp1c = 'a && b';
 36 |   var exp1_tok = ['a', '&', 'b'];
 37 | 
 38 |   var exp2 = '(a&&(b))';
 39 |   var exp2_tok = [['a', '&', ['b']]];
 40 | 
 41 |   var exp3 = '(a&&b)';
 42 |   var exp3_tok = [['a', '&', 'b']];
 43 | 
 44 |   var exp4 = '((aa)) && b';
 45 |   var exp4_tok = [[['aa']], '&', 'b'];
 46 | 
 47 |   var exp5 = '((aa)&&(aa||bb))';
 48 |   var exp5_tok = [[['aa'], '&', ['aa', '|', 'bb']]];
 49 | 
 50 |   var exp6 = 'a&&(b||c&&!(!d||(e&&f)))&&(g||h)';
 51 |   var exp6_tok =
 52 |       ['a', '&', ['b', '|', 'c', '&', '!', ['!', 'd', '|', ['e', '&', 'f']]],
 53 |        '&', ['g', '|', 'h']];
 54 | 
 55 |   var exp7 = 'a&&b&&c(d,e)';
 56 |   var exp7_tok =
 57 |       ['a', '&', 'b', '&', 'c', ['d', ',', 'e']];
 58 | 
 59 |   var exp8 = 'a||"b"||c';
 60 |   var exp8_tok =
 61 |       ['a', '|', '"b"', '|', 'c'];
 62 | 
 63 |   var exp9 = '"a\\"b\\\\\\"c\\\\"';
 64 |   var exp9_tok =
 65 |       ['"a\\"b\\\\\\"c\\\\"'];
 66 | 
 67 |   var invalid_exp1 = 'a!&&b';
 68 |   var invalid_exp2 = 'a&&(c||d';
 69 |   var invalid_exp3 = 'a&b';
 70 |   var invalid_exp4 = 'a&&b&&(c';
 71 |   var invalid_exp5 = 'a&&b&&c)';
 72 |   var invalid_exp6 = 'a&&b&&c(d';
 73 |   var invalid_exp7 = 'a&&b&&c(d,';
 74 |   var invalid_exp8 = 'a&&b&&c(d e)';
 75 |   var invalid_exp9 = 'a(b(c)';
 76 |   var invalid_expA = '"a';
 77 |   var invalid_expB = '"\\"';
 78 |   var invalid_expC = 'a "';
 79 |   var invalid_expD = 'a "b';
 80 | 
 81 | 
 82 |   var tokenize = vsaq.questionnaire.utils.tokenizeExpression;
 83 | 
 84 |   assertArrayEquals(exp1_tok, tokenize(exp1));
 85 |   assertArrayEquals(exp1_tok, tokenize(exp1b));
 86 |   assertArrayEquals(exp1_tok, tokenize(exp1c));
 87 | 
 88 |   assertArrayEquals(exp2_tok, tokenize(exp2));
 89 |   assertArrayEquals(exp3_tok, tokenize(exp3));
 90 |   assertArrayEquals(exp4_tok, tokenize(exp4));
 91 |   assertArrayEquals(exp5_tok, tokenize(exp5));
 92 |   assertArrayEquals(exp6_tok, tokenize(exp6));
 93 |   assertArrayEquals(exp7_tok, tokenize(exp7));
 94 |   assertArrayEquals(exp8_tok, tokenize(exp8));
 95 |   assertArrayEquals(exp9_tok, tokenize(exp9));
 96 | 
 97 |   assertThrows(function() { tokenize(invalid_exp1) });
 98 |   assertThrows(function() { tokenize(invalid_exp2) });
 99 |   assertThrows(function() { tokenize(invalid_exp3) });
100 |   assertThrows(function() { tokenize(invalid_exp4) });
101 |   assertThrows(function() { tokenize(invalid_exp5) });
102 |   assertThrows(function() { tokenize(invalid_exp6) });
103 |   assertThrows(function() { tokenize(invalid_exp7) });
104 |   assertThrows(function() { tokenize(invalid_exp8) });
105 |   assertThrows(function() { tokenize(invalid_exp9) });
106 |   assertThrows(function() { tokenize(invalid_expA) });
107 |   assertThrows(function() { tokenize(invalid_expB) });
108 |   assertThrows(function() { tokenize(invalid_expC) });
109 |   assertThrows(function() { tokenize(invalid_expD) });
110 | }
111 | 
112 | 
113 | /**
114 |  * Tests evaluating expressions.
115 |  */
116 | function testEvalExpression() {
117 |   var resolver = function(variable) {
118 |     return variable == 'true';
119 |   };
120 |   var evalExp = vsaq.questionnaire.utils.evalExpression;
121 | 
122 |   var fexp1 = 'true && false';
123 |   var fexp2 = '(true && false)';
124 |   var fexp3 = 'false && false';
125 |   var fexp4 = 'false || false';
126 |   var fexp5 = 'true && (false && !true)';
127 |   var fexp6 = 'contains("abc", "b") && contains("abc", "d")';
128 | 
129 |   var texp1 = 'true && true';
130 |   var texp2 = 'true && (true || false)';
131 |   var texp3 = 'false || !false';
132 |   var texp4 = 'true && (false || (!true)) || (true && true)';
133 |   var texp5 = 'matches("a\\"b\\\\\\"c\\\\", "^(z|[a]).(?:b{1,})..[asxdc].$")';
134 |   var texp6 = 'matches("abc", "B", "i")';
135 |   var texp7 = '!(matches("abc", "d"))';
136 |   var texp8 = 'contains("abc", "b")';
137 |   var texp9 = '!(contains("abc", "d"))';
138 |   var texp10 = 'contains("abc", "b") && contains("abc", "c")';
139 | 
140 |   assertEquals(false, evalExp(fexp1, resolver));
141 |   assertEquals(false, evalExp(fexp2, resolver));
142 |   assertEquals(false, evalExp(fexp3, resolver));
143 |   assertEquals(false, evalExp(fexp4, resolver));
144 |   assertEquals(false, evalExp(fexp5, resolver));
145 |   assertEquals(false, evalExp(fexp6, resolver));
146 | 
147 |   assertEquals(true, evalExp(texp1, resolver));
148 |   assertEquals(true, evalExp(texp2, resolver));
149 |   assertEquals(true, evalExp(texp3, resolver));
150 |   assertEquals(true, evalExp(texp4, resolver));
151 |   assertEquals(true, evalExp(texp5, resolver));
152 |   assertEquals(true, evalExp(texp6, resolver));
153 |   assertEquals(true, evalExp(texp7, resolver));
154 |   assertEquals(true, evalExp(texp8, resolver));
155 |   assertEquals(true, evalExp(texp9, resolver));
156 |   assertEquals(true, evalExp(texp10, resolver));
157 | }
158 | 


--------------------------------------------------------------------------------
/vsaq/static/qpage_base.js:
--------------------------------------------------------------------------------
  1 | /**
  2 |  * @license
  3 |  * Copyright 2016 Google Inc. All rights reserved.
  4 |  * Licensed under the Apache License, Version 2.0 (the "License");
  5 |  * you may not use this file except in compliance with the License.
  6 |  * You may obtain a copy of the License at
  7 |  *
  8 |  *   http://www.apache.org/licenses/LICENSE-2.0
  9 |  *
 10 |  * Unless required by applicable law or agreed to in writing, software
 11 |  * distributed under the License is distributed on an "AS IS" BASIS,
 12 |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13 |  * See the License for the specific language governing permissions and
 14 |  * limitations under the License.
 15 |  */
 16 | 
 17 | /**
 18 |  * @fileoverview Main client-side renderer for questionnaire.
 19 |  */
 20 | 
 21 | goog.provide('vsaq.QpageBase');
 22 | 
 23 | goog.require('goog.array');
 24 | goog.require('goog.dom');
 25 | goog.require('goog.events');
 26 | goog.require('goog.events.EventType');
 27 | goog.require('goog.structs');
 28 | goog.require('goog.ui.Tooltip');
 29 | goog.require('vsaq.Questionnaire');
 30 | goog.require('vsaq.utils');
 31 | 
 32 | 
 33 | 
 34 | /**
 35 |  * Initialize the questionnaire page.
 36 |  * @constructor
 37 |  */
 38 | vsaq.QpageBase = function() {
 39 |   this.changes = {};
 40 |   var qNode = goog.dom.getElement('_vsaq_body');
 41 |   if (!qNode) {
 42 |     alert('Did not find an element with ID _vsaq_body to render the ' +
 43 |         'questionnaire into.');
 44 |     return;
 45 |   }
 46 |   this.questionnaire = new vsaq.Questionnaire(qNode);
 47 |   this.nextUpdateAttemptIn = vsaq.QpageBase.SAVE_TIMEOUT_LENGTH;
 48 |   this.isReadOnly = goog.dom.getElement('_rom_').value == 'true';
 49 | 
 50 |   this.statusIndicator = goog.dom.getElement('_vsaq_saved_status') ||
 51 |       goog.dom.createDom('span');
 52 |   this.questionnaire.setReadOnlyMode(this.isReadOnly);
 53 | 
 54 |   vsaq.utils.initClickables({
 55 |     'eh-edit': goog.bind(this.makeEditable, this)
 56 |   });
 57 | 
 58 |   goog.events.listen(window, [goog.events.EventType.BEFOREUNLOAD],
 59 |       function() {
 60 |         if (vsaq.qpageObject_ && vsaq.qpageObject_.unsavedChanges())
 61 |           return 'You have unsaved changes.';
 62 |       });
 63 | };
 64 | 
 65 | 
 66 | /**
 67 |  * Length of timeout to automatically save updates (in seconds).
 68 |  * @type {number}
 69 |  * @const
 70 |  * @protected
 71 |  */
 72 | vsaq.QpageBase.SAVE_TIMEOUT_LENGTH = 2;
 73 | 
 74 | 
 75 | /**
 76 |  * The questionnaire shown on the page.
 77 |  * @type {vsaq.Questionnaire}
 78 |  */
 79 | vsaq.QpageBase.prototype.questionnaire;
 80 | 
 81 | 
 82 | /**
 83 |  * A dictionary containing all the changes since the last successful save. Keys
 84 |  * are the ids of the changed questions, values are the updated values of the
 85 |  * question.
 86 |  * @type {Object.}
 87 |  * @protected
 88 |  */
 89 | vsaq.QpageBase.prototype.changes;
 90 | 
 91 | 
 92 | /**
 93 |  * TimeoutID for timeout that saves changes every few seconds.
 94 |  * @type {number}
 95 |  * @protected
 96 |  */
 97 | vsaq.QpageBase.prototype.saveTimeout;
 98 | 
 99 | 
100 | /**
101 |  * The current timeout for saving changes (in sec).
102 |  * @type {number}
103 |  * @protected
104 |  */
105 | vsaq.QpageBase.prototype.nextUpdateAttemptIn;
106 | 
107 | 
108 | /**
109 |  * Whether or not the questionnaire is read-only.
110 |  * @type {boolean}
111 |  * @protected
112 |  */
113 | vsaq.QpageBase.prototype.isReadOnly;
114 | 
115 | 
116 | /**
117 |  * Whether or not the questionnaire is read-only.
118 |  * @type {!Element}
119 |  * @protected
120 |  */
121 | vsaq.QpageBase.prototype.statusIndicator;
122 | 
123 | 
124 | /**
125 |  * Make questionnaire editable.
126 |  */
127 | vsaq.QpageBase.prototype.makeEditable = function() {
128 |   this.isReadOnly = false;
129 |   this.questionnaire.setReadOnlyMode(this.isReadOnly);
130 |   this.questionnaire.render();
131 | };
132 | 
133 | 
134 | /**
135 |  * Attempts to keep track of  updates that were done to the current
136 |  * questionnaire.
137 |  */
138 | vsaq.QpageBase.prototype.sendUpdate = goog.abstractMethod;
139 | 
140 | 
141 | /**
142 |  * Creates tooltips for all span elements with the class "tooltip-link". The
143 |  * content of the tooltip is taken from the data-html-tooltip attribute.
144 |  * @protected
145 |  */
146 | vsaq.QpageBase.prototype.installToolTips = function() {
147 |   var ttElements = goog.dom.getElementsByClass('tooltip-link',
148 |       this.questionnaire.getRootElement());
149 |   goog.array.forEach(ttElements, function(element) {
150 |     var tip = new goog.ui.Tooltip(element);
151 |     tip.setText(element.getAttribute('data-tooltip'));
152 |     tip.className = 'vsaq-tooltip';
153 |   });
154 | };
155 | 
156 | 
157 | /**
158 |  * Sets a timeout for when to next attempt to save changes.
159 |  * @param {boolean} lastSaveFailed Whether or not the last attempt to save
160 |  *     updates failed.
161 |  * @protected
162 |  */
163 | vsaq.QpageBase.prototype.scheduleNextUpdate = function(lastSaveFailed) {
164 |   if (lastSaveFailed) {
165 |     if (this.nextUpdateAttemptIn <= 256)
166 |       this.nextUpdateAttemptIn *= 2;
167 |   } else {
168 |     this.nextUpdateAttemptIn = vsaq.QpageBase.SAVE_TIMEOUT_LENGTH;
169 |   }
170 | 
171 |   this.saveTimeout = window.setTimeout(
172 |       goog.bind(this.sendUpdate, this), this.nextUpdateAttemptIn * 1000);
173 | };
174 | 
175 | 
176 | /**
177 |  * Loads the template of a questionnaire. Once completed, tries to load the
178 |  * answers as well.
179 |  */
180 | vsaq.QpageBase.prototype.loadQuestionnaire = goog.abstractMethod;
181 | 
182 | 
183 | /**
184 |  * Returns whether there are unsaved changes.
185 |  * @return {boolean} Returns true if there are unsaved changes.
186 |  */
187 | vsaq.QpageBase.prototype.unsavedChanges = function() {
188 |   return (goog.structs.getCount(this.changes) > 0);
189 | };
190 | 
191 | 
192 | /**
193 |  * Instance of the questionnaire page for the current page.
194 |  * @type {vsaq.QpageBase}
195 |  * @protected
196 |  */
197 | vsaq.qpageObject_;
198 | 


--------------------------------------------------------------------------------
/vsaq/static/questionnaire/containeritem.js:
--------------------------------------------------------------------------------
  1 | /**
  2 |  * @license
  3 |  * Copyright 2016 Google Inc. All rights reserved.
  4 |  * Licensed under the Apache License, Version 2.0 (the "License");
  5 |  * you may not use this file except in compliance with the License.
  6 |  * You may obtain a copy of the License at
  7 |  *
  8 |  *   http://www.apache.org/licenses/LICENSE-2.0
  9 |  *
 10 |  * Unless required by applicable law or agreed to in writing, software
 11 |  * distributed under the License is distributed on an "AS IS" BASIS,
 12 |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13 |  * See the License for the specific language governing permissions and
 14 |  * limitations under the License.
 15 |  */
 16 | 
 17 | /**
 18 |  * @fileoverview A container item that can contain further items. A typical
 19 |  *     container item could be a blockitem or groupitem.
 20 |  */
 21 | 
 22 | goog.provide('vsaq.questionnaire.items.ContainerItem');
 23 | 
 24 | 
 25 | goog.require('goog.array');
 26 | goog.require('goog.dom');
 27 | goog.require('vsaq.questionnaire.items.Item');
 28 | 
 29 | 
 30 | 
 31 | /**
 32 |  * A container of items e.g. blockitem or groupitems.
 33 |  * @inheritDoc
 34 |  * @extends {vsaq.questionnaire.items.Item}
 35 |  * @constructor
 36 |  */
 37 | vsaq.questionnaire.items.ContainerItem = function(id, conditions) {
 38 |   goog.base(this, id, conditions);
 39 | 
 40 |   /**
 41 |    * An array of contained items.
 42 |    * @type {!vsaq.questionnaire.items.ItemArray}
 43 |    * @protected
 44 |    */
 45 |   this.containerItems = [];
 46 | };
 47 | goog.inherits(vsaq.questionnaire.items.ContainerItem,
 48 |               vsaq.questionnaire.items.Item);
 49 | 
 50 | 
 51 | /**
 52 |  * Return all container items.
 53 |  * @return {!vsaq.questionnaire.items.ItemArray} all container items.
 54 |  */
 55 | vsaq.questionnaire.items.ContainerItem.prototype.getContainerItems =
 56 |     function() {
 57 |   return this.containerItems;
 58 | };
 59 | 
 60 | 
 61 | /**
 62 |  * Adds a subitem to the current container.
 63 |  * @param {!vsaq.questionnaire.items.Item} item The item to be added
 64 |  * to the container.
 65 |  */
 66 | vsaq.questionnaire.items.ContainerItem.prototype.addItem = function(item) {
 67 |   this.containerItems.push(item);
 68 |   item.parentItem = this;
 69 |   this.container.appendChild(item.container);
 70 | };
 71 | 
 72 | 
 73 | /**
 74 |  * Returns the passed item's index within the internal containerItems array.
 75 |  * @param {!vsaq.questionnaire.items.Item} item The item to be found.
 76 |  * @return {?number} The index of the item within subItem.
 77 |  * @private
 78 |  */
 79 | vsaq.questionnaire.items.ContainerItem.prototype.getContainerItemId_ =
 80 |     function(item) {
 81 |   var targetSubItemId = null;
 82 |   var subItemCounter = 0;
 83 |   goog.array.some(this.containerItems, function(subItem) {
 84 |     if (subItem.id == item.id) {
 85 |       targetSubItemId = subItemCounter;
 86 |       return true;
 87 |     }
 88 |     subItemCounter++;
 89 |     return false;
 90 |   });
 91 |   return targetSubItemId;
 92 | };
 93 | 
 94 | 
 95 | /**
 96 |  * Returns the upper or lower sibling of a given item.
 97 |  * @param {!vsaq.questionnaire.items.Item} item The reference item.
 98 |  * @param {!boolean} getLowerSibling true if the lower sibling should be
 99 |  *     returned, else the upper sibling will be returned.
100 |  * @return {?vsaq.questionnaire.items.Item} The upper or lower sibling.
101 |  */
102 | vsaq.questionnaire.items.ContainerItem.prototype.getSiblingItem = function(item,
103 |     getLowerSibling) {
104 |   var targetSubItemId = this.getContainerItemId_(item);
105 |   if (targetSubItemId == null)
106 |     return null;
107 |   var numbercontainerItems_ = this.containerItems.length;
108 |   var siblingItem = null;
109 |   if (getLowerSibling) {
110 |     if (targetSubItemId + 1 < numbercontainerItems_)
111 |       siblingItem = this.containerItems[targetSubItemId + 1];
112 |   } else if (targetSubItemId - 1 >= 0) {
113 |     siblingItem = this.containerItems[targetSubItemId - 1];
114 |   }
115 |   return siblingItem;
116 | };
117 | 
118 | 
119 | /**
120 |  * Adds a subitem after or before a specific other item in the current
121 |  * container.
122 |  * @param {!vsaq.questionnaire.items.Item} newItem The item to be inserted.
123 |  * @param {!vsaq.questionnaire.items.Item} srcItem The item to which newItem
124 |  *     is inserted relative to.
125 |  * @param {boolean} insertBelow true if newItem is supposed to be inserted
126 |  *     below srcItem.
127 |  * @private
128 |  */
129 | vsaq.questionnaire.items.ContainerItem.prototype.insertItemRelativeTo_ =
130 |     function(newItem, srcItem, insertBelow) {
131 | 
132 |   var srcSubItemId = this.getContainerItemId_(srcItem);
133 |   if (srcSubItemId == null)
134 |     return;
135 | 
136 |   var newItemPosition;
137 |   if (insertBelow) {
138 |     newItemPosition = srcSubItemId + 1;
139 |   } else {
140 |     // We prepend newItem by inserting it at the same position as srcItem.
141 |     // All consecutive items (including srcItem) will then be moved to a
142 |     // higher position.
143 |     newItemPosition = srcSubItemId;
144 |   }
145 | 
146 |   // Insert newItem directly after srcItem within the containerItems array.
147 |   goog.array.insertAt(this.containerItems, newItem, newItemPosition);
148 |   newItem.parentItemSet(this);
149 | };
150 | 
151 | 
152 | /**
153 |  * Adds a subitem after a specific other item in the current container.
154 |  * @param {!vsaq.questionnaire.items.Item} newItem The item to be appended to
155 |  *     the container.
156 |  * @param {!vsaq.questionnaire.items.Item} srcItem The item after which the
157 |  *     newItem will be inserted.
158 |  */
159 | vsaq.questionnaire.items.ContainerItem.prototype.insertAfter = function(newItem,
160 |     srcItem) {
161 |   this.insertItemRelativeTo_(newItem, srcItem, true);
162 |   goog.dom.insertSiblingAfter(newItem.container, srcItem.container);
163 | };
164 | 
165 | 
166 | /**
167 |  * Adds a subitem before a specific other item in the current container.
168 |  * @param {!vsaq.questionnaire.items.Item} newItem The item to be appended to
169 |  *     the container.
170 |  * @param {!vsaq.questionnaire.items.Item} srcItem The item before which the
171 |  *     newItem will be inserted.
172 |  */
173 | vsaq.questionnaire.items.ContainerItem.prototype.insertBefore =
174 |     function(newItem, srcItem) {
175 |   this.insertItemRelativeTo_(newItem, srcItem, false);
176 |   goog.dom.insertSiblingBefore(newItem.container, srcItem.container);
177 | };
178 | 
179 | 
180 | /**
181 |  * Remove an item from a container.
182 |  * @param {!vsaq.questionnaire.items.Item} item The to be deleted item.
183 |  */
184 | vsaq.questionnaire.items.ContainerItem.prototype.deleteItem = function(item) {
185 |   var targetSubItemId = this.getContainerItemId_(item);
186 |   if (targetSubItemId == null)
187 |     return;
188 |   // Remove the target from the array and close the new gap again.
189 |   goog.array.removeAt(this.containerItems, targetSubItemId);
190 |   goog.dom.removeNode(item.container);
191 | };
192 | 


--------------------------------------------------------------------------------
/vsaq/static/questionnaire/blockitems.js:
--------------------------------------------------------------------------------
  1 | /**
  2 |  * @license
  3 |  * Copyright 2016 Google Inc. All rights reserved.
  4 |  * Licensed under the Apache License, Version 2.0 (the "License");
  5 |  * you may not use this file except in compliance with the License.
  6 |  * You may obtain a copy of the License at
  7 |  *
  8 |  *   http://www.apache.org/licenses/LICENSE-2.0
  9 |  *
 10 |  * Unless required by applicable law or agreed to in writing, software
 11 |  * distributed under the License is distributed on an "AS IS" BASIS,
 12 |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13 |  * See the License for the specific language governing permissions and
 14 |  * limitations under the License.
 15 |  */
 16 | 
 17 | /**
 18 |  * @fileoverview Questionnaire block items.
 19 |  *
 20 |  * Questionnaires allow to group questions in blocks. Blocks are indicated by
 21 |  * {vsaq.questionnaire.items.BlockItem}.
 22 |  */
 23 | 
 24 | goog.provide('vsaq.questionnaire.items.BlockItem');
 25 | 
 26 | goog.require('goog.array');
 27 | goog.require('goog.dom');
 28 | goog.require('goog.soy');
 29 | goog.require('goog.string');
 30 | goog.require('vsaq.questionnaire.items.ContainerItem');
 31 | goog.require('vsaq.questionnaire.items.Item');
 32 | goog.require('vsaq.questionnaire.items.ParseError');
 33 | goog.require('vsaq.questionnaire.items.RadioItem');
 34 | goog.require('vsaq.questionnaire.items.ValueItem');
 35 | goog.require('vsaq.questionnaire.templates');
 36 | 
 37 | 
 38 | 
 39 | /**
 40 |  * Starts a new block in the questionnaire.
 41 |  * @param {?string} id An ID uniquely identifying the item.
 42 |  * @param {?string} conditions A string containing conditions which must be met
 43 |  *     for the item to be visible to the user.
 44 |  * @param {?string} caption The caption of the block.
 45 |  * @param {?string=} opt_auth The needed authorization to get an item displayed.
 46 |  *     The auth param on {@code vsaq.questionnaire.items.BlockItem} only
 47 |  *     prevents that items are displayed to the user (hidden by display=none).
 48 |  * @param {?string=} opt_className Name of a CSS class to add to the block.
 49 |  * @extends {vsaq.questionnaire.items.ContainerItem}
 50 |  * @constructor
 51 |  */
 52 | vsaq.questionnaire.items.BlockItem = function(id, conditions, caption,
 53 |     opt_auth, opt_className) {
 54 |   goog.base(this, id, conditions);
 55 | 
 56 |   /**
 57 |    * The needed authorization to get an item displayed.
 58 |    * @type {string}
 59 |    */
 60 |   this.auth = goog.string.makeSafe(opt_auth);
 61 |   var propertyInformation = {
 62 |     nameInClass: 'auth',
 63 |     defaultValues: {
 64 |       admin: 'admin'
 65 |     },
 66 |     metadata: true
 67 |   };
 68 |   this.addPropertyInformation('auth', propertyInformation);
 69 | 
 70 |   /**
 71 |    * Extra class to add to the item.
 72 |    * Stores the className attribute from the JSON definition.
 73 |    * @type {string}
 74 |    */
 75 |   this.className = goog.string.makeSafe(opt_className);
 76 |   propertyInformation = {
 77 |     nameInClass: 'className',
 78 |     defaultValues: {
 79 |       vsaq_invisible: 'vsaq-invisible'
 80 |     },
 81 |     metadata: true
 82 |   };
 83 |   this.addPropertyInformation('className', propertyInformation);
 84 | 
 85 |   /**
 86 |    * Text shown at the top of the block.
 87 |    * @type {string}
 88 |    */
 89 |   this.text = goog.string.makeSafe(caption);
 90 |   propertyInformation = {
 91 |     nameInClass: 'text',
 92 |     mandatory: true
 93 |   };
 94 |   this.addPropertyInformation('text', propertyInformation);
 95 | 
 96 |   this.render();
 97 | };
 98 | goog.inherits(vsaq.questionnaire.items.BlockItem,
 99 |               vsaq.questionnaire.items.ContainerItem);
100 | 
101 | 
102 | /**
103 |  * Render the HTML for this item.
104 |  */
105 | vsaq.questionnaire.items.BlockItem.prototype.render = function() {
106 |   var oldNode = this.container;
107 |   this.container = (goog.soy.renderAsElement(
108 |       vsaq.questionnaire.templates.block, {
109 |         id: this.id,
110 |         captionHtml: soydata.VERY_UNSAFE.ordainSanitizedHtml(this.text),
111 |         blockId: this.id
112 |       }));
113 |   // Append all children.
114 |   goog.array.forEach(this.containerItems, function(item) {
115 |     this.container.appendChild(item.container);
116 |   }, this);
117 |   goog.dom.replaceNode(this.container, oldNode);
118 | };
119 | 
120 | 
121 | /**
122 |  * Returns the number of unanswered questions in the block.
123 |  * @return {number} The number of unanswered questions in the block.
124 |  */
125 | vsaq.questionnaire.items.BlockItem.prototype.getUnansweredCount =
126 |     function() {
127 |   var count = 0, radioChecked = false, hasRadio = false;
128 | 
129 |   goog.array.forEach(this.containerItems, function(item) {
130 |     if (item instanceof vsaq.questionnaire.items.RadioItem) {
131 |       hasRadio = true;
132 |       if (item.isChecked()) radioChecked = true;
133 |       return;
134 |     }
135 |     // If we come across a ValueItem that is visible and not answered,
136 |     // increment the counter.
137 |     if (item instanceof vsaq.questionnaire.items.ValueItem &&
138 |         item.isVisible() &&
139 |         !item.isAnswered())
140 |       count++;
141 |     // Finally, if we come across a Block, count everything in that block
142 |     // recursively.
143 |     if (item instanceof vsaq.questionnaire.items.BlockItem)
144 |       count += item.getUnansweredCount();
145 |   }, this);
146 | 
147 |   // If there are radio buttons and none is checked, count as one.
148 |   if (hasRadio && !radioChecked) count++;
149 | 
150 |   return count;
151 | };
152 | 
153 | 
154 | /**
155 |  * Parses BlockItems. If the topmost item in the passed Array is a
156 |  * BlockItem, it is consumed and a BlockItem instance is returned.
157 |  * If the topmost item is not a BlockItem, an exception is thrown.
158 |  * @param {!Array.} questionStack Array of serialized
159 |  *     questionnaire items.
160 |  * @return {!vsaq.questionnaire.items.BlockItem} The parsed BlockItem.
161 |  */
162 | vsaq.questionnaire.items.BlockItem.parse = function(questionStack) {
163 |   var item = questionStack.shift();
164 |   if (item.type != vsaq.questionnaire.items.BlockItem.TYPE)
165 |     throw new vsaq.questionnaire.items.ParseError('Wrong parser chosen.');
166 | 
167 |   return new vsaq.questionnaire.items.BlockItem(item.id, item.cond,
168 |       item.text, item.auth, item.className);
169 | };
170 | 
171 | 
172 | /** @inheritDoc */
173 | vsaq.questionnaire.items.BlockItem.prototype.exportItem = function() {
174 |   var exportProperties =
175 |       vsaq.questionnaire.items.Item.prototype.exportItem.call(this);
176 |   var containerItems = [];
177 |   goog.array.forEach(this.containerItems, function(item) {
178 |     containerItems.push(item.exportItem());
179 |   });
180 |   exportProperties.set('items', containerItems);
181 |   return exportProperties;
182 | };
183 | 
184 | 
185 | /**
186 |  * Type of the question. This is used to distinguish questions in serialized
187 |  * format.
188 |  * @type {string}
189 |  * @const
190 |  */
191 | vsaq.questionnaire.items.BlockItem.TYPE = 'block';
192 | 
193 | 


--------------------------------------------------------------------------------
/vsaq/static/questionnaire/radioitem.js:
--------------------------------------------------------------------------------
  1 | /**
  2 |  * @license
  3 |  * Copyright 2016 Google Inc. All rights reserved.
  4 |  * Licensed under the Apache License, Version 2.0 (the "License");
  5 |  * you may not use this file except in compliance with the License.
  6 |  * You may obtain a copy of the License at
  7 |  *
  8 |  *   http://www.apache.org/licenses/LICENSE-2.0
  9 |  *
 10 |  * Unless required by applicable law or agreed to in writing, software
 11 |  * distributed under the License is distributed on an "AS IS" BASIS,
 12 |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13 |  * See the License for the specific language governing permissions and
 14 |  * limitations under the License.
 15 |  */
 16 | 
 17 | /**
 18 |  * @fileoverview A questionnaire item with a radio button.
 19 |  */
 20 | 
 21 | goog.provide('vsaq.questionnaire.items.RadioItem');
 22 | 
 23 | goog.require('goog.array');
 24 | goog.require('goog.dom');
 25 | goog.require('goog.events');
 26 | goog.require('goog.events.EventType');
 27 | goog.require('goog.soy');
 28 | goog.require('vsaq.questionnaire.items.Item');
 29 | goog.require('vsaq.questionnaire.items.ParseError');
 30 | goog.require('vsaq.questionnaire.items.ValueItem');
 31 | goog.require('vsaq.questionnaire.templates');
 32 | goog.require('vsaq.questionnaire.utils');
 33 | 
 34 | 
 35 | 
 36 | /**
 37 |  * A question that allows the user to answer by choosing a radio button.
 38 |  * @param {string} id An ID uniquely identifying the question.
 39 |  * @param {?string} conditions A string containing conditions which must be met
 40 |  *     for the item to be visible to the user.
 41 |  * @param {string} caption The caption to show next to the radio button.
 42 |  * @extends {vsaq.questionnaire.items.ValueItem}
 43 |  * @constructor
 44 |  */
 45 | vsaq.questionnaire.items.RadioItem = function(id, conditions, caption) {
 46 |   goog.base(this, id, conditions, caption);
 47 | 
 48 |   /**
 49 |    * The radio button that is the actual control behind this question.
 50 |    * @type {!HTMLInputElement}
 51 |    * @private
 52 |    */
 53 |   this.radioButton;
 54 | 
 55 |   this.render();
 56 | };
 57 | goog.inherits(vsaq.questionnaire.items.RadioItem,
 58 |               vsaq.questionnaire.items.ValueItem);
 59 | 
 60 | 
 61 | /**
 62 |  * Render the HTML for this item.
 63 |  */
 64 | vsaq.questionnaire.items.RadioItem.prototype.render = function() {
 65 |   var oldNode = this.container;
 66 |   this.container = goog.soy.renderAsElement(vsaq.questionnaire.templates.radio,
 67 |       {
 68 |         id: this.id,
 69 |         captionHtml: soydata.VERY_UNSAFE.ordainSanitizedHtml(this.text)
 70 |       });
 71 |   goog.dom.replaceNode(this.container, oldNode);
 72 | 
 73 |   this.radioButton = /** @type {!HTMLInputElement} */ (
 74 |       vsaq.questionnaire.utils.findById(this.container, this.id));
 75 |   goog.events.listen(this.radioButton,
 76 |       [goog.events.EventType.CHANGE],
 77 |       function(e) {
 78 |         // We are only changing the answer for the set radio-button.
 79 |         if (this.radioButton.checked) this.answerChanged();
 80 |       }, true, this);
 81 | };
 82 | 
 83 | 
 84 | /**
 85 |  * Constant indicating the string value of the radio item when selected.
 86 |  * @type {string}
 87 |  */
 88 | vsaq.questionnaire.items.RadioItem.CHECKED_VALUE = 'checked';
 89 | 
 90 | 
 91 | /**
 92 |  * Type of the question. This is used to distinguish questions in serialized
 93 |  * format.
 94 |  * @type {string}
 95 |  * @const
 96 |  */
 97 | vsaq.questionnaire.items.RadioItem.TYPE = 'radio';
 98 | 
 99 | 
100 | /**
101 |  * Parses RadioItems. If the topmost item in the passed Array is an a
102 |  * RadioItem, it is consumed and a RadioItem instance is returned.
103 |  * If the topmost item is not a RadioItem, an exception is thrown.
104 |  * @param {!Array.} questionStack Array of serialized
105 |  *     questionnaire Items.
106 |  * @return {!vsaq.questionnaire.items.RadioItem} The parsed RadioItem.
107 |  */
108 | vsaq.questionnaire.items.RadioItem.parse = function(questionStack) {
109 |   var item = questionStack.shift();
110 |   if (item.type != vsaq.questionnaire.items.RadioItem.TYPE)
111 |     throw new vsaq.questionnaire.items.ParseError('Wrong parser chosen.');
112 | 
113 |   return new vsaq.questionnaire.items.RadioItem(item.id, item.cond, item.text);
114 | };
115 | 
116 | 
117 | /** @inheritDoc */
118 | vsaq.questionnaire.items.RadioItem.prototype.setReadOnly = function(readOnly) {
119 |   this.radioButton.disabled = readOnly;
120 | };
121 | 
122 | 
123 | /** @inheritDoc */
124 | vsaq.questionnaire.items.RadioItem.prototype.isChecked = function(opt_value) {
125 |   return this.radioButton.checked;
126 | };
127 | 
128 | 
129 | /**
130 |  * If a radio item is changed, all other radio items in the same container need
131 |  * to have their value reset. For this reason, {@code
132 |  * vsaq.questionnaire.items.RadioItem} overrides the answerChanged method to
133 |  * ensure this happens.
134 |  * @protected
135 |  */
136 | vsaq.questionnaire.items.RadioItem.prototype.answerChanged = function() {
137 |   var changes = {};
138 |   var containerItems = this.parentItem.getContainerItems();
139 |   goog.array.forEach(containerItems, function(item) {
140 |     if (item instanceof vsaq.questionnaire.items.RadioItem)
141 |       changes[item.id] = item.getValue();
142 |   });
143 |   changes[this.id] = this.getValue();
144 |   var ev = {
145 |     type: vsaq.questionnaire.items.Item.CHANGED,
146 |     source: this,
147 |     changes: changes
148 |   };
149 |   this.eventDispatcher.dispatchEvent(ev);
150 | };
151 | 
152 | 
153 | /** @inheritDoc */
154 | vsaq.questionnaire.items.RadioItem.prototype.getValue = function() {
155 |   return this.radioButton.checked ?
156 |       vsaq.questionnaire.items.RadioItem.CHECKED_VALUE : '';
157 | };
158 | 
159 | 
160 | /**
161 |  * When the item is added to a parent container, the name of the radio item is
162 |  * set, so all radio items in a given container have the same name and only one
163 |  * can be selected at a time.
164 |  * @param {!vsaq.questionnaire.items.ContainerItem} parentItem The container the
165 |  * radio item was added to.
166 |  */
167 | vsaq.questionnaire.items.RadioItem.prototype.parentItemSet = function(
168 |     parentItem) {
169 |   this.parentItem = parentItem;
170 |   this.radioButton.name = 'radio_' + parentItem.id;
171 | };
172 | 
173 | 
174 | /** @inheritDoc */
175 | vsaq.questionnaire.items.RadioItem.prototype.setInternalValue =
176 |     function(value) {
177 |   if (goog.isBoolean(value) && value) {
178 |     this.radioButton.checked = true;
179 |   } else if (goog.isString(value) &&
180 |              (value == vsaq.questionnaire.items.RadioItem.CHECKED_VALUE)) {
181 |     this.radioButton.checked = true;
182 |   }
183 | };
184 | 
185 | 
186 | /**
187 |  * This function must not be called, as for RadioItems it is impossible to know
188 |  * from an individual item whether the group has been answered or not.
189 |  * If called, this function throws an exception.
190 |  */
191 | vsaq.questionnaire.items.RadioItem.prototype.isAnswered = function() {
192 |   throw 'This function should never be called.';
193 | };
194 | 


--------------------------------------------------------------------------------
/vsaq/static/questionnaire/yesnoitem.js:
--------------------------------------------------------------------------------
  1 | /**
  2 |  * @license
  3 |  * Copyright 2016 Google Inc. All rights reserved.
  4 |  * Licensed under the Apache License, Version 2.0 (the "License");
  5 |  * you may not use this file except in compliance with the License.
  6 |  * You may obtain a copy of the License at
  7 |  *
  8 |  *   http://www.apache.org/licenses/LICENSE-2.0
  9 |  *
 10 |  * Unless required by applicable law or agreed to in writing, software
 11 |  * distributed under the License is distributed on an "AS IS" BASIS,
 12 |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13 |  * See the License for the specific language governing permissions and
 14 |  * limitations under the License.
 15 |  */
 16 | 
 17 | /**
 18 |  * @fileoverview A questionnaire item which offers two choices (typically
 19 |  * yes and no).
 20 |  */
 21 | 
 22 | goog.provide('vsaq.questionnaire.items.YesNoItem');
 23 | 
 24 | goog.require('goog.dom');
 25 | goog.require('goog.events');
 26 | goog.require('goog.events.EventType');
 27 | goog.require('goog.soy');
 28 | goog.require('vsaq.questionnaire.items.ParseError');
 29 | goog.require('vsaq.questionnaire.items.ValueItem');
 30 | goog.require('vsaq.questionnaire.templates');
 31 | goog.require('vsaq.questionnaire.utils');
 32 | 
 33 | 
 34 | 
 35 | /**
 36 |  * A question that allows the user to choose between 2 options.
 37 |  * @param {string} id An ID uniquely identifying the question.
 38 |  * @param {?string} conditions A string containing conditions which must be met
 39 |  *     for the item to be visible to the user.
 40 |  * @param {string} caption The caption to show next to the radio button.
 41 |  * @param {string} yes String shown as label for the first option.
 42 |  * @param {string} no String shown as label for the second option.
 43 |  * @extends {vsaq.questionnaire.items.ValueItem}
 44 |  * @constructor
 45 |  */
 46 | vsaq.questionnaire.items.YesNoItem = function(id, conditions, caption, yes,
 47 |     no) {
 48 |   goog.base(this, id, conditions, caption);
 49 | 
 50 |   /**
 51 |    * The text for the yes choice.
 52 |    * @type {!string}
 53 |    */
 54 |   this.yes = yes;
 55 |   var propertyInformation = {
 56 |     nameInClass: 'yes',
 57 |     mandatory: true
 58 |   };
 59 |   this.addPropertyInformation('yes', propertyInformation);
 60 | 
 61 |   /**
 62 |    * The text for the no choice.
 63 |    * @type {!string}
 64 |    */
 65 |   this.no = no;
 66 |   propertyInformation = {
 67 |     nameInClass: 'no',
 68 |     mandatory: true
 69 |   };
 70 |   this.addPropertyInformation('no', propertyInformation);
 71 | 
 72 |   /**
 73 |    * The radio button for the 'yes' answer.
 74 |    * @type {!HTMLInputElement}
 75 |    * @private
 76 |    */
 77 |   this.yesRadio_;
 78 | 
 79 |   /**
 80 |    * The radio button for the 'no' answer.
 81 |    * @type {!HTMLInputElement}
 82 |    * @private
 83 |    */
 84 |   this.noRadio_;
 85 | 
 86 |   this.render();
 87 | };
 88 | goog.inherits(vsaq.questionnaire.items.YesNoItem,
 89 |               vsaq.questionnaire.items.ValueItem);
 90 | 
 91 | 
 92 | /**
 93 |  * Render the HTML for this item.
 94 |  */
 95 | vsaq.questionnaire.items.YesNoItem.prototype.render = function() {
 96 |   var oldNode = this.container;
 97 |   this.container = goog.soy.renderAsElement(vsaq.questionnaire.templates.yesno,
 98 |       {
 99 |         id: this.id,
100 |         captionHtml: soydata.VERY_UNSAFE.ordainSanitizedHtml(this.text),
101 |         yesHtml: soydata.VERY_UNSAFE.ordainSanitizedHtml(this.yes),
102 |         noHtml: soydata.VERY_UNSAFE.ordainSanitizedHtml(this.no)
103 |       });
104 |   goog.dom.replaceNode(this.container, oldNode);
105 | 
106 |   this.yesRadio_ = /** @type {!HTMLInputElement} */ (
107 |       vsaq.questionnaire.utils.findById(this.container, this.id + '/yes'));
108 |   this.noRadio_ = /** @type {!HTMLInputElement} */ (
109 |       vsaq.questionnaire.utils.findById(this.container, this.id + '/no'));
110 |   goog.events.listen(this.yesRadio_, goog.events.EventType.CLICK,
111 |       this.answerChanged, true, this);
112 |   goog.events.listen(this.noRadio_, goog.events.EventType.CLICK,
113 |       this.answerChanged, true, this);
114 | };
115 | 
116 | 
117 | /**
118 |  * Constant indicating the string value of the item when 'yes' is selected.
119 |  * @type {string}
120 |  */
121 | vsaq.questionnaire.items.YesNoItem.YES_VALUE = 'yes';
122 | 
123 | 
124 | /**
125 |  * Constant indicating the string value of the item when 'no' is selected.
126 |  * @type {string}
127 |  */
128 | vsaq.questionnaire.items.YesNoItem.NO_VALUE = 'no';
129 | 
130 | 
131 | /**
132 |  * Type of the question. This is used to distinguish questions in serialized
133 |  * format.
134 |  * @type {string}
135 |  * @const
136 |  */
137 | vsaq.questionnaire.items.YesNoItem.TYPE = 'yesno';
138 | 
139 | 
140 | /**
141 |  * Parses YesNoItems. If the topmost item in the passed Array is an a
142 |  * YesNoItem, it is consumed and a YesNoItem instance is returned.
143 |  * If the topmost item is not a YesNoItem, an exception is thrown.
144 |  * @param {!Array.} questionStack Array of serialized
145 |  *     questionnaire Items.
146 |  * @return {!vsaq.questionnaire.items.YesNoItem} The parsed YesNoItem.
147 |  */
148 | vsaq.questionnaire.items.YesNoItem.parse = function(questionStack) {
149 |   var item = questionStack.shift();
150 |   if (item.type != vsaq.questionnaire.items.YesNoItem.TYPE)
151 |     throw new vsaq.questionnaire.items.ParseError('Wrong parser chosen.');
152 | 
153 |   return new vsaq.questionnaire.items.YesNoItem(item.id, item.cond, item.text,
154 |                                                 item.yes, item.no);
155 | };
156 | 
157 | 
158 | /** @inheritDoc */
159 | vsaq.questionnaire.items.YesNoItem.prototype.setReadOnly = function(readOnly) {
160 |   this.yesRadio_.disabled = readOnly;
161 |   this.noRadio_.disabled = readOnly;
162 | };
163 | 
164 | 
165 | /** @inheritDoc */
166 | vsaq.questionnaire.items.YesNoItem.prototype.isChecked = function(opt_value) {
167 |   if (!opt_value) opt_value = '/yes';
168 |   var exp_value = opt_value.substring(opt_value.lastIndexOf('/') + 1);
169 |   return (this.getValue() == exp_value);
170 | };
171 | 
172 | 
173 | /** @inheritDoc */
174 | vsaq.questionnaire.items.YesNoItem.prototype.getValue = function() {
175 |   if (this.yesRadio_.checked) {
176 |     return vsaq.questionnaire.items.YesNoItem.YES_VALUE;
177 |   } else if (this.noRadio_.checked) {
178 |     return vsaq.questionnaire.items.YesNoItem.NO_VALUE;
179 |   }
180 |   return '';
181 | };
182 | 
183 | 
184 | /** @inheritDoc */
185 | vsaq.questionnaire.items.YesNoItem.prototype.setInternalValue =
186 |     function(value) {
187 |   if (value == vsaq.questionnaire.items.YesNoItem.YES_VALUE) {
188 |     this.yesRadio_.checked = true;
189 |     this.noRadio_.checked = false;
190 |   } else if (value == vsaq.questionnaire.items.YesNoItem.NO_VALUE) {
191 |     this.yesRadio_.checked = false;
192 |     this.noRadio_.checked = true;
193 |   } else {
194 |     this.yesRadio_.checked = false;
195 |     this.noRadio_.checked = false;
196 |   }
197 | };
198 | 
199 | 
200 | /** @inheritDoc */
201 | vsaq.questionnaire.items.YesNoItem.prototype.isAnswered = function() {
202 |   return this.getValue().length > 0;
203 | };
204 | 


--------------------------------------------------------------------------------
/vsaq/static/vsaq_base.css:
--------------------------------------------------------------------------------
  1 | /**
  2 |  * Copyright 2016 Google Inc. All Rights Reserved.
  3 |  *
  4 |  * Licensed under the Apache License, Version 2.0 (the "License");
  5 |  * you may not use this file except in compliance with the License.
  6 |  * You may obtain a copy of the License at
  7 |  *
  8 |  *     http://www.apache.org/licenses/LICENSE-2.0
  9 |  *
 10 |  * Unless required by applicable law or agreed to in writing, software
 11 |  * distributed under the License is distributed on an "AS IS" BASIS,
 12 |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13 |  * See the License for the specific language governing permissions and
 14 |  * limitations under the License.
 15 |  */
 16 | 
 17 | /**
 18 |  * Normal body and header styles.
 19 |  */
 20 | .right {
 21 |   float: right;
 22 | }
 23 | .left {
 24 |   float: left;
 25 | }
 26 | .center_text {
 27 |   text-align: center;
 28 | }
 29 | 
 30 | /**
 31 |  * Styles of various questionnaire items.
 32 |  */
 33 | #_vsaq_body fieldset {
 34 |   border:1px solid #ccc;
 35 |   padding:0px 16px 16px 16px;
 36 |   margin-bottom:16px;
 37 | }
 38 | #_vsaq_body fieldset>legend {
 39 |   font-size: 20px;
 40 |   color: #DD4B39;
 41 | }
 42 | #_vsaq_body fieldset fieldset>legend {
 43 |   font-size: 16px;
 44 |   color: #00549A;
 45 | }
 46 | #_vsaq_body fieldset fieldset fieldset>legend {
 47 |   font-size: 14px;
 48 |   font-weight: bold;
 49 |   color: #4173C9;
 50 | }
 51 | #_vsaq_body legend {
 52 |   padding: 0 6px;
 53 |   margin-bottom: 16px;
 54 |   display: block !important; /* override maia.css display:none; of legend */
 55 | }
 56 | #_vsaq_body ul {
 57 |   list-style-type: disc;
 58 |   margin-bottom: 18px;
 59 | }
 60 | #_vsaq_body li {
 61 |   margin-left: 30px;
 62 |   margin-bottom: 0px;
 63 |   padding-left: 20px;
 64 |   text-indent: -20px;
 65 | }
 66 | #_vsaq_butterbar {
 67 |   background-color: #F9BECA;
 68 |   text-align: center;
 69 |   z-index: 999;
 70 |   padding: 0px 4px 0px 4px;
 71 |   font-size: 12px;
 72 | }
 73 | #_vsaq_sections_header {
 74 |   padding: 3px 3px 3px 3px;
 75 |   font-size: 16px;
 76 |   font-weight: bold;
 77 |   color: #1155CC;
 78 | }
 79 | .vsaq-section-status {
 80 |   font-size: 10px;
 81 | }
 82 | .vsaq-spacer {
 83 |   margin-top: 24px;
 84 |   border: 0px;
 85 | }
 86 | .vsaq-question-title {
 87 |   margin-top: 18px;
 88 |   margin-bottom: 4px;
 89 | }
 90 | .vsaq-yesno-block {
 91 |   margin-bottom: 12px;
 92 | }
 93 | .vsaq-info-item {
 94 |   margin-bottom: 24px;
 95 |   font-weight: bold;
 96 | }
 97 | .vsaq-info-item p {
 98 |   margin-bottom: 16px;
 99 | }
100 | .vsaq-question-title {
101 |   font-weight: bold;
102 | }
103 | .vsaq-bubble-block {
104 |   position: relative;
105 |   padding-bottom: 6px;
106 | }
107 | .vsaq-bubble-medium {
108 |   background-color: #F9EDBE !important;
109 | }
110 | .vsaq-bubble-high {
111 |   background-color: #F9CFBE !important;
112 | }
113 | .vsaq-bubble-critical {
114 |   background-color: #F9BECA !important;
115 | }
116 | .vsaq-checkbox-item label {
117 |   font-weight: normal;
118 |   text-indent: -18px;
119 |   padding-left: 20px;
120 |   margin-left: 20px;
121 | }
122 | .vsaq-radio-item label {
123 |   font-weight: normal;
124 |   text-indent: -18px;
125 |   padding-left: 20px;
126 |   margin-left: 20px;
127 | }
128 | .vsaq-yesno-block label {
129 |   font-weight: normal;
130 |   text-indent: -18px;
131 |   padding-left: 20px;
132 |   margin-left: 20px;
133 | }
134 | .vsaq-line {
135 |   width: 100% !important;
136 |   -webkit-box-sizing: border-box;
137 |   -moz-box-sizing: border-box;
138 |   box-sizing: border-box;
139 | }
140 | .vsaq-box {
141 |   width: 100%;
142 |   -webkit-box-sizing: border-box;
143 |   -moz-box-sizing: border-box;
144 |   box-sizing: border-box;
145 | }
146 | .vsaq-compensating-desc {
147 |   margin-top: 18px;
148 |   margin-bottom: 16px;
149 | }
150 | .vsaq-invisible {
151 |   display: none;
152 | }
153 | .vsaq-group-item {
154 |   padding-bottom: 10px;
155 | }
156 | .vsaq-nowrap {
157 |   white-space: nowrap;
158 | }
159 | .vsaq-progressbar {
160 |   display: block;
161 |   width: 200px;
162 |   height: 16px;
163 |   margin: 0 0 2px;
164 |   float: right;
165 |   border: 1px solid #E5E5E5;
166 | }
167 | .vsaq-progressbar div.blue {
168 |   background: #8a8a8a;
169 |   height: 16px;
170 |   width: 75%;
171 |   text-align: center;
172 |   display: block;
173 |   float: left;
174 | }
175 | .vsaq-statsbox {
176 |   background: #F1F1F1;
177 |   max-width: 300px;
178 |   padding: 5px;
179 |   overflow: auto;
180 |   font-size: 10px;
181 |   margin: 8px 0px 8px 0px;
182 | }
183 | .vsaq-stats-title {
184 |   color: #a1a1a1;
185 |   font-weight: bold;
186 | }
187 | .vsaq-statsbox label {
188 |   float: left;
189 |   padding-right: 5px;
190 |   display: block;
191 |   width: 80px;
192 | }
193 | .vsaq-statsitem {
194 |   clear: both;
195 |   display: block;
196 | }
197 | .vsaq-saved-status {
198 |   margin-left: 20px;
199 |   color: #666;
200 | }
201 | .vsaq-tooltip {
202 |   background-color: #F9EDBE;
203 |   border: 2px solid #8c8c8c;
204 |   margin: 5px;
205 |   padding: 20px;
206 |   width: 33%;
207 |   z-index: 999;
208 | }
209 | .vsaq-block-link {
210 |   font-size: 85%;
211 |   padding-left: 5px;
212 |   color: black;
213 |   font-weight: initial;
214 | }
215 | a.vsaq-block-link:visited,
216 | a.vsaq-block-link:hover,
217 | a.vsaq-block-link:active {
218 |   color: black;
219 |   text-decoration: none;
220 | }
221 | 
222 | .vsaq-normal-weight {
223 |   font-weight: normal;
224 | }
225 | 
226 | #vsaq_todo_ul {
227 |   list-style-type: none !important;
228 |   margin-left: -30px;
229 | }
230 | 
231 | /**
232 |  * Closure styles that need adjustment.
233 |  */
234 | .ac-renderer {
235 |   z-index: 110;
236 | }
237 | 
238 | .tooltip-link {
239 |   border-bottom: 1px dotted;
240 | }
241 | 
242 | .butterbar-container {
243 |   top: 0px;
244 |   width: 75%;
245 |   position: fixed;
246 |   left: 50%;
247 |   margin: 0 0 0 -37.5%;
248 | }
249 | 
250 | .butterbar {
251 |   display: inline-block;
252 | }
253 | 
254 | table.borders,
255 | div.table {
256 |   background: #fff;
257 |   border-collapse: collapse;
258 |   border-bottom: 1px solid #e5e5e5;
259 |   border-top: 1px solid #e5e5e5;
260 |   margin: 0 0 20px 0;
261 |   padding: 0;
262 |   width: 100%;
263 | }
264 | 
265 | table.borders.hover tbody tr:hover td,
266 | .table div.table-item:hover {
267 |   background: #fffed6;
268 |   cursor: default;
269 | }
270 | 
271 | table.borders tr.alt td {
272 |   background: #c5d6f1;
273 | }
274 | 
275 | table.borders thead .meta {
276 |   color: #555;
277 |   float: right;
278 |   font-size: 11px;
279 |   font-weight: normal;
280 |   padding: 2px 4px;
281 | }
282 | 
283 | table.borders thead.sub td {
284 |   background: #d4dbde;
285 | }
286 | 
287 | table.borders thead td,
288 | .table div.table-header {
289 |   background: #f1f1f1;
290 |   font-weight: bold;
291 |   margin: 0;
292 | }
293 | 
294 | table.borders td,
295 | table.borders th {
296 |   border-bottom: 1px solid #e5e5e5;
297 |   padding: 5px;
298 | }
299 | 
300 | div.table-row {
301 |   overflow: hidden;
302 | }
303 | 
304 | table.borders tr td:first-child {
305 |   width: auto;
306 | }
307 | 
308 | table.borders tr td img.icon {
309 |   border: 0;
310 |   margin-right: 8px;
311 |   vertical-align: middle;
312 | }
313 | 
314 | table.borders tr td a img {
315 |   border: 0;
316 |   margin-top: -2px;
317 |   vertical-align: middle;
318 | }
319 | 
320 | table.borders thead tr th {
321 |   background: #f1f1f1;
322 |   text-align: left;
323 |   cursor: pointer;
324 | }
325 | 
326 | .little-meta {
327 |   color: #666;
328 |   font-size: 11px;
329 | }
330 | 
331 | body .loading-content {
332 |   display: none;
333 | }
334 | 
335 | body.done-loading .loading-mask {
336 |   display: none;
337 | }
338 | 
339 | body.done-loading .loading-content {
340 |   display: inherit;
341 | }
342 | 
343 | body .loading-mask {
344 |   font-size: 18px;
345 | }
346 | 
347 | /**
348 |  * Reduce space between navigation and page content
349 |  */
350 | #maia-nav-x.maia-complex+#maia-main {
351 |   margin-top: 5px !important;
352 | }
353 | 
354 | 


--------------------------------------------------------------------------------
/vsaq/static/questionnaire/tipitem.js:
--------------------------------------------------------------------------------
  1 | /**
  2 |  * @license
  3 |  * Copyright 2016 Google Inc. All rights reserved.
  4 |  * Licensed under the Apache License, Version 2.0 (the "License");
  5 |  * you may not use this file except in compliance with the License.
  6 |  * You may obtain a copy of the License at
  7 |  *
  8 |  *   http://www.apache.org/licenses/LICENSE-2.0
  9 |  *
 10 |  * Unless required by applicable law or agreed to in writing, software
 11 |  * distributed under the License is distributed on an "AS IS" BASIS,
 12 |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13 |  * See the License for the specific language governing permissions and
 14 |  * limitations under the License.
 15 |  */
 16 | 
 17 | /**
 18 |  * @fileoverview Questionnaire tip items provide advice to the user.
 19 |  */
 20 | 
 21 | goog.provide('vsaq.questionnaire.items.TipItem');
 22 | 
 23 | goog.require('goog.dom');
 24 | goog.require('goog.events');
 25 | goog.require('goog.events.EventType');
 26 | goog.require('goog.soy');
 27 | goog.require('goog.string');
 28 | goog.require('vsaq.questionnaire.items.ParseError');
 29 | goog.require('vsaq.questionnaire.items.ValueItem');
 30 | goog.require('vsaq.questionnaire.templates');
 31 | goog.require('vsaq.questionnaire.utils');
 32 | 
 33 | 
 34 | 
 35 | /**
 36 |  * A tip that provides advice to the user. If {@code clarification} is set to
 37 |  * true, additionally a text area is shown to the user where they can provide
 38 |  * additional information.
 39 |  * @param {string} id An ID uniquely identifying the tip.
 40 |  * @param {?string} conditions A string containing conditions which must be met
 41 |  *     for the item to be visible to the user.
 42 |  * @param {string} text The caption to show next to the radio button.
 43 |  * @param {boolean} warn If set to true, the tip will be shown with an alarming
 44 |  *     background color, and feature a similarly alarming caption.
 45 |  * @param {string=} opt_severity Severity of the issue.
 46 |  * @param {string=} opt_clarification If provided, an additional text area is
 47 |  *     shown to the user, where they can provide additional clarification.
 48 |  * @param {string=} opt_name Name of the issue.
 49 |  * @param {string=} opt_todo Todo list entry associated with the tip.
 50 |  * @param {string=} opt_customTitle Title of bubble defined in JSON.
 51 |  * @extends {vsaq.questionnaire.items.ValueItem}
 52 |  * @constructor
 53 |  */
 54 | vsaq.questionnaire.items.TipItem = function(id, conditions, text, warn,
 55 |     opt_severity, opt_clarification, opt_name, opt_todo, opt_customTitle) {
 56 |   goog.base(this, id, conditions, text);
 57 | 
 58 |   /**
 59 |    * Indicating whether the tip is a warning or only informational.
 60 |    * @type {boolean}
 61 |    */
 62 |   this.warn = warn;
 63 |   var propertyInformation = {
 64 |     nameInClass: 'warn',
 65 |     defaultValues: {
 66 |       'true' : 'yes'
 67 |     }
 68 |   };
 69 |   this.addPropertyInformation('warn', propertyInformation);
 70 | 
 71 |   /**
 72 |    * Name of the issue.
 73 |    * @type {string}
 74 |    */
 75 |   this.name = goog.string.makeSafe(opt_name);
 76 |   propertyInformation = {
 77 |     nameInClass: 'name',
 78 |     metadata: true
 79 |   };
 80 |   this.addPropertyInformation('name', propertyInformation);
 81 | 
 82 |   /**
 83 |    * A request for clarification.
 84 |    * @type {string}
 85 |    */
 86 |   this.clarification = goog.string.makeSafe(opt_clarification);
 87 |   propertyInformation = {nameInClass: 'clarification'};
 88 |   this.addPropertyInformation('why', propertyInformation);
 89 | 
 90 |   /**
 91 |    * A todo list entry associated with the tip.
 92 |    * @type {string}
 93 |    */
 94 |   this.todo = goog.string.makeSafe(opt_todo);
 95 |   propertyInformation = {
 96 |     nameInClass: 'todo',
 97 |     metadata: true
 98 |   };
 99 |   this.addPropertyInformation('todo', propertyInformation);
100 | 
101 |   /**
102 |    * Optional custom title of bubble. Any string is valid.
103 |    * @type {string}
104 |    */
105 |   this.customTitle = goog.string.makeSafe(opt_customTitle);
106 |   propertyInformation = {nameInClass: 'customTitle'};
107 |   this.addPropertyInformation('customTitle', propertyInformation);
108 | 
109 |   /**
110 |    * The severity of the issue. Valid values are medium, high and critical.
111 |    * @type {?string}
112 |    */
113 |   this.severity = opt_severity || '';
114 |   propertyInformation = {
115 |     nameInClass: 'severity',
116 |     defaultValues: {
117 |       medium: 'medium',
118 |       high: 'high',
119 |       critical: 'critical'
120 |     },
121 |     dependencies: {
122 |       warn: 'yes'
123 |     }
124 |   };
125 |   this.addPropertyInformation('severity', propertyInformation);
126 | 
127 |   /**
128 |    * The text area where the user can provide clarification.
129 |    * @type {HTMLTextAreaElement}
130 |    * @private
131 |    */
132 |   this.textArea_;
133 | 
134 |   this.render();
135 | };
136 | goog.inherits(vsaq.questionnaire.items.TipItem,
137 |               vsaq.questionnaire.items.ValueItem);
138 | 
139 | 
140 | /**
141 |  * Render the HTML for this item.
142 |  */
143 | vsaq.questionnaire.items.TipItem.prototype.render = function() {
144 |   var oldNode = this.container;
145 |   this.container = goog.soy.renderAsElement(vsaq.questionnaire.templates.bubble,
146 |       {
147 |         contentHtml: soydata.VERY_UNSAFE.ordainSanitizedHtml(this.text),
148 |         id: this.id,
149 |         whyHtml: (
150 |             this.clarification ?
151 |             soydata.VERY_UNSAFE.ordainSanitizedHtml(this.clarification) : ''),
152 |         isWarning: this.warn,
153 |         severity: this.severity,
154 |         customTitle: this.customTitle
155 |       });
156 |   goog.dom.replaceNode(this.container, oldNode);
157 | 
158 |   if (this.clarification) {
159 |     this.textArea_ = /** @type {HTMLTextAreaElement} */
160 |         (vsaq.questionnaire.utils.findById(this.container, this.id));
161 |     goog.events.listen(
162 |         this.textArea_,
163 |         [goog.events.EventType.KEYUP, goog.events.EventType.CHANGE],
164 |         this.answerChanged,
165 |         true,
166 |         this);
167 |   }
168 | };
169 | 
170 | 
171 | /**
172 |  * Type of the question. This is used to distinguish questions in serialized
173 |  * format.
174 |  * @type {string}
175 |  * @const
176 |  */
177 | vsaq.questionnaire.items.TipItem.TYPE = 'tip';
178 | 
179 | 
180 | /**
181 |  * Parses TipItems. If the topmost item in the passed Array is an a
182 |  * TipItem, it is consumed and a TipItem instance is returned.
183 |  * If the topmost item is not a TipItem, an exception is thrown.
184 |  * @param {!Array.} questionStack Array of serialized
185 |  *     questionnaire Items.
186 |  * @return {!vsaq.questionnaire.items.TipItem} The parsed TipItem.
187 |  */
188 | vsaq.questionnaire.items.TipItem.parse = function(questionStack) {
189 |   var item = questionStack.shift();
190 |   if (item.type != vsaq.questionnaire.items.TipItem.TYPE)
191 |     throw new vsaq.questionnaire.items.ParseError('Wrong parser chosen.');
192 | 
193 |   var isWarn = goog.string.makeSafe(item.warn) == 'yes';
194 |   return new vsaq.questionnaire.items.TipItem(item.id, item.cond, item.text,
195 |       isWarn, item.severity, item.why, item.name, item.todo, item.customTitle);
196 | };
197 | 
198 | 
199 | /** @inheritDoc */
200 | vsaq.questionnaire.items.TipItem.prototype.setReadOnly = function(readOnly) {
201 |   if (this.textArea_)
202 |     this.textArea_.readOnly = readOnly;
203 | };
204 | 
205 | 
206 | /** @inheritDoc */
207 | vsaq.questionnaire.items.TipItem.prototype.getValue = function() {
208 |   if (this.textArea_)
209 |     return this.textArea_.value;
210 | 
211 |   return null;
212 | };
213 | 
214 | 
215 | /** @inheritDoc */
216 | vsaq.questionnaire.items.TipItem.prototype.setInternalValue =
217 |     function(value) {
218 |   if (this.textArea_)
219 |     this.textArea_.value = /** @type {string} */ (value);
220 | };
221 | 
222 | 
223 | /** @inheritDoc */
224 | vsaq.questionnaire.items.TipItem.prototype.isAnswered = function() {
225 |   // TipItems are not mandatory to fill out, therefore always counting them as
226 |   // answered.
227 |   return true;
228 | };
229 | 


--------------------------------------------------------------------------------
/vsaq/static/questionnaire/uploaditem_test.js:
--------------------------------------------------------------------------------
  1 | /**
  2 |  * @license
  3 |  * Copyright 2016 Google Inc. All rights reserved.
  4 |  * Licensed under the Apache License, Version 2.0 (the "License");
  5 |  * you may not use this file except in compliance with the License.
  6 |  * You may obtain a copy of the License at
  7 |  *
  8 |  *   http://www.apache.org/licenses/LICENSE-2.0
  9 |  *
 10 |  * Unless required by applicable law or agreed to in writing, software
 11 |  * distributed under the License is distributed on an "AS IS" BASIS,
 12 |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13 |  * See the License for the specific language governing permissions and
 14 |  * limitations under the License.
 15 |  */
 16 | 
 17 | /**
 18 |  * @fileoverview Tests for vsaq.questionnaire.items.UploadItem.
 19 |  */
 20 | 
 21 | goog.provide('vsaq.questionnaire.items.UploadItemTests');
 22 | goog.setTestOnly('vsaq.questionnaire.items.UploadItemTests');
 23 | 
 24 | goog.require('goog.dom');
 25 | goog.require('goog.dom.TagName');
 26 | goog.require('goog.net.IframeIo');
 27 | goog.require('goog.net.XhrIo');
 28 | goog.require('goog.style');
 29 | goog.require('goog.testing.MockControl');
 30 | goog.require('goog.testing.PropertyReplacer');
 31 | goog.require('goog.testing.events');
 32 | goog.require('goog.testing.jsunit');
 33 | goog.require('goog.testing.mockmatchers');
 34 | goog.require('goog.testing.net.XhrIo');
 35 | goog.require('vsaq.questionnaire.items.UploadItem');
 36 | 
 37 | var CAPTION = 'uploaditem_caption';
 38 | var ID = 'uploaditem_id';
 39 | var VALUE = 'fileid|filename';
 40 | 
 41 | var upload;
 42 | 
 43 | var stubs = new goog.testing.PropertyReplacer();
 44 | var mock;
 45 | 
 46 | 
 47 | /**
 48 |  * Initializes variables used by all tests.
 49 |  */
 50 | function setUp() {
 51 |   stubs.replace(goog.net.XhrIo, 'send', goog.testing.net.XhrIo.send);
 52 |   mock = new goog.testing.MockControl();
 53 |   upload = new vsaq.questionnaire.items.UploadItem(ID, [], CAPTION);
 54 | }
 55 | 
 56 | 
 57 | /**
 58 |  * Resets mocks after each test.
 59 |  */
 60 | function tearDown() {
 61 |   goog.testing.net.XhrIo.cleanup();
 62 |   stubs.reset();
 63 | }
 64 | 
 65 | 
 66 | /**
 67 |  * Tests whether UploadItems are rendered correctly.
 68 |  */
 69 | function testUploadItem() {
 70 |   var el = upload.container;
 71 | 
 72 |   assertEquals(goog.dom.TagName.DIV, el.tagName);
 73 |   var desc = goog.dom.getFirstElementChild(el);
 74 |   assertEquals(CAPTION, goog.dom.getTextContent(desc));
 75 |   var fileTypeDesc = goog.dom.getNextElementSibling(desc);
 76 |   assertEquals(goog.dom.TagName.DIV, fileTypeDesc.tagName);
 77 |   var form = goog.dom.getNextElementSibling(fileTypeDesc);
 78 |   assertEquals(upload.form_, form);
 79 |   assertEquals('multipart/form-data', form.enctype);
 80 |   var label = goog.dom.getFirstElementChild(form);
 81 |   assertEquals('uploaditem_id-label', label.id);
 82 |   assertEquals(upload.label_, label);
 83 |   var input = goog.dom.getNextElementSibling(label);
 84 |   assertEquals(goog.dom.TagName.INPUT, input.tagName);
 85 |   assertEquals('uploaditem_id-file', input.id);
 86 |   var dlLink = goog.dom.getNextElementSibling(input);
 87 |   assertEquals(goog.dom.TagName.A, dlLink.tagName);
 88 |   assertEquals('uploaditem_id-download', dlLink.id);
 89 |   var delLink = goog.dom.getNextElementSibling(dlLink);
 90 |   assertEquals(goog.dom.TagName.A, delLink.tagName);
 91 |   assertEquals('uploaditem_id-delete', delLink.id);
 92 | }
 93 | 
 94 | 
 95 | /**
 96 |  * Tests setting and retrieving the value of the item.
 97 |  */
 98 | function testUploadItemSetGetValue() {
 99 |   assertEquals('', upload.getValue());
100 |   upload.setValue(VALUE);
101 |   assertEquals(VALUE, upload.getValue());
102 |   assertEquals('filename', upload.filename_);
103 |   assertEquals('fileid', upload.fileId_);
104 |   assertEquals(false, goog.style.isElementShown(upload.fileInput_));
105 |   assertEquals('Uploaded file: filename',
106 |       goog.dom.getTextContent(upload.label_));
107 |   assertEquals(true, goog.style.isElementShown(upload.deleteLink_));
108 |   assertEquals(true, goog.style.isElementShown(upload.downloadLink_));
109 | 
110 |   upload.setValue('');
111 |   assertEquals('', upload.getValue());
112 |   assertEquals('', upload.filename_);
113 |   assertEquals('', upload.fileId_);
114 |   assertEquals(true, goog.style.isElementShown(upload.fileInput_));
115 |   assertEquals('', goog.dom.getTextContent(upload.label_));
116 |   assertEquals(false, goog.style.isElementShown(upload.deleteLink_));
117 |   assertEquals(false, goog.style.isElementShown(upload.downloadLink_));
118 | }
119 | 
120 | 
121 | /**
122 |  * Tests parsing of UploadItems.
123 |  */
124 | function testUploadItemParse() {
125 |   var testStack = [{
126 |     'type': 'upload',
127 |     'text': CAPTION,
128 |     'id': ID
129 |   }];
130 |   upload = vsaq.questionnaire.items.UploadItem.parse(testStack);
131 |   assert(upload instanceof vsaq.questionnaire.items.UploadItem);
132 |   assertEquals(ID, upload.id);
133 |   assertEquals(CAPTION, upload.text);
134 |   assertEquals(0, testStack.length);
135 | }
136 | 
137 | 
138 | /**
139 |  * Tests clicking the delete link.
140 |  */
141 | function testUploadItemDeleteLink() {
142 |   upload.setValue(VALUE);
143 | 
144 |   mock.createMethodMock(upload, 'answerChanged');
145 |   mock.createMethodMock(window, 'confirm');
146 | 
147 |   window.confirm(goog.testing.mockmatchers.isString).$returns(true);
148 |   upload.answerChanged();
149 | 
150 |   mock.$replayAll();
151 |   goog.testing.events.fireClickEvent(upload.deleteLink_);
152 |   mock.$verifyAll();
153 | 
154 |   assertEquals('', upload.getValue());
155 |   assertEquals('', upload.filename_);
156 |   assertEquals('', upload.fileId_);
157 |   assertEquals(true, goog.style.isElementShown(upload.fileInput_));
158 |   assertEquals('', goog.dom.getTextContent(upload.label_));
159 |   assertEquals(false, goog.style.isElementShown(upload.deleteLink_));
160 |   assertEquals(false, goog.style.isElementShown(upload.downloadLink_));
161 | }
162 | 
163 | 
164 | /**
165 |  * Tests handling of uploads.
166 |  */
167 | function testUploadItemHandleUpload() {
168 |   var mockIframeIo = mock.createLooseMock(goog.net.IframeIo, true);
169 |   var mockIframeIoConstructor = mock.createConstructorMock(goog.net,
170 |       'IframeIo');
171 | 
172 |   // need to mock out fileInput_ as file input elements have a readonly value.
173 |   upload.fileInput_ = goog.dom.createDom('input');
174 |   upload.fileInput_.value = 'test.pdf';
175 | 
176 |   mockIframeIoConstructor().$returns(mockIframeIo);
177 |   mockIframeIo.sendFromForm(upload.form_);
178 | 
179 |   mock.$replayAll();
180 |   upload.handleUpload_();
181 | 
182 |   var xhrio = goog.testing.net.XhrIo.getSendInstances();
183 |   assertEquals(1, xhrio.length);
184 |   xhrio = xhrio[0];
185 | 
186 |   assertEquals('POST', xhrio.getLastMethod());
187 |   assertEquals('/ajax?f=GetUploadUrl', xhrio.getLastUri());
188 | 
189 |   xhrio.simulateResponse(200, '{"url": "/upload123"}');
190 |   mock.$verifyAll();
191 | 
192 |   assertEquals('Uploading...', goog.dom.getTextContent(upload.label_));
193 |   assertEquals(false, goog.style.isElementShown(upload.fileInput_));
194 | }
195 | 
196 | 
197 | /**
198 |  * Tests file uploads with an illegal extension.
199 |  */
200 | function testUploadItemHandleUploadIllegalExtension() {
201 |   // need to mock out fileInput_ as file input elements have a readonly value.
202 |   upload.fileInput_ = goog.dom.createDom('input');
203 |   upload.fileInput_.value = 'test.illegal';
204 | 
205 |   upload.handleUpload_();
206 | 
207 |   assertEquals('Invalid extension.', goog.dom.getTextContent(upload.label_));
208 |   assertEquals(true, goog.style.isElementShown(upload.fileInput_));
209 | }
210 | 
211 | 
212 | /**
213 |  * Tests handling of completed uploads.
214 |  */
215 | function testUploadItemHandleCompletedUpload() {
216 |   var mockEvent = {};
217 |   mockEvent.target = {};
218 |   mockEvent.target.getResponseJson = function() {
219 |     var mockReturn = {};
220 |     mockReturn['filename'] = 'filename';
221 |     mockReturn['fileId'] = 'fileid';
222 |     return mockReturn;
223 |   };
224 | 
225 |   mock.createMethodMock(upload, 'answerChanged');
226 |   upload.answerChanged();
227 | 
228 |   mock.$replayAll();
229 |   upload.handleCompletedUpload_(mockEvent);
230 |   mock.$verifyAll();
231 | 
232 |   assertEquals('filename', upload.filename_);
233 |   assertEquals('fileid', upload.fileId_);
234 |   assertEquals('Uploaded file: filename',
235 |       goog.dom.getTextContent(upload.label_));
236 |   assertEquals(true, goog.style.isElementShown(upload.deleteLink_));
237 |   assertEquals(true, goog.style.isElementShown(upload.downloadLink_));
238 | }
239 | 


--------------------------------------------------------------------------------
/vsaq/static/questionnaire/uploaditem.js:
--------------------------------------------------------------------------------
  1 | /**
  2 |  * @license
  3 |  * Copyright 2016 Google Inc. All rights reserved.
  4 |  * Licensed under the Apache License, Version 2.0 (the "License");
  5 |  * you may not use this file except in compliance with the License.
  6 |  * You may obtain a copy of the License at
  7 |  *
  8 |  *   http://www.apache.org/licenses/LICENSE-2.0
  9 |  *
 10 |  * Unless required by applicable law or agreed to in writing, software
 11 |  * distributed under the License is distributed on an "AS IS" BASIS,
 12 |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13 |  * See the License for the specific language governing permissions and
 14 |  * limitations under the License.
 15 |  */
 16 | 
 17 | /**
 18 |  * @fileoverview A questionnaire item with a file upload field.
 19 |  */
 20 | 
 21 | goog.provide('vsaq.questionnaire.items.UploadItem');
 22 | 
 23 | goog.require('goog.array');
 24 | goog.require('goog.dom');
 25 | goog.require('goog.events');
 26 | goog.require('goog.events.EventType');
 27 | goog.require('goog.net.EventType');
 28 | goog.require('goog.net.IframeIo');
 29 | goog.require('goog.net.XhrIo');
 30 | goog.require('goog.soy');
 31 | goog.require('goog.string');
 32 | goog.require('goog.string.format');
 33 | goog.require('goog.style');
 34 | goog.require('vsaq.questionnaire.items.ParseError');
 35 | goog.require('vsaq.questionnaire.items.ValueItem');
 36 | goog.require('vsaq.questionnaire.templates');
 37 | goog.require('vsaq.questionnaire.utils');
 38 | 
 39 | 
 40 | 
 41 | /**
 42 |  * A question that allows the user to answer by uploading a file
 43 |  * @param {string} id An ID uniquely identifying the question.
 44 |  * @param {?string} conditions A string containing conditions which must be met
 45 |  *     for the item to be visible to the user.
 46 |  * @param {string} caption The caption to show above the file upload.
 47 |  * @extends {vsaq.questionnaire.items.ValueItem}
 48 |  * @constructor
 49 |  */
 50 | vsaq.questionnaire.items.UploadItem = function(id, conditions, caption) {
 51 |   goog.base(this, id, conditions, caption);
 52 | 
 53 |   /**
 54 |    * The form through which the file is uploaded.
 55 |    * @type {!HTMLFormElement}
 56 |    * @private
 57 |    */
 58 |   this.form_;
 59 | 
 60 |   /**
 61 |    * The label next to the file upload. This is used to show the filename of the
 62 |    * currently submitted file.
 63 |    * @type {!HTMLSpanElement}
 64 |    * @private
 65 |    */
 66 |   this.label_;
 67 | 
 68 |   /**
 69 |    * A link that allows the user to delete the currently submitted file.
 70 |    * @type {!HTMLAnchorElement}
 71 |    * @private
 72 |    */
 73 |   this.deleteLink_;
 74 | 
 75 |   /**
 76 |    * A link that allows the user to download the currently submitted file.
 77 |    * @type {!HTMLAnchorElement}
 78 |    * @private
 79 |    */
 80 |   this.downloadLink_;
 81 | 
 82 |   /**
 83 |    * The name of the currently submitted file.
 84 |    * @type {string}
 85 |    * @private
 86 |    */
 87 |   this.filename_ = '';
 88 | 
 89 |   /**
 90 |    * The URL to the currently submitted file.
 91 |    * @type {string}
 92 |    * @private
 93 |    */
 94 |   this.fileId_ = '';
 95 | 
 96 |   /**
 97 |    * The html input element where the user can answer the question.
 98 |    * @type {!HTMLInputElement}
 99 |    * @private
100 |    */
101 |   this.fileInput_;
102 | 
103 |   this.render();
104 | };
105 | goog.inherits(vsaq.questionnaire.items.UploadItem,
106 |               vsaq.questionnaire.items.ValueItem);
107 | 
108 | 
109 | /**
110 |  * Render the HTML for this item.
111 |  */
112 | vsaq.questionnaire.items.UploadItem.prototype.render = function() {
113 |   var oldNode = this.container;
114 |   this.container = goog.soy.renderAsElement(vsaq.questionnaire.templates.upload,
115 |       {
116 |         id: this.id,
117 |         captionHtml: soydata.VERY_UNSAFE.ordainSanitizedHtml(this.text)
118 |       });
119 |   goog.dom.replaceNode(this.container, oldNode);
120 | 
121 |   this.form_ = /** @type {!HTMLFormElement} */
122 |       (vsaq.questionnaire.utils.findById(this.container, this.id + '-form'));
123 |   this.label_ = /** @type {!HTMLSpanElement} */
124 |       (vsaq.questionnaire.utils.findById(this.container, this.id + '-label'));
125 |   this.deleteLink_ = /** @type {!HTMLAnchorElement} */
126 |       (vsaq.questionnaire.utils.findById(this.container, this.id + '-delete'));
127 |   goog.style.setElementShown(this.deleteLink_, false);
128 |   this.downloadLink_ = /** @type {!HTMLAnchorElement} */ (
129 |       vsaq.questionnaire.utils.findById(this.container, this.id + '-download'));
130 |   goog.style.setElementShown(this.downloadLink_, false);
131 |   this.fileInput_ = /** @type {!HTMLInputElement} */
132 |       (vsaq.questionnaire.utils.findById(this.container, this.id + '-file'));
133 |   goog.events.listen(
134 |       this.downloadLink_, goog.events.EventType.CLICK,
135 |       function(e) {
136 |         e.target.href = goog.string.format('/download/%s?q=%s', this.fileId_,
137 |             encodeURIComponent(document.location.pathname));
138 |       }, true, this);
139 |   goog.events.listen(
140 |       this.deleteLink_, goog.events.EventType.CLICK,
141 |       function(e) {
142 |         if (confirm('Are you sure you would like to delete this file?'))
143 |           this.deleteFile_(e);
144 |       },
145 |       true, this);
146 |   goog.events.listen(
147 |       vsaq.questionnaire.utils.findById(this.container, this.id + '-file'),
148 |       goog.events.EventType.CHANGE,
149 |       this.handleUpload_,
150 |       true, this);
151 | };
152 | 
153 | 
154 | /**
155 |  * Type of the question. This is used to distinguish questions in serialized
156 |  * format.
157 |  * @type {string}
158 |  * @const
159 |  */
160 | vsaq.questionnaire.items.UploadItem.TYPE = 'upload';
161 | 
162 | 
163 | /**
164 |  * A URL from where to get the upload URL.
165 |  */
166 | vsaq.questionnaire.items.UploadItem.FETCH_URL_URL = '/ajax?f=GetUploadUrl';
167 | 
168 | 
169 | /**
170 |  * A list of file extensions that are accepted by the server.
171 |  */
172 | vsaq.questionnaire.items.UploadItem.VALID_EXTENSIONS =
173 |     ['pdf', 'odt', 'doc', 'docx'];
174 | 
175 | 
176 | /**
177 |  * Uploads the file after a user selected one.
178 |  * @private
179 |  */
180 | vsaq.questionnaire.items.UploadItem.prototype.handleUpload_ = function() {
181 |   var file = this.fileInput_.value;
182 |   if (!file) return;
183 | 
184 |   var ext = file.substring(file.lastIndexOf('.') + 1).toLowerCase();
185 |   if (!goog.array.contains(
186 |       vsaq.questionnaire.items.UploadItem.VALID_EXTENSIONS, ext)) {
187 |     this.deleteFile_();
188 |     goog.dom.setTextContent(this.label_, 'Invalid extension.');
189 |     return;
190 |   }
191 | 
192 |   goog.net.XhrIo.send(vsaq.questionnaire.items.UploadItem.FETCH_URL_URL,
193 |       goog.bind(function(e) {
194 |         if (e.target.isSuccess()) {
195 |           var body = e.target.getResponseJson();
196 |           this.form_.action = body['url'];
197 |           var io = new goog.net.IframeIo();
198 |           goog.events.listen(io, [goog.net.EventType.COMPLETE],
199 |               this.handleCompletedUpload_, true, this);
200 |           io.sendFromForm(this.form_);
201 |           goog.dom.setTextContent(this.label_, 'Uploading...');
202 |           goog.style.setElementShown(this.fileInput_, false);
203 |         }
204 |       }, this), 'POST', 'q=' + encodeURIComponent(document.location.pathname));
205 | };
206 | 
207 | 
208 | /**
209 |  * Handles callback from IframeIo (when an upload is complete).
210 |  * @param {goog.events.Event} e The COMPLETED event from goog.net.IframeIo.
211 |  * @private
212 |  */
213 | vsaq.questionnaire.items.UploadItem.prototype.handleCompletedUpload_ =
214 |     function(e) {
215 |   var response = {};
216 |   try {
217 |     response = e.target.getResponseJson();
218 |   } catch (err) {}
219 |   if (response['filename'] && response['fileId']) {
220 |     this.setValue(response['fileId'] + '|' + response['filename']);
221 |   } else {
222 |     this.deleteFile_();
223 |     var error = response['error'] || 'Error. Upload unsuccessful.';
224 |     goog.dom.setTextContent(this.label_, error);
225 |   }
226 | };
227 | 
228 | 
229 | /**
230 |  * Removes the currently uploaded file.
231 |  * @param {goog.events.Event=} opt_event The event that triggered the function.
232 |  * @private
233 |  */
234 | vsaq.questionnaire.items.UploadItem.prototype.deleteFile_ = function(
235 |     opt_event) {
236 |   this.filename_ = '';
237 |   this.fileId_ = '';
238 |   this.form_.reset();
239 |   goog.dom.setTextContent(this.label_, '');
240 |   goog.style.setElementShown(this.deleteLink_, false);
241 |   goog.style.setElementShown(this.downloadLink_, false);
242 |   goog.style.setElementShown(this.fileInput_, true);
243 |   if (opt_event)
244 |     this.answerChanged();
245 | };
246 | 
247 | 
248 | /**
249 |  * Parses UploadItems. If the topmost item in the passed Array is an a
250 |  * UploadItem, it is consumed and a UploadItem instance is returned.
251 |  * If the topmost item is not a UploadItem, an exception is thrown.
252 |  * @param {!Array.} questionStack Array of serialized
253 |  *     questionnaire Items.
254 |  * @return {!vsaq.questionnaire.items.UploadItem} The parsed UploadItem.
255 |  */
256 | vsaq.questionnaire.items.UploadItem.parse = function(questionStack) {
257 |   var item = questionStack.shift();
258 |   if (item.type != vsaq.questionnaire.items.UploadItem.TYPE)
259 |     throw new vsaq.questionnaire.items.ParseError('Wrong parser chosen.');
260 | 
261 |   return new vsaq.questionnaire.items.UploadItem(item.id, item.cond, item.text);
262 | };
263 | 
264 | 
265 | /** @inheritDoc */
266 | vsaq.questionnaire.items.UploadItem.prototype.setReadOnly = function(readOnly) {
267 |   this.fileInput_.readOnly = readOnly;
268 | };
269 | 
270 | 
271 | /** @inheritDoc */
272 | vsaq.questionnaire.items.UploadItem.prototype.getValue = function() {
273 |   if (this.fileId_ && this.filename_)
274 |     return this.fileId_ + '|' + this.filename_;
275 | 
276 |   return '';
277 | };
278 | 
279 | 
280 | /** @inheritDoc */
281 | vsaq.questionnaire.items.UploadItem.prototype.setInternalValue =
282 |     function(value) {
283 |   var parts = value.split('|');
284 |   if (parts.length == 2) {
285 |     this.fileId_ = parts[0];
286 |     this.filename_ = parts[1];
287 | 
288 |     goog.dom.setTextContent(this.label_, 'Uploaded file: ' + this.filename_);
289 |     this.downloadLink_.href = goog.string.format('/download/%s?q=%s',
290 |         this.fileId_, encodeURIComponent(document.location.pathname));
291 |     goog.style.setElementShown(this.deleteLink_, true);
292 |     goog.style.setElementShown(this.downloadLink_, true);
293 |     goog.style.setElementShown(this.fileInput_, false);
294 |     this.form_.reset();
295 |   } else if (!value) {
296 |     this.deleteFile_();
297 |   }
298 | };
299 | 
300 | 
301 | /** @inheritDoc */
302 | vsaq.questionnaire.items.UploadItem.prototype.isAnswered = function() {
303 |   return this.fileId_.length > 0;
304 | };
305 | 


--------------------------------------------------------------------------------
/do.sh:
--------------------------------------------------------------------------------
  1 | #!/usr/bin/env bash
  2 | # Copyright 2016 Google Inc. All rights reserved.
  3 | #
  4 | # Licensed under the Apache License, Version 2.0 (the "License");
  5 | # you may not use this file except in compliance with the License.
  6 | # You may obtain a copy of the License at
  7 | #
  8 | # http://www.apache.org/licenses/LICENSE-2.0
  9 | #
 10 | # Unless required by applicable law or agreed to in writing, software
 11 | # distributed under the License is distributed on an "AS IS" BASIS,
 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13 | # See the License for the specific language governing permissions and
 14 | # limitations under the License.
 15 | #
 16 | # @fileoverview Shell script to facilitate build-related tasks for VSAQ.
 17 | #
 18 | 
 19 | PYTHON_CMD="python"
 20 | JSCOMPILE_CMD="java -jar third_party/closure-compiler/build/compiler.jar --flagfile=compiler.flags"
 21 | CKSUM_CMD="cksum" # chosen because it's available on most Linux/OS X installations
 22 | BUILD_DIR="build"
 23 | BUILD_TPL_DIR="$BUILD_DIR/templates"
 24 | cd ${0%/*}
 25 | 
 26 | vsaq_assert_dependencies() {
 27 |   # Check if required binaries are present.
 28 |   type "$PYTHON_CMD" >/dev/null 2>&1 || { echo >&2 "Python is required to build VSAQ."; exit 1; }
 29 |   type ant >/dev/null 2>&1 || { echo >&2 "Ant is required to build VSAQ."; exit 1; }
 30 |   type java >/dev/null 2>&1 || { echo >&2 "Java is required to build VSAQ."; exit 1; }
 31 |   jversion=$(java -version 2>&1 | grep version | awk -F '"' '{print $2}')
 32 |   if [[ $jversion < "1.7" ]]; then
 33 |     echo "Java 1.7 or higher is required to build VSAQ."
 34 |     exit 1
 35 |   fi
 36 |   # Check if required files are present.
 37 |   files=(third_party/closure-library \
 38 |     third_party/closure-templates-compiler \
 39 |     third_party/closure-stylesheets/build/closure-stylesheets.jar \
 40 |     third_party/closure-compiler/build/compiler.jar \
 41 |     third_party/closure-compiler/contrib/externs/chrome_extensions.js \
 42 |   )
 43 |   for var in "${files[@]}"
 44 |   do
 45 |     if [ ! -e $var ]; then
 46 |       echo $var "not found"
 47 |       echo >&2 "Download libraries needed to build first. Use $0 install_deps."
 48 |       exit 1
 49 |     fi
 50 |   done
 51 |   echo "All dependencies met."
 52 | }
 53 | 
 54 | vsaq_get_file_cksum() {
 55 |   # creates a checksum of a given file spec
 56 |   # no-op if $CKSUM_CMD is not available
 57 |   type $CKSUM_CMD >/dev/null 2>&1 && (find "vsaq" -name $1 | sort | xargs $CKSUM_CMD | $CKSUM_CMD) || true
 58 | }
 59 | 
 60 | vsaq_build_templates() {
 61 |   vsaq_assert_dependencies
 62 |   set -e
 63 |   mkdir -p "$BUILD_TPL_DIR"
 64 |   rm -rf "$BUILD_TPL_DIR/*"
 65 |   # Compile soy templates
 66 |   echo "Compiling Soy templates..."
 67 |   rm -f "$BUILD_TPL_DIR/cksum"
 68 |   vsaq_get_file_cksum '*.soy' > "$BUILD_TPL_DIR/cksum"
 69 |   find "vsaq" -name '*.soy' -exec java -jar third_party/closure-templates-compiler/SoyToJsSrcCompiler.jar \
 70 |   --shouldProvideRequireSoyNamespaces --shouldGenerateJsdoc --shouldDeclareTopLevelNamespaces --srcs {} \
 71 |   --outputPathFormat "$BUILD_TPL_DIR/{INPUT_DIRECTORY}{INPUT_FILE_NAME}.js" \;
 72 |   echo "Done."
 73 | }
 74 | 
 75 | vsaq_assert_buildfiles() {
 76 |   if [ ! -d "$BUILD_DIR" ] || [ ! -f "$BUILD_DIR/vsaq.html" ]; then
 77 |     echo "Please build VSAQ first."
 78 |     exit 1
 79 |   fi
 80 | }
 81 | 
 82 | vsaq_assert_templates() {
 83 |   if [ ! -d $BUILD_TPL_DIR ]; then
 84 |     vsaq_build_templates
 85 |   else
 86 |     # If cmp is unavailable, just ignore the check, instead of exiting
 87 |     type cmp >/dev/null 2>&1 && (vsaq_get_file_cksum '*.soy' | cmp "$BUILD_TPL_DIR/cksum" - >/dev/null 2>&1) || true
 88 |     if [ -f "$BUILD_TPL_DIR/cksum" -a $? -eq 0 ] ; then
 89 |       echo "Using previous template build. Run ./do.sh clean if you want to rebuild the templates."
 90 |     else
 91 |       echo "Template files changed since last build. Rebuilding..."
 92 |       vsaq_build_templates
 93 |     fi
 94 |   fi
 95 | }
 96 | 
 97 | vsaq_assert_jsdeps() {
 98 |   if [ ! -f "$BUILD_DIR/deps.js" ]; then
 99 |     vsaq_generate_jsdeps
100 |   fi
101 | }
102 | 
103 | vsaq_build_closure_lib_() {
104 |   # $1 - Closure entry point
105 |   # $2 - Filename
106 |   # $3 - Additional source dir
107 |   # $4 - [debug|optimized]
108 |   ENTRY_POINT=$1
109 |   FNAME=$2
110 |   SRC_DIRS=( \
111 |     vsaq \
112 |     client_side_only_impl \
113 |     third_party/closure-library/closure/goog \
114 |     third_party/closure-library/third_party/closure/goog \
115 |     third_party/closure-templates-compiler )
116 |   if [ -d "$3" ]; then
117 |     SRC_DIRS+=("$3")
118 |   fi
119 |   jscompile_vsaq="$JSCOMPILE_CMD"
120 |   for var in "${SRC_DIRS[@]}"
121 |   do
122 |     jscompile_vsaq+=" --js='$var/**.js' --js='!$var/**_test.js' --js='!$var/**_perf.js'"
123 |   done
124 |   jscompile_vsaq+=" --js='!third_party/closure-library/closure/goog/demos/**.js'"
125 |   if [ "$4" == "debug" ]; then
126 |      jscompile_vsaq+=" --debug --formatting=PRETTY_PRINT -O WHITESPACE_ONLY"
127 |   elif [ "$4" == "optimized" ]; then
128 |      jscompile_vsaq+=" -O ADVANCED"
129 |   fi
130 |   echo -n "."
131 |   $jscompile_vsaq --closure_entry_point "$ENTRY_POINT" --js_output_file "$FNAME"
132 | }
133 | 
134 | vsaq_build_jsmodule() {
135 |   echo "Building JS module $1 into $BUILD_DIR/$1.js..."
136 |   vsaq_assert_dependencies
137 |   set -e
138 |   vsaq_assert_jsdeps
139 |   mkdir -p "$BUILD_DIR"
140 |   if [ "$2" == "debug" ]; then
141 |     echo "Debug mode enabled"
142 |   fi
143 |   vsaq_build_closure_lib_ $1 "$BUILD_DIR/$1.js" "" $2;
144 |   echo ""
145 |   echo "Done."
146 | }
147 | 
148 | vsaq_build() {
149 |   vsaq_assert_dependencies
150 |   set -e
151 |   vsaq_assert_jsdeps
152 |   vsaq_assert_templates
153 | 
154 |   echo "Building VSAQ app to $BUILD_DIR"
155 |   # compile javascript files
156 |   if [ "$1" == "debug" ]; then
157 |     echo "Debug mode enabled"
158 |   fi
159 |   echo "Compiling JS files..."
160 |   vsaq_build_closure_lib_ "vsaq" "$BUILD_DIR/vsaq_binary.js" "$BUILD_TPL_DIR" "$1"
161 | 
162 |   BUILD_DIR_STATIC="$BUILD_DIR/static"
163 |   mkdir -p "$BUILD_DIR_STATIC"
164 |   csscompile_vsaq="java -jar third_party/closure-stylesheets/build/closure-stylesheets.jar --allowed-non-standard-function color-stop"
165 |   echo "Compiling CSS files..."
166 |   $csscompile_vsaq "vsaq/static/vsaq_base.css" "vsaq/static/vsaq.css" > "$BUILD_DIR_STATIC/vsaq.css"
167 |   echo "Copying remaining static files..."
168 |   find "vsaq" -regex '.*.\(gif\|png\|ico\)$' -exec cp -f "{}" "$BUILD_DIR_STATIC" \;
169 |   echo "Copying main html files..."
170 |   find "client_side_only_impl" -regex .*.html -not -regex .*_test_dom.html -exec cp -f "{}" "$BUILD_DIR" \;
171 |   echo "Copying questionnaire files..."
172 |   cp -R "questionnaires" "$BUILD_DIR"
173 |   echo "Done."
174 | }
175 | 
176 | vsaq_build_prod() {
177 |  vsaq_build
178 |  rm -f "$BUILD_DIR/example.html"
179 |  rm -f "$BUILD_DIR/all_tests.html"
180 | }
181 | 
182 | vsaq_build_clean() {
183 |   echo "Cleaning all builds..."
184 |   rm -rfv "$BUILD_DIR"
185 |   echo "Done."
186 | }
187 | 
188 | vsaq_clean_deps() {
189 |   echo "Removing all build dependencies. Install them with ./do.sh install_deps."
190 |   rm -rfv lib
191 |   echo "Done."
192 | }
193 | 
194 | vsaq_install_deps() {
195 |   set -e
196 |   echo "Installing build dependencies..."
197 |   ./download-libs.sh
198 |   echo "Done."
199 | }
200 | 
201 | vsaq_generate_jsdeps() {
202 |   vsaq_assert_templates
203 |   $PYTHON_CMD third_party/closure-library/closure/bin/build/depswriter.py \
204 |     --root_with_prefix="build/templates/ build/templates/" \
205 |     --root_with_prefix="vsaq/ vsaq/" \
206 |     --root_with_prefix="third_party/closure-templates-compiler/ third_party/closure-templates-compiler/" \
207 |     > "$BUILD_DIR/deps.js"
208 | }
209 | 
210 | vsaq_run() {
211 |   vsaq_assert_buildfiles
212 |   vsaq_assert_templates
213 |   echo "Generating build/deps-runfiles.js file..."
214 |   mkdir -p "$BUILD_DIR"
215 |   $PYTHON_CMD third_party/closure-library/closure/bin/build/depswriter.py \
216 |     --root_with_prefix="build/templates/ ../../../build/templates/" \
217 |     --root_with_prefix="vsaq/ ../vsaq/" \
218 |     --root_with_prefix="third_party/closure-templates-compiler/ ../../../../third_party/closure-templates-compiler/" \
219 |     > "$BUILD_DIR/deps-runfiles.js"
220 | 
221 |   rm -f "$BUILD_DIR/all_tests.js"
222 |   echo "Starting the VSAQ server (Press Ctrl-C to stop)..."
223 |   $PYTHON_CMD vsaq_server.py $*
224 |   echo "Done."
225 | }
226 | 
227 | vsaq_lint() {
228 |   if [ -z `which gjslint` ]; then
229 |     echo "Closure Linter is not installed."
230 |     echo "Follow instructions at https://developers.google.com/closure/utilities/docs/linter_howto to install (root access is needed)."
231 |     RETVAL=1
232 |   else
233 |     echo "Running Closure Linter..."
234 |     if [ -z "$1" ]; then
235 |       ADDITIONAL="-r vsaq"
236 |     else
237 |       ADDITIONAL=$*
238 |     fi
239 |     gjslint --strict --closurized_namespaces=goog,vsaq --limited_doc_files=_test.js --exclude_files=deps.js,externs.js $ADDITIONAL
240 |     RETVAL=$?
241 |   fi
242 | }
243 | 
244 | vsaq_build_docs() {
245 |   rm -rf docs/*
246 |   if [ ! -f third_party/js-dossier/buck-out/gen/src/java/com/github/jsdossier/dossier.jar ]; then
247 |     if [ -z `which buck` ]; then
248 |       echo "Facebook Buck is not installed. Buck is needed by js-dossier to build the documentation."
249 |       echo "Follow instructions at http://buckbuild.com/setup/quick_start.html to install."
250 |       echo "Make sure 'buck' command line tool is available."
251 |       RETVAL=1
252 |       exit
253 |     else
254 |       cd third_party/js-dossier
255 |       ./gendossier.sh -r
256 |       cd ../..
257 |     fi
258 |   fi
259 |   vsaq_build_templates
260 |   java -jar third_party/js-dossier/buck-out/gen/src/java/com/github/jsdossier/dossier.jar -c third_party/docs-build/dossier-config.json
261 |   RETVAL=$?
262 | }
263 | 
264 | RETVAL=0
265 | 
266 | CMD=$1
267 | shift
268 | 
269 | case "$CMD" in
270 |   check_deps)
271 |     vsaq_assert_dependencies;
272 |     ;;
273 |   install_deps)
274 |     vsaq_install_deps;
275 |     ;;
276 |   build)
277 |     vsaq_build $1;
278 |     ;;
279 |   build_prod)
280 |     vsaq_build_prod;
281 |     ;;
282 |   build_templates)
283 |     vsaq_build_templates;
284 |     ;;
285 |   build_jsmodule)
286 |     vsaq_build_jsmodule $*;
287 |     ;;
288 |   clean)
289 |     vsaq_build_clean;
290 |     ;;
291 |   clean_deps)
292 |     vsaq_clean_deps;
293 |     ;;
294 |   run)
295 |     vsaq_run;
296 |     ;;
297 |   lint)
298 |     vsaq_lint $*;
299 |     ;;
300 |   build_docs)
301 |     vsaq_build_docs;
302 |     ;;
303 |   deps)
304 |     vsaq_generate_deps;
305 |     ;;
306 |   *)
307 |     echo "Usage:   $0 PARAMETER"
308 |     echo "Setup:   $0 {install_deps|check_deps}"
309 |     echo "Build:   $0 {build|build_prod|build_templates|build_docs} [debug]"
310 |     echo "Run:     $0 {run}"
311 |     echo "Cleanup: $0 {clean|clean_deps}"
312 |     echo "Other:   $0 {lint}"
313 |     RETVAL=1
314 | esac
315 | 
316 | exit $RETVAL
317 | 


--------------------------------------------------------------------------------