diff --git a/.changeset/pretty-wolves-type.md b/.changeset/pretty-wolves-type.md new file mode 100644 index 0000000..8836a52 --- /dev/null +++ b/.changeset/pretty-wolves-type.md @@ -0,0 +1,6 @@ +--- +"@jspsych/metadata-cli": minor +"@jspsych/metadata": patch +--- + +Updating metadata-cli to implement Psych-DS validator and small build changes to metadata module diff --git a/.gitignore b/.gitignore index c6bba59..ec961c2 100644 --- a/.gitignore +++ b/.gitignore @@ -128,3 +128,6 @@ dist .yarn/build-state.yml .yarn/install-state.gz .pnp.* + +# DS Store +.DS_Store \ No newline at end of file diff --git a/README.md b/README.md index bc55acb..ce520b5 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,17 @@ -# metadata -Library and CLI tool to generate Psych-DS compliant metadata for jsPsych experiments +# Psych-DS and metadata + +## What is Psych-DS and metadata? + +Psych-DS is a template data schema to standardize the representation of data files for common Psychology and Cognitive Science experiments. This representation aims to help facilate data sharing by making data more understandable using common structures and metadata files that describe the data. Metadata files describe the data and including relevant information about the types of variables present as well as what each file represents. + +## How does it relate to Jspsych? + +JsPsych is one software among many to begin implementing this data standard and has many different tools to generate metadata automatically from data files. + +# Creating metadata and Psych-DS directories + +## How do I create a metadata file and organize my project according to Psych-DS? + +There are many different ways to generate metadata. Each tool has it's own pros and cons, + +# Developer Roadmap diff --git a/dev/jspsych-metadata-generated/CHANGES.md b/dev/jspsych-metadata-generated/CHANGES.md new file mode 100644 index 0000000..c105f4d --- /dev/null +++ b/dev/jspsych-metadata-generated/CHANGES.md @@ -0,0 +1 @@ +For version tracking - if the dataset is updated after being uploaded/shared, changes (with human-readable descriptions) may be recorded here. \ No newline at end of file diff --git a/dev/jspsych-metadata-generated/README.md b/dev/jspsych-metadata-generated/README.md new file mode 100644 index 0000000..9d52faa --- /dev/null +++ b/dev/jspsych-metadata-generated/README.md @@ -0,0 +1,2 @@ +# My Project + Human-readable description of the project and dataset. \ No newline at end of file diff --git a/dev/jspsych-metadata-generated/data/study-exampleStudy_responsetype-audioSlider_data.csv b/dev/jspsych-metadata-generated/data/study-exampleStudy_responsetype-audioSlider_data.csv new file mode 100644 index 0000000..a708e18 --- /dev/null +++ b/dev/jspsych-metadata-generated/data/study-exampleStudy_responsetype-audioSlider_data.csv @@ -0,0 +1,6 @@ +"success","timeout","failed_images","failed_audio","failed_video","trial_type","trial_index","plugin_version","time_elapsed","rt","stimulus","response","slider_start" +"true","false","[]","[]","[]","preload","0","2.0.0","26","","","","" +"","","","","","html-button-response","1","2.0.0","1200","920","

Some browsers now require the user to interact with a page before it can play audio. Clicking the button below counts as an interaction.

Be aware of this when planning audio experiments if you want the first trial to include audio.

","0","" +"","","","","","audio-slider-response","2","2.0.0","3465","-1521640","sound/speech_joke.mp3","6","50" +"","","","","","audio-slider-response","3","2.0.0","5133","-3782974","sound/speech_red.mp3","93","50" +"","","","","","audio-slider-response","4","2.0.0","12999","-5443808","sound/hammer.mp3","100","50" diff --git a/dev/jspsych-metadata-generated/data/study-exampleStudy_responsetype-categorizeHTML_data.csv b/dev/jspsych-metadata-generated/data/study-exampleStudy_responsetype-categorizeHTML_data.csv new file mode 100644 index 0000000..47c8e09 --- /dev/null +++ b/dev/jspsych-metadata-generated/data/study-exampleStudy_responsetype-categorizeHTML_data.csv @@ -0,0 +1 @@ +[{"rt":null,"correct":false,"stimulus":"
","response":null,"trial_type":"categorize-html","trial_index":0,"plugin_version":"2.0.0","time_elapsed":3503}] \ No newline at end of file diff --git a/dev/jspsych-metadata-generated/data/study-exampleStudy_responsetype-keyboardData_data.csv b/dev/jspsych-metadata-generated/data/study-exampleStudy_responsetype-keyboardData_data.csv new file mode 100644 index 0000000..5f0e667 --- /dev/null +++ b/dev/jspsych-metadata-generated/data/study-exampleStudy_responsetype-keyboardData_data.csv @@ -0,0 +1,18 @@ +"success","timeout","failed_images","failed_audio","failed_video","trial_type","trial_index","plugin_version","time_elapsed","rt","stimulus","response" +"true","false","[]","[]","[]","preload","0","2.0.0","11","","","" +"","","","","","image-keyboard-response","1","2.0.0","314","null","img/happy_face_1.jpg","null" +"","","","","","image-keyboard-response","2","2.0.0","617","null","img/happy_face_2.jpg","null" +"","","","","","image-keyboard-response","3","2.0.0","919","null","img/happy_face_3.jpg","null" +"","","","","","image-keyboard-response","4","2.0.0","1221","null","img/happy_face_4.jpg","null" +"","","","","","image-keyboard-response","5","2.0.0","1524","null","img/happy_face_1.jpg","null" +"","","","","","image-keyboard-response","6","2.0.0","1827","null","img/happy_face_2.jpg","null" +"","","","","","image-keyboard-response","7","2.0.0","2129","null","img/happy_face_3.jpg","null" +"","","","","","image-keyboard-response","8","2.0.0","2431","null","img/happy_face_4.jpg","null" +"","","","","","image-keyboard-response","9","2.0.0","2733","null","img/happy_face_1.jpg","null" +"","","","","","image-keyboard-response","10","2.0.0","3036","null","img/happy_face_2.jpg","null" +"","","","","","image-keyboard-response","11","2.0.0","3339","null","img/happy_face_3.jpg","null" +"","","","","","image-keyboard-response","12","2.0.0","3641","null","img/happy_face_4.jpg","null" +"","","","","","image-keyboard-response","13","2.0.0","4989","1346","img/happy_face_1.jpg","y" +"","","","","","image-keyboard-response","14","2.0.0","5435","444","img/happy_face_2.jpg","y" +"","","","","","image-keyboard-response","15","2.0.0","7438","null","img/happy_face_3.jpg","null" +"","","","","","image-keyboard-response","16","2.0.0","7919","478","img/happy_face_4.jpg","y" diff --git a/dev/jspsych-metadata-generated/data/study-exampleStudy_responsetype-mouseExtension_data.csv b/dev/jspsych-metadata-generated/data/study-exampleStudy_responsetype-mouseExtension_data.csv new file mode 100644 index 0000000..64df79e --- /dev/null +++ b/dev/jspsych-metadata-generated/data/study-exampleStudy_responsetype-mouseExtension_data.csv @@ -0,0 +1 @@ +[{"task":"draw","rt":1995,"stimulus":"
","response":0,"trial_type":"html-button-response","trial_index":0,"plugin_version":"2.0.0","time_elapsed":1997,"extension_type":["mouse-tracking"],"extension_version":["1.1.0"],"mouse_tracking_data":[{"x":503,"y":272,"t":1364,"event":"mousemove"},{"x":526,"y":285,"t":1367,"event":"mousemove"},{"x":583,"y":315,"t":1380,"event":"mousemove"},{"x":670,"y":362,"t":1397,"event":"mousemove"},{"x":777,"y":417,"t":1414,"event":"mousemove"},{"x":844,"y":457,"t":1430,"event":"mousemove"},{"x":914,"y":501,"t":1447,"event":"mousemove"},{"x":950,"y":525,"t":1464,"event":"mousemove"},{"x":980,"y":547,"t":1480,"event":"mousemove"},{"x":988,"y":557,"t":1497,"event":"mousemove"},{"x":996,"y":566,"t":1514,"event":"mousemove"},{"x":997,"y":569,"t":1530,"event":"mousemove"},{"x":997,"y":570,"t":1547,"event":"mousemove"},{"x":993,"y":573,"t":1564,"event":"mousemove"},{"x":983,"y":577,"t":1580,"event":"mousemove"},{"x":967,"y":580,"t":1597,"event":"mousemove"},{"x":959,"y":583,"t":1614,"event":"mousemove"},{"x":948,"y":587,"t":1630,"event":"mousemove"},{"x":942,"y":590,"t":1647,"event":"mousemove"},{"x":937,"y":592,"t":1664,"event":"mousemove"},{"x":933,"y":594,"t":1680,"event":"mousemove"},{"x":929,"y":595,"t":1697,"event":"mousemove"},{"x":925,"y":595,"t":1714,"event":"mousemove"},{"x":917,"y":595,"t":1730,"event":"mousemove"},{"x":906,"y":594,"t":1747,"event":"mousemove"},{"x":897,"y":593,"t":1764,"event":"mousemove"},{"x":869,"y":593,"t":1780,"event":"mousemove"},{"x":851,"y":593,"t":1797,"event":"mousemove"},{"x":840,"y":593,"t":1814,"event":"mousemove"},{"x":830,"y":593,"t":1830,"event":"mousemove"},{"x":824,"y":593,"t":1847,"event":"mousemove"},{"x":820,"y":593,"t":1864,"event":"mousemove"},{"x":818,"y":593,"t":1881,"event":"mousemove"},{"x":818,"y":593,"t":1897,"event":"mousemove"},{"x":818,"y":593,"t":1914,"event":"mousemove"}],"mouse_tracking_targets":{"#target":{"x":465.984375,"y":170.046875,"width":250,"height":250,"top":170.046875,"right":715.984375,"bottom":420.046875,"left":465.984375}}},{"task":"replay","rt":2363,"stimulus":"
","response":0,"trial_type":"html-button-response","trial_index":1,"plugin_version":"2.0.0","time_elapsed":4363},{"rt":51279,"stimulus":"

Trial data:

\n
[\n  {\n    \"task\": \"draw\",\n    \"rt\": 1995,\n    \"stimulus\": \"
\",\n \"response\": 0,\n \"trial_type\": \"html-button-response\",\n \"trial_index\": 0,\n \"plugin_version\": \"2.0.0\",\n \"time_elapsed\": 1997,\n \"extension_type\": [\n \"mouse-tracking\"\n ],\n \"extension_version\": [\n \"1.1.0\"\n ],\n \"mouse_tracking_data\": [\n {\n \"x\": 503,\n \"y\": 272,\n \"t\": 1364,\n \"event\": \"mousemove\"\n },\n {\n \"x\": 526,\n \"y\": 285,\n \"t\": 1367,\n \"event\": \"mousemove\"\n },\n {\n \"x\": 583,\n \"y\": 315,\n \"t\": 1380,\n \"event\": \"mousemove\"\n },\n {\n \"x\": 670,\n \"y\": 362,\n \"t\": 1397,\n \"event\": \"mousemove\"\n },\n {\n \"x\": 777,\n \"y\": 417,\n \"t\": 1414,\n \"event\": \"mousemove\"\n },\n {\n \"x\": 844,\n \"y\": 457,\n \"t\": 1430,\n \"event\": \"mousemove\"\n },\n {\n \"x\": 914,\n \"y\": 501,\n \"t\": 1447,\n \"event\": \"mousemove\"\n },\n {\n \"x\": 950,\n \"y\": 525,\n \"t\": 1464,\n \"event\": \"mousemove\"\n },\n {\n \"x\": 980,\n \"y\": 547,\n \"t\": 1480,\n \"event\": \"mousemove\"\n },\n {\n \"x\": 988,\n \"y\": 557,\n \"t\": 1497,\n \"event\": \"mousemove\"\n },\n {\n \"x\": 996,\n \"y\": 566,\n \"t\": 1514,\n \"event\": \"mousemove\"\n },\n {\n \"x\": 997,\n \"y\": 569,\n \"t\": 1530,\n \"event\": \"mousemove\"\n },\n {\n \"x\": 997,\n \"y\": 570,\n \"t\": 1547,\n \"event\": \"mousemove\"\n },\n {\n \"x\": 993,\n \"y\": 573,\n \"t\": 1564,\n \"event\": \"mousemove\"\n },\n {\n \"x\": 983,\n \"y\": 577,\n \"t\": 1580,\n \"event\": \"mousemove\"\n },\n {\n \"x\": 967,\n \"y\": 580,\n \"t\": 1597,\n \"event\": \"mousemove\"\n },\n {\n \"x\": 959,\n \"y\": 583,\n \"t\": 1614,\n \"event\": \"mousemove\"\n },\n {\n \"x\": 948,\n \"y\": 587,\n \"t\": 1630,\n \"event\": \"mousemove\"\n },\n {\n \"x\": 942,\n \"y\": 590,\n \"t\": 1647,\n \"event\": \"mousemove\"\n },\n {\n \"x\": 937,\n \"y\": 592,\n \"t\": 1664,\n \"event\": \"mousemove\"\n },\n {\n \"x\": 933,\n \"y\": 594,\n \"t\": 1680,\n \"event\": \"mousemove\"\n },\n {\n \"x\": 929,\n \"y\": 595,\n \"t\": 1697,\n \"event\": \"mousemove\"\n },\n {\n \"x\": 925,\n \"y\": 595,\n \"t\": 1714,\n \"event\": \"mousemove\"\n },\n {\n \"x\": 917,\n \"y\": 595,\n \"t\": 1730,\n \"event\": \"mousemove\"\n },\n {\n \"x\": 906,\n \"y\": 594,\n \"t\": 1747,\n \"event\": \"mousemove\"\n },\n {\n \"x\": 897,\n \"y\": 593,\n \"t\": 1764,\n \"event\": \"mousemove\"\n },\n {\n \"x\": 869,\n \"y\": 593,\n \"t\": 1780,\n \"event\": \"mousemove\"\n },\n {\n \"x\": 851,\n \"y\": 593,\n \"t\": 1797,\n \"event\": \"mousemove\"\n },\n {\n \"x\": 840,\n \"y\": 593,\n \"t\": 1814,\n \"event\": \"mousemove\"\n },\n {\n \"x\": 830,\n \"y\": 593,\n \"t\": 1830,\n \"event\": \"mousemove\"\n },\n {\n \"x\": 824,\n \"y\": 593,\n \"t\": 1847,\n \"event\": \"mousemove\"\n },\n {\n \"x\": 820,\n \"y\": 593,\n \"t\": 1864,\n \"event\": \"mousemove\"\n },\n {\n \"x\": 818,\n \"y\": 593,\n \"t\": 1881,\n \"event\": \"mousemove\"\n },\n {\n \"x\": 818,\n \"y\": 593,\n \"t\": 1897,\n \"event\": \"mousemove\"\n },\n {\n \"x\": 818,\n \"y\": 593,\n \"t\": 1914,\n \"event\": \"mousemove\"\n }\n ],\n \"mouse_tracking_targets\": {\n \"#target\": {\n \"x\": 465.984375,\n \"y\": 170.046875,\n \"width\": 250,\n \"height\": 250,\n \"top\": 170.046875,\n \"right\": 715.984375,\n \"bottom\": 420.046875,\n \"left\": 465.984375\n }\n }\n }\n]
","response":0,"trial_type":"html-button-response","trial_index":2,"plugin_version":"2.0.0","time_elapsed":55644}] \ No newline at end of file diff --git a/dev/jspsych-metadata-generated/data/study-exampleStudy_responsetype-timelineVariables_data.csv b/dev/jspsych-metadata-generated/data/study-exampleStudy_responsetype-timelineVariables_data.csv new file mode 100644 index 0000000..c784869 --- /dev/null +++ b/dev/jspsych-metadata-generated/data/study-exampleStudy_responsetype-timelineVariables_data.csv @@ -0,0 +1,14 @@ +"success","timeout","failed_images","failed_audio","failed_video","trial_type","trial_index","plugin_version","time_elapsed","rt","stimulus","response" +"true","false","[]","[]","[]","preload","0","2.0.0","5","","","" +"","","","","","html-keyboard-response","1","2.0.0","1088","null","

+

","null" +"","","","","","image-keyboard-response","2","2.0.0","2739","1398","img/happy_face_2.jpg","y" +"","","","","","html-keyboard-response","3","2.0.0","3494","null","

+

","null" +"","","","","","image-keyboard-response","4","2.0.0","4002","255","img/happy_face_3.jpg","n" +"","","","","","html-keyboard-response","5","2.0.0","4756","null","

+

","null" +"","","","","","image-keyboard-response","6","2.0.0","5073","64","img/happy_face_1.jpg","y" +"","","","","","html-keyboard-response","7","2.0.0","5828","null","

+

","null" +"","","","","","image-keyboard-response","8","2.0.0","6462","381","img/happy_face_2.jpg","n" +"","","","","","html-keyboard-response","9","2.0.0","7217","null","

+

","null" +"","","","","","image-keyboard-response","10","2.0.0","7639","168","img/happy_face_3.jpg","y" +"","","","","","html-keyboard-response","11","2.0.0","8393","null","

+

","null" +"","","","","","image-keyboard-response","12","2.0.0","8697","50","img/happy_face_1.jpg","n" diff --git a/dev/jspsych-metadata-generated/dataset_description.json b/dev/jspsych-metadata-generated/dataset_description.json new file mode 100644 index 0000000..3bbafa1 --- /dev/null +++ b/dev/jspsych-metadata-generated/dataset_description.json @@ -0,0 +1,237 @@ +{ + "name": "title", + "schemaVersion": "Psych-DS 0.4.0", + "@context": "https://schema.org", + "@type": "Dataset", + "description": "Dataset generated using JsPsych", + "randomField": "this is a field", + "author": [ + { + "@type":"Person", + "name": "John", + "givenName": "Johnathan" + } + ], + "variableMeasured": [ + { + "@type": "PropertyValue", + "name": "trial_type", + "description": "The name of the plugin used to run the trial.", + "value": "string", + "levels": [ + "preload", + "html-keyboard-response", + "image-keyboard-response", + "html-button-response", + "audio-slider-response", + "categorize-html", + "initialize-camera" + ] + }, + { + "@type": "PropertyValue", + "name": "trial_index", + "description": "The index of the current trial across the whole experiment.", + "value": "numeric", + "minValue": 0, + "maxValue": 16 + }, + { + "@type": "PropertyValue", + "name": "time_elapsed", + "description": "The number of milliseconds between the start of the experiment and when the trial ended.", + "value": "numeric", + "minValue": 5, + "maxValue": 55644 + }, + { + "@type": "PropertyValue", + "name": "success", + "description": "If `true`, then all files loaded successfully within the `max_load_time`. If `false`, then one or * more file requests returned a failure and/or the file loading did not complete within the `max_load_time` duration.", + "value": "boolean", + "levels": [ + true + ] + }, + { + "@type": "PropertyValue", + "name": "timeout", + "description": "If `true`, then the files did not finish loading within the `max_load_time` duration. * If `false`, then the file loading did not timeout. Note that when the preload trial does not timeout * (`timeout: false`), it is still possible for loading to fail (`success: false`). This happens if * one or more files fails to load and all file requests trigger either a success or failure event before * the `max_load_time` duration.", + "value": "boolean", + "levels": [ + false + ] + }, + { + "@type": "PropertyValue", + "name": "failed_images", + "description": "One or more image file paths that produced a loading failure before the trial ended.", + "value": "object", + "levels": [ + "[]" + ] + }, + { + "@type": "PropertyValue", + "name": "failed_audio", + "description": "One or more audio file paths that produced a loading failure before the trial ended.", + "value": "object", + "levels": [ + "[]" + ] + }, + { + "@type": "PropertyValue", + "name": "failed_video", + "description": "One or more video file paths that produced a loading failure before the trial ended.", + "value": "object", + "levels": [ + "[]" + ] + }, + { + "@type": "PropertyValue", + "name": "plugin_version", + "description": "unknown", + "value": "string", + "levels": [ + "1.1.3", + "2.0.0" + ] + }, + { + "@type": "PropertyValue", + "name": "stimulus", + "description": { + "image-keyboard-response": "The path of the image that was displayed.", + "html-keyboard-response, html-button-response": "The HTML content that was displayed on the screen.", + "audio-slider-response": "The path of the audio file that was played.", + "categorize-html": "Either the path to the image file or the string containing the HTML formatted content that the participant saw on this trial." + }, + "value": "string", + "levels": [ + "

+

", + "img/happy_face_1.jpg", + "img/happy_face_2.jpg", + "img/happy_face_3.jpg", + "img/happy_face_4.jpg", + "

+

Some browsers now...", + "sound/speech_joke.mp3", + "sound/speech_red.mp3", + "sound/hammer.mp3", + "

Trial data:<..." + ] + }, + { + "@type": "PropertyValue", + "name": "rt", + "description": { + "html-button-response": "The response time in milliseconds for the participant to make a response. The time is measured from when the stimulus first appears on the screen until the participant's response.", + "audio-slider-response": "The time in milliseconds for the participant to make a response. The time is measured from when the stimulus first * began playing until the participant's response.", + "image-keyboard-response": "The response time in milliseconds for the participant to make a response. The time is measured from when the stimulus * first appears on the screen until the participant's response." + }, + "value": "number", + "minValue": -5443808, + "maxValue": 51279 + }, + { + "@type": "PropertyValue", + "name": "response", + "description": { + "html-button-response": "Indicates which button the participant pressed. The first button in the `choices` array is 0, the second is 1, and so on.", + "audio-slider-response": "The numeric value of the slider.", + "image-keyboard-response": "Indicates which key the participant pressed." + }, + "value": "string", + "levels": [ + "y", + "n" + ], + "minValue": 0, + "maxValue": 100 + }, + { + "@type": "PropertyValue", + "name": "slider_start", + "description": "The starting value of the slider.", + "value": "number", + "minValue": 50, + "maxValue": 50 + }, + { + "@type": "PropertyValue", + "name": "correct", + "description": "`true` if the participant got the correct answer, `false` otherwise.", + "value": "boolean", + "levels": [ + false + ] + }, + { + "@type": "PropertyValue", + "name": "device_id", + "description": "The [device ID](https://developer.mozilla.org/en-US/docs/Web/API/MediaDeviceInfo/deviceId) of the selected camera.", + "value": "string", + "levels": [ + "224904af1fc7ec895c764b361a04a167504d8af498a36c576e...", + "ed812fc33d145bfb870d47b64ec9702b3456f6640af448d78b..." + ] + }, + { + "@type": "PropertyValue", + "name": "extension_type", + "description": "The name(s) of the extension(s) used in the trial.", + "value": "string" + }, + { + "@type": "PropertyValue", + "name": "extension_version", + "description": "The version(s) of the extension(s) used in the trial.", + "value": "numeric" + }, + { + "@type": "PropertyValue", + "name": "record_video_data", + "description": "[Base 64 encoded](https://developer.mozilla.org/en-US/docs/Glossary/Base64) representation of the video data.", + "value": "string", + "levels": [ + "GkXfo6NChoEBQveBAULygQRC84EIQoKIbWF0cm9za2FCh4EEQo...", + "GkXfo59ChoEBQveBAULygQRC84EIQoKEd2VibUKHgQRChYECGF..." + ] + }, + { + "@type": "PropertyValue", + "name": "task", + "description": "unknown", + "value": "string", + "levels": [ + "draw", + "replay" + ] + }, + { + "@type": "PropertyValue", + "name": "mouse_tracking_data", + "description": "* An array of objects containing mouse movement data for the trial. Each object has an `x`, a `y`, a `t`, and an * `event` property. The `x` and `y` properties specify the mouse coordinates in pixels relative to the top left * corner of the viewport and `t` specifies the time in milliseconds since the start of the trial. The `event` * will be either 'mousemove', 'mousedown', or 'mouseup' depending on which event was generated.", + "value": "object" + }, + { + "@type": "PropertyValue", + "name": "mouse_tracking_targets", + "description": "* An object contain the pixel coordinates of elements on the screen specified by the `.targets` parameter. Each key * in this object will be a `selector` property, containing the CSS selector string used to find the element. The object * corresponding to each key will contain `x` and `y` properties specifying the top-left corner of the object, `width` * and `height` values, plus `top`, `bottom`, `left`, and `right` parameters which specify the * [bounding rectangle](https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect) of the element.", + "value": "object" + }, + { + "@type": "PropertyValue", + "name": "index", + "description": "The index of the current trial across the whole experiment.", + "value": "numeric", + "minValue": 0, + "maxValue": 16 + } + ] +} diff --git a/dev/metadata-timeline-variables.html b/dev/metadata-timeline-variables.html index 5b6f10a..75de424 100644 --- a/dev/metadata-timeline-variables.html +++ b/dev/metadata-timeline-variables.html @@ -8,8 +8,8 @@ - - + +