Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DTL (Test > Levelbuilder): d5e1431f #26756

Merged
merged 56 commits into from
Jan 25, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
c67ab00
Set a max height and vertical scroll bar for the copy to screen menu.
larrypo Jan 11, 2019
8f2866a
Revert "Revert "Rename Instructions in all levels""
Hamms Jan 22, 2019
738f169
update a lingering reference to the old instructions
Hamms Jan 22, 2019
da1ec08
Merge branch 'staging' into revert-26673-revert-26625-rename-instruct…
Hamms Jan 23, 2019
f7132bf
Merge pull request #26706 from code-dot-org/staging
deploy-code-org Jan 23, 2019
46c62f3
Updating FiT registration form
hacodeorg Jan 23, 2019
28952c3
Merge pull request #26710 from code-dot-org/staging
deploy-code-org Jan 23, 2019
ea7ff93
Add new required field to test [skip ui]
hacodeorg Jan 23, 2019
b4c9974
Removed react-test-utils from PairingTest.js
jmkulwik Jan 23, 2019
0e7282a
Refactored unit test to remove global variables, remove duplicate tes…
jmkulwik Jan 23, 2019
4bd2f7b
Update read-only doc links. Remove useless code
hacodeorg Jan 23, 2019
48fe188
Merge pull request #26714 from code-dot-org/staging
deploy-code-org Jan 23, 2019
668b6f3
Use Expo SDK 31 instead, enable offline with assetBundlePatterns, dis…
cpirich Jan 24, 2019
053bf50
Minimal prototype for docker local development
Jan 24, 2019
3ee95f2
Remove commented-out code from Dockerfile
Jan 24, 2019
41adc5d
Update comments on Dockerfile
Jan 24, 2019
b25263c
Update README / comments on test scripts
Jan 24, 2019
9cf8339
Removed react-test-utils from AppLabCrosshairOverlayTest.js
jmkulwik Jan 24, 2019
6e1e161
Merge pull request #26724 from code-dot-org/staging
deploy-code-org Jan 24, 2019
3d1a038
complete Expo rollback to SDK 31, add additional styles to gamelab ex…
cpirich Jan 24, 2019
2694990
Merge pull request #26726 from code-dot-org/staging
deploy-code-org Jan 24, 2019
e12381e
Log asset delete from manage and choose dialogs
Jan 24, 2019
4a5e14e
Merge pull request #26727 from code-dot-org/staging
deploy-code-org Jan 24, 2019
24562d1
Update question [skip ui]
hacodeorg Jan 24, 2019
04892d9
Merge pull request #26730 from code-dot-org/staging
deploy-code-org Jan 24, 2019
992209f
Merge pull request #26731 from code-dot-org/levelbuilder
Jan 24, 2019
47fbe18
Merge pull request #26732 from code-dot-org/staging
deploy-code-org Jan 24, 2019
85aa7ae
Merge pull request #26708 from code-dot-org/ha/FitRegForm
hacodeorg Jan 24, 2019
3bb2d05
Log element id when deleting an image
Jan 24, 2019
0ea254d
Merge pull request #26733 from code-dot-org/staging
deploy-code-org Jan 24, 2019
5e1247f
update reference to renamed class
Jan 24, 2019
e012242
add a reference from old workshops table to new pd_workshops, so we c…
Hamms Jan 24, 2019
163dea5
Merge pull request #26720 from code-dot-org/remove-react-applab-cross…
jmkulwik Jan 24, 2019
724603b
Merge pull request #26711 from code-dot-org/remove-react-pairing-test
jmkulwik Jan 24, 2019
420f444
Merge pull request #26738 from code-dot-org/staging
deploy-code-org Jan 24, 2019
c584af7
Merge pull request #26674 from code-dot-org/revert-26673-revert-26625…
Hamms Jan 24, 2019
f5d6d13
Revert "Strip metadata from large (>2mb) png files"
islemaster Jan 25, 2019
f590167
Merge pull request #26741 from code-dot-org/revert-26713-optimize-res…
islemaster Jan 25, 2019
8dd6afc
Merge pull request #26742 from code-dot-org/staging
deploy-code-org Jan 25, 2019
2d2f81a
Merge pull request #26716 from code-dot-org/export_expo_offline_support
cpirich Jan 25, 2019
9e4dc21
Merge pull request #26737 from code-dot-org/add-workshop-conversion-r…
Hamms Jan 25, 2019
b4bb951
Update schema cache dump after schema changes.
deploy-code-org Jan 25, 2019
d01c83b
Enable TTS in Dance Party Extras
Jan 25, 2019
ce6a9bc
staging content changes (-robo-commit)
deploy-code-org Jan 25, 2019
cd6c321
update fit cohort view to show current applications and add tests
Jan 25, 2019
a57c67d
Merge pull request #26740 from code-dot-org/log-delete-assets
Jan 25, 2019
9e4b94e
Merge pull request #26745 from code-dot-org/levelbuilder
deploy-code-org Jan 25, 2019
0da48d8
Merge pull request #26746 from code-dot-org/staging
deploy-code-org Jan 25, 2019
bf8a710
Merge pull request #26743 from code-dot-org/enable-TTS-dance-extras
Jan 25, 2019
b59e82b
Merge pull request #26747 from code-dot-org/staging
deploy-code-org Jan 25, 2019
64102d7
Merge pull request #26735 from code-dot-org/remove-tc
Jan 25, 2019
47ebcc7
Merge pull request #26719 from code-dot-org/docker-local-dev
Jan 25, 2019
992c1d6
Update apps/src/applab/designElements/CopyElementToScreenButton.jsx
islemaster Jan 25, 2019
cc433ae
Merge pull request #26581 from larrypo/copy_to_screen_scroll_bar
larrypo Jan 25, 2019
d5e1431
Merge pull request #26751 from code-dot-org/staging
deploy-code-org Jan 25, 2019
982a4c2
Merge branch 'levelbuilder' into dtl_candidate_d5e1431f
Hamms Jan 25, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
The diff you're trying to view is too large. We only load the first 3000 changed files.
2 changes: 1 addition & 1 deletion apps/src/applab/Exporter.js
Original file line number Diff line number Diff line change
Expand Up @@ -571,7 +571,7 @@ export default {
sessionId: `${getEnvironmentPrefix()}-${project.getCurrentId()}`,
files,
name: project.getCurrentName(),
sdkVersion: '32.0.0',
sdkVersion: '31.0.0',
});

// Important that index.html comes first:
Expand Down
5 changes: 5 additions & 0 deletions apps/src/applab/designElements/CopyElementToScreenButton.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ const styles = {
},
screen: {
},
menu: {
maxHeight: '200px',
overflowY: 'auto',
},
};

/**
Expand Down Expand Up @@ -104,6 +108,7 @@ class CopyElementToScreenButton extends React.Component {
targetPoint={targetPoint}
offset={{x: 0, y: 0}}
beforeClose={this.beforeClose}
style={styles.menu}
>
{otherScreens}
</PopUpMenu>
Expand Down
4 changes: 3 additions & 1 deletion apps/src/applab/designElements/ImagePickerPropertyRow.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export default class ImagePickerPropertyRow extends React.Component {
initialValue: PropTypes.string.isRequired,
handleChange: PropTypes.func,
desc: PropTypes.node,
elementId: PropTypes.string
};

componentDidMount() {
Expand Down Expand Up @@ -61,7 +62,8 @@ export default class ImagePickerPropertyRow extends React.Component {
// However today the `createModalDialog` function and `Dialog` component
// are intertwined with `StudioApp` which is why we have this direct call.
dashboard.assets.showAssetManager(this.changeImage, 'image', null, {
showUnderageWarning: !getStore().getState().pageConstants.is13Plus
showUnderageWarning: !getStore().getState().pageConstants.is13Plus,
elementId: this.props.elementId
});
};

Expand Down
1 change: 1 addition & 0 deletions apps/src/applab/designElements/button.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ class ButtonProperties extends React.Component {
desc={'image'}
initialValue={element.getAttribute('data-canonical-image-url') || ''}
handleChange={this.props.handleChange.bind(this, 'image')}
elementId={elementUtils.getId(element)}
/>
{iconColorPicker}
<BooleanPropertyRow
Expand Down
1 change: 1 addition & 0 deletions apps/src/applab/designElements/image.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ class ImageProperties extends React.Component {
desc={'image'}
initialValue={element.getAttribute('data-canonical-image-url') || ''}
handleChange={this.props.handleChange.bind(this, 'picture')}
elementId={elementUtils.getId(element)}
/>
{iconColorPicker}
<EnumPropertyRow
Expand Down
1 change: 1 addition & 0 deletions apps/src/applab/designElements/screen.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ class ScreenProperties extends React.Component {
desc={'image'}
initialValue={element.getAttribute('data-canonical-image-url') || ''}
handleChange={this.props.handleChange.bind(this, 'screen-image')}
elementId={elementUtils.getId(element)}
/>
{iconColorPicker}
<DefaultScreenButtonPropertyRow
Expand Down
4 changes: 3 additions & 1 deletion apps/src/code-studio/assets/show.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ var Dialog = require('../LegacyDialog');
* @param [options.showUnderageWarning] {boolean} Warn if underage.
* @param [options.useFilesApi] {boolean} Use files API instead of assets API.
* @param [options.disableAudioRecording] {boolean} Do not display option to record and upload audio files
* @param [options.elementId] {string} Logging Purposes: which element is the image chosen for
*/
module.exports = function showAssetManager(assetChosen, typeFilter, onClose, options) {
options = options || {};
Expand Down Expand Up @@ -48,7 +49,8 @@ module.exports = function showAssetManager(assetChosen, typeFilter, onClose, opt
showUnderageWarning: !!options.showUnderageWarning,
projectId: dashboard.project.getCurrentId(),
soundPlayer: sounds,
disableAudioRecording: options.disableAudioRecording
disableAudioRecording: options.disableAudioRecording,
elementId: options.elementId
}), codeDiv);

dialog.show();
Expand Down
26 changes: 22 additions & 4 deletions apps/src/code-studio/components/AssetManager.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,13 @@ export default class AssetManager extends React.Component {
allowedExtensions: PropTypes.string,
uploadsEnabled: PropTypes.bool.isRequired,
useFilesApi: PropTypes.bool,
//For logging upload failures
projectId: PropTypes.string,
soundPlayer: PropTypes.object,
disableAudioRecording: PropTypes.bool,
//Temp prop for logging - identifies if displayed by 'Manage Assets' flow
imagePicker: PropTypes.bool

// For logging purposes
imagePicker: PropTypes.bool, // identifies if displayed by 'Manage Assets' flow
projectId: PropTypes.string,
elementId: PropTypes.string
};

constructor(props) {
Expand Down Expand Up @@ -143,6 +144,21 @@ export default class AssetManager extends React.Component {
if (this.props.assetsChanged) {
this.props.assetsChanged();
}
firehoseClient.putRecord(
{
study: 'delete-asset',
study_group: this.props.assetChosen && typeof this.props.assetChosen === 'function' ? 'choose-assets' : 'manage-assets',
event: 'confirm',
project_id: this.props.projectId,
data_json: JSON.stringify(
{
assetName: name,
elementId: this.props.elementId
}
)
}
);

this.setState({
assets: assetListStore.list(this.props.allowedExtensions),
statusMessage: 'File "' + name + '" successfully deleted!'
Expand Down Expand Up @@ -223,6 +239,8 @@ export default class AssetManager extends React.Component {
onDelete={this.deleteAssetRow.bind(this, asset.filename)}
soundPlayer={this.props.soundPlayer}
imagePicker={this.props.imagePicker}
projectId={this.props.projectId}
elementId={this.props.elementId}
/>
);
}.bind(this));
Expand Down
20 changes: 18 additions & 2 deletions apps/src/code-studio/components/AssetRow.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@ export default class AssetRow extends React.Component {
onChoose: PropTypes.func,
onDelete: PropTypes.func.isRequired,
soundPlayer: PropTypes.object,
projectId: PropTypes.string,

//temporary prop to differentiate choosing images and sounds
imagePicker: PropTypes.bool
// For logging purposes
imagePicker: PropTypes.bool, // identifies if displayed by 'Manage Assets' flow
elementId: PropTypes.string
};

state = {
Expand All @@ -32,6 +34,20 @@ export default class AssetRow extends React.Component {
*/
confirmDelete = () => {
this.setState({action: 'confirming delete', actionText: ''});
firehoseClient.putRecord(
{
study: 'delete-asset',
study_group: this.props.onChoose && typeof this.props.onChoose === 'function' ? 'choose-assets' : 'manage-assets',
event: 'initiate',
project_id: this.props.projectId,
data_json: JSON.stringify(
{
assetName: this.props.name,
elementId: this.props.elementId
}
)
}
);
};

/**
Expand Down
6 changes: 4 additions & 2 deletions apps/src/code-studio/components/ImagePicker.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@ export default class ImagePicker extends React.Component {
useFilesApi: PropTypes.bool,
soundPlayer: PropTypes.object,
disableAudioRecording: PropTypes.bool,
//For logging upload failures
projectId: PropTypes.string
//For logging purposes
projectId: PropTypes.string,
elementId: PropTypes.string
};

state = {mode: 'files'};
Expand Down Expand Up @@ -93,6 +94,7 @@ export default class ImagePicker extends React.Component {
soundPlayer={this.props.soundPlayer}
disableAudioRecording={this.props.disableAudioRecording}
imagePicker={true}
elementId={this.props.elementId}
/> :
<IconLibrary assetChosen={this.getAssetNameWithPrefix}/>;

Expand Down
6 changes: 3 additions & 3 deletions apps/src/code-studio/pd/fit_weekend_registration/Joining.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export default class Joining extends LabeledFormComponent {
lastName: "Last name",
email: "Email",
phone: "Phone number",
ableToAttend: "Are you able to attend your assigned FiT Weekend?"
ableToAttend: "Are you able to attend your assigned FiT Workshop?"
};

static associatedFields = Object.keys(Joining.labels);
Expand Down Expand Up @@ -52,7 +52,7 @@ export default class Joining extends LabeledFormComponent {

<FormGroup>
<ControlLabel>
Your assigned Facilitator-in-Training (FiT) Weekend is:
Your assigned Facilitator-in-Training (FiT) Workshop is:
<br />
<strong>
FiT {this.props.city}, {this.props.date}
Expand All @@ -61,7 +61,7 @@ export default class Joining extends LabeledFormComponent {
{this.radioButtonsFor("ableToAttend")}
{this.props.data.ableToAttend === "No" &&
<p>
If you're unable to attend your assigned FiT Weekend, please
If you're unable to attend your assigned FiT Workshop, please
contact <a href="mailto:facilitators@code.org">facilitators@code.org</a> as
soon as possible so we can assist you.
</p>
Expand Down
4 changes: 2 additions & 2 deletions apps/src/code-studio/pd/fit_weekend_registration/Releases.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export default class Releases extends LabeledFormComponent {
<h4>Section 3: Releases</h4>
<FormGroup>
<ControlLabel>
Please read this <a target="_blank" href="https://docs.google.com/document/d/12NVUcqccNRbVukoGMCserwSpg4vfG0vNlEqTxr6oit0/edit">photo release.</a>
Please read this <a target="_blank" href="https://docs.google.com/document/d/1zke9hlGbI1XbSzwQ1rNZofXuXaOboJuTFPW2Z_XOA1w/edit">photo release.</a>
</ControlLabel>
{this.singleCheckboxFor("photoRelease")}
<ControlLabel>
Expand Down Expand Up @@ -61,7 +61,7 @@ export default class Releases extends LabeledFormComponent {
<p>
We're excited you're planning to join us this summer! You will receive
more information about travel approximately six weeks before the FiT
Weekend. In the meantime, please <strong>do not</strong> book your flight,
Workshop. In the meantime, please <strong>do not</strong> book your flight,
and make sure to contact <a href="mailto:facilitators@code.org">facilitators@code.org</a> with any questions. We look
forward to meeting you this summer!
</p>
Expand Down
29 changes: 9 additions & 20 deletions apps/src/code-studio/pd/fit_weekend_registration/TravelPlans.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@ export default class TravelPlans extends LabeledFormComponent {
contactRelationship: "Relationship to you:",
contactPhone: "Phone number:",
dietaryNeeds: "Do you have any dietary needs or food allergies?",
dietaryNeedsDetails: "Please provide details about your food allergy.",
dietaryNeedsDetails: "Please provide details about your dietary needs.",
addressStreet: "Street",
addressCity: "City",
addressState: "State",
addressZip: "Zip",
howTraveling: "Code.org provides a round trip flight for every FiT Weekend attendee. If you choose to fly, we will provide you with detailed flight booking instructions approximately six weeks prior to the event. If you choose not to fly, and live at least 25 miles from the event location, Code.org will provide you with a $150 gift card to help cover the cost of driving, trains, or public transit. Code.org is not able to provide reimbursement for the cost of driving, trains, or public transit if you live less than 25 miles from the event location. How will you travel to the FiT Weekend?",
needHotel: "Code.org provides a hotel room for every FiT Weekend attendee. Attendees will not be required to share a room. Would you like a hotel room at the FiT Weekend?",
howTraveling: "Code.org provides a round trip flight for every FiT Workshop attendee. If you choose to fly, we will provide you with detailed flight booking instructions approximately six weeks prior to the event. If you choose not to fly, and live at least 25 miles from the event location, Code.org will provide you with a $150 gift card to help cover the cost of driving, trains, or public transit. Code.org is not able to provide reimbursement for the cost of driving, trains, or public transit if you live less than 25 miles from the event location. How will you travel to the FiT Workshop?",
needHotel: "Code.org provides a hotel room for every FiT Workshop attendee. Attendees will not be required to share a room. Would you like a hotel room at the FiT Workshop?",
needAda: "Do you require an ADA accessible hotel room?",
explainAda: "Please explain your specific accommodation needs."
needDisabilitySupport: "Do you have a disability and/or require accommodation in order to fully participate in our event? If so, please select yes, and you will be contacted by someone from our staff to discuss your specific needs."
};

static associatedFields = Object.keys(TravelPlans.labels).concat([
Expand Down Expand Up @@ -63,16 +63,12 @@ export default class TravelPlans extends LabeledFormComponent {
);
}

if (data.dietaryNeeds && data.dietaryNeeds.includes('Food Allergy')) {
if (data.dietaryNeeds && (data.dietaryNeeds.includes('Food Allergy') || data.dietaryNeeds.includes('Other'))) {
requiredFields.push('dietaryNeedsDetails');
}

if (data.needHotel === 'Yes') {
requiredFields.push("needAda");

if (data.needAda === 'Yes') {
requiredFields.push("explainAda");
}
}

return requiredFields;
Expand All @@ -98,7 +94,7 @@ export default class TravelPlans extends LabeledFormComponent {
{this.checkBoxesFor("dietaryNeeds")}
{
this.props.data.dietaryNeeds &&
this.props.data.dietaryNeeds.includes('Food Allergy') &&
(this.props.data.dietaryNeeds.includes('Food Allergy') || this.props.data.dietaryNeeds.includes('Other')) &&
this.largeInputFor("dietaryNeedsDetails")
}
</FormGroup>
Expand Down Expand Up @@ -126,18 +122,14 @@ export default class TravelPlans extends LabeledFormComponent {

<FormGroup>
{this.radioButtonsWithAdditionalTextFieldsFor("howTraveling", {
'I will carpool with another FiT Weekend attendee (Please note who)': 'carpooling_with_attendee'
'I will carpool with another FiT Workshop attendee (Please note who)': 'carpooling_with_attendee'
})}
{this.radioButtonsFor("needHotel")}
{
this.props.data.needHotel === 'Yes' &&
this.radioButtonsFor("needAda")
}
{
this.props.data.needHotel === 'Yes' &&
this.props.data.needAda === 'Yes' &&
this.largeInputFor("explainAda")
}
{this.radioButtonsFor("needDisabilitySupport")}
</FormGroup>
</FormGroup>
);
Expand All @@ -154,11 +146,8 @@ export default class TravelPlans extends LabeledFormComponent {
if (data.needHotel !== 'Yes') {
changes.needAda = undefined;
}
if (changes.needAda !== 'Yes') {
changes.explainAda = undefined;
}

if (data.dietaryNeeds && !data.dietaryNeeds.includes('Food Allergy')) {
if (data.dietaryNeeds && !data.dietaryNeeds.includes('Food Allergy') && !data.dietaryNeeds.includes('Other')) {
changes.dietaryNeedsDetails = undefined;
}

Expand Down
2 changes: 1 addition & 1 deletion apps/src/gamelab/Exporter.js
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ export default {
sessionId: `${getEnvironmentPrefix()}-${project.getCurrentId()}`,
files,
name: project.getCurrentName(),
sdkVersion: '32.0.0',
sdkVersion: '31.0.0',
});

// Important that index.html comes first:
Expand Down
17 changes: 14 additions & 3 deletions apps/src/templates/export/expo/CustomAsset.exported_js
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,25 @@ export default class CustomAsset {
// Ignore this because it throws if the dir already exists on Android
}
}
({ md5 } = await FileSystem.downloadAsync(this.asset.uri, localUri, {
if (this.asset.localUri) {
await FileSystem.copyAsync({
from: this.asset.localUri,
to: localUri,
});
({ md5 } = await FileSystem.getInfoAsync(localUri, {
cache: true,
md5: true,
}));
}));
} else {
({ md5 } = await FileSystem.downloadAsync(this.asset.uri, localUri, {
cache: true,
md5: true,
}));
}
if (md5 !== this.asset.hash) {
throw new Error(
`Downloaded file for asset '${this.fileName} ` +
`Located at ${this.asset.localUri} ` +
`Located at ${localUri} ` +
`failed MD5 integrity check`
);
}
Expand Down
6 changes: 5 additions & 1 deletion apps/src/templates/export/expo/app.json.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"description": "<%- appName %> from Code.org Code Studio.",
"slug": "<%- appName.replace(/([^a-zA-Z0-9_\-]+)/gi, '-') %>",
"privacy": "public",
"sdkVersion": "32.0.0",
"sdkVersion": "31.0.0",
"platforms": ["ios", "android"],
"version": "1.0.0",
"orientation": "portrait",
Expand All @@ -15,8 +15,12 @@
"backgroundColor": "#ffffff"
},
"updates": {
"enabled": false,
"fallbackToCacheTimeout": 0
},
"assetBundlePatterns": [
"**/*"
],
"packagerOpts": {
"assetExts": [
"html",
Expand Down
4 changes: 2 additions & 2 deletions apps/src/templates/export/expo/package.exported_json
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
"eject": "expo eject"
},
"dependencies": {
"expo": "^32.0.0",
"expo": "^31.0.4",
"expo-cli": "^2.6.14",
"react": "16.5.0",
"react-native": "https://github.com/expo/react-native/archive/sdk-32.0.0.tar.gz"
"react-native": "https://github.com/expo/react-native/archive/sdk-31.0.1.tar.gz"
}
}