How can I duplicate a form using js / dojo / jquery?

allows me to have a form like:

<form name="frmMail" method="POST" action="save.do" dojoType="dijit.form.Form" onSubmit="return this.validate();">
    <label for="mailSubject">subject</label>
    <input type="text" name="mailSubject"
            dojoType="dijit.form.ValidationTextBox"
            required="true"
            value="" />
    <label for="mailContent">Content</label>        
    <textarea name="mailContent"
            dojoType="dijit.Editor"
            propercase="true" ></textarea>

    <button dojoType="dijit.form.Button" type="submit" id="save" name="save" >Save</button>
</form>     

Is it possible to duplicate this code using javascript? Right now I put all this in a div and I copy the innerHtml data, but that is not good, because the identifier of each element is duplicated. any thoughts?

UPDATE

<form name="frmMail" dojoattachevent="onreset:_onReset,onsubmit:_onSubmit" dojoattachpoint="containerNode" action="save.do" method="POST" id="dijit_form_Form_0" dir="ltr" widgetid="dijit_form_Form_0">
<label for="mailSubject">subject</label>
<div wairole="presentation" id="widget_dijit_form_ValidationTextBox_0" class="dijit dijitReset dijitInlineTable dijitLeft dijitTextBox dijitValidationTextBox dijitTextBoxError dijitValidationTextBoxError dijitError dijitTextBoxFocused dijitValidationTextBoxFocused dijitTextBoxErrorFocused dijitValidationTextBoxErrorFocused dijitErrorFocused dijitFocused" role="presentation" dir="ltr" widgetid="dijit_form_ValidationTextBox_0"><div class="dijitReset dijitValidationContainer"><input type="text" wairole="presentation" readonly="" tabindex="-1" value="? " class="dijitReset dijitInputField dijitValidationIcon dijitValidationInner" role="presentation"></div><div class="dijitReset dijitInputField dijitInputContainer"><input type="text" name="mailSubject" autocomplete="off" dojoattachpoint="textbox,focusNode" class="dijitReset dijitInputInner" aria-invalid="true" id="dijit_form_ValidationTextBox_0" tabindex="0" aria-required="true" value=""></div></div>
<label for="mailContent">Content</label>        
<div widgetid="dijit_Editor_0" class=" dijitEditor"><div>
<div lang="" dojoattachpoint="containerNode" tabindex="0" wairole="toolbar" class="dijit dijitToolbar" role="toolbar" id="dijit_Toolbar_0" dir="ltr" widgetid="dijit_Toolbar_0"><span lang="" class="dijit dijitReset dijitInline dijitButton dijitButtonDisabled dijitDisabled" dir="ltr" widgetid="dijit_form_Button_5"><span dojoattachevent="ondijitclick:_onButtonClick" class="dijitReset dijitInline dijitButtonNode"><span waistate="labelledby-dijit_form_Button_5_label" wairole="button" dojoattachpoint="titleNode,focusNode" class="dijitReset dijitStretch dijitButtonContents" role="button" aria-labelledby="dijit_form_Button_5_label" id="dijit_form_Button_5" title="Undo" style="-moz-user-select: none;" aria-disabled="true"><span dojoattachpoint="iconNode" class="dijitReset dijitInline dijitIcon dijitEditorIcon dijitEditorIconUndo"></span><span class="dijitReset dijitToggleButtonIconChar">?</span><span dojoattachpoint="containerNode" id="dijit_form_Button_5_label" class="dijitReset dijitInline dijitButtonText dijitDisplayNone">Undo</span></span></span><input type="button" dojoattachpoint="valueNode" class="dijitOffScreen" value="" disabled=""></span><span lang="" class="dijit dijitReset dijitInline dijitButton dijitButtonDisabled dijitDisabled" dir="ltr" widgetid="dijit_form_Button_6"><span dojoattachevent="ondijitclick:_onButtonClick" class="dijitReset dijitInline dijitButtonNode"><span waistate="labelledby-dijit_form_Button_6_label" wairole="button" dojoattachpoint="titleNode,focusNode" class="dijitReset dijitStretch dijitButtonContents" role="button" aria-labelledby="dijit_form_Button_6_label" id="dijit_form_Button_6" title="Redo" style="-moz-user-select: none;" aria-disabled="true"><span dojoattachpoint="iconNode" class="dijitReset dijitInline dijitIcon dijitEditorIcon dijitEditorIconRedo"></span><span class="dijitReset dijitToggleButtonIconChar">?</span><span dojoattachpoint="containerNode" id="dijit_form_Button_6_label" class="dijitReset dijitInline dijitButtonText dijitDisplayNone">Redo</span></span></span><input type="button" dojoattachpoint="valueNode" class="dijitOffScreen" value="" disabled=""></span><div wairole="presentation" class="dijitToolbarSeparator dijitInline" role="presentation" id="dijit_ToolbarSeparator_0" widgetid="dijit_ToolbarSeparator_0" style="-moz-user-select: none;"></div><span lang="" class="dijit dijitReset dijitInline dijitButton dijitButtonDisabled dijitDisabled" dir="ltr" widgetid="dijit_form_Button_7"><span dojoattachevent="ondijitclick:_onButtonClick" class="dijitReset dijitInline dijitButtonNode"><span waistate="labelledby-dijit_form_Button_7_label" wairole="button" dojoattachpoint="titleNode,focusNode" class="dijitReset dijitStretch dijitButtonContents" role="button" aria-labelledby="dijit_form_Button_7_label" id="dijit_form_Button_7" title="Cut" style="-moz-user-select: none;" aria-disabled="true"><span dojoattachpoint="iconNode" class="dijitReset dijitInline dijitIcon dijitEditorIcon dijitEditorIconCut"></span><span class="dijitReset dijitToggleButtonIconChar">?</span><span dojoattachpoint="containerNode" id="dijit_form_Button_7_label" class="dijitReset dijitInline dijitButtonText dijitDisplayNone">Cut</span></span></span><input type="button" dojoattachpoint="valueNode" class="dijitOffScreen" value="" disabled=""></span><span lang="" class="dijit dijitReset dijitInline dijitButton dijitButtonDisabled dijitDisabled" dir="ltr" widgetid="dijit_form_Button_8"><span dojoattachevent="ondijitclick:_onButtonClick" class="dijitReset dijitInline dijitButtonNode"><span waistate="labelledby-dijit_form_Button_8_label" wairole="button" dojoattachpoint="titleNode,focusNode" class="dijitReset dijitStretch dijitButtonContents" role="button" aria-labelledby="dijit_form_Button_8_label" id="dijit_form_Button_8" title="Copy" style="-moz-user-select: none;" aria-disabled="true"><span dojoattachpoint="iconNode" class="dijitReset dijitInline dijitIcon dijitEditorIcon dijitEditorIconCopy"></span><span class="dijitReset dijitToggleButtonIconChar">?</span><span dojoattachpoint="containerNode" id="dijit_form_Button_8_label" class="dijitReset dijitInline dijitButtonText dijitDisplayNone">Copy</span></span></span><input type="button" dojoattachpoint="valueNode" class="dijitOffScreen" value="" disabled=""></span><span lang="" class="dijit dijitReset dijitInline dijitButton" dir="ltr" widgetid="dijit_form_Button_9"><span dojoattachevent="ondijitclick:_onButtonClick" class="dijitReset dijitInline dijitButtonNode"><span waistate="labelledby-dijit_form_Button_9_label" wairole="button" dojoattachpoint="titleNode,focusNode" class="dijitReset dijitStretch dijitButtonContents" role="button" aria-labelledby="dijit_form_Button_9_label" id="dijit_form_Button_9" tabindex="-1" title="Paste" style="-moz-user-select: none;" aria-disabled="false"><span dojoattachpoint="iconNode" class="dijitReset dijitInline dijitIcon dijitEditorIcon dijitEditorIconPaste"></span><span class="dijitReset dijitToggleButtonIconChar">?</span><span dojoattachpoint="containerNode" id="dijit_form_Button_9_label" class="dijitReset dijitInline dijitButtonText dijitDisplayNone">Paste</span></span></span><input type="button" dojoattachpoint="valueNode" class="dijitOffScreen" value=""></span><div wairole="presentation" class="dijitToolbarSeparator dijitInline" role="presentation" id="dijit_ToolbarSeparator_1" widgetid="dijit_ToolbarSeparator_1" style="-moz-user-select: none;"></div><span lang="" class="dijit dijitReset dijitInline dijitToggleButton" dir="ltr" widgetid="dijit_form_ToggleButton_0"><span dojoattachevent="ondijitclick:_onButtonClick" class="dijitReset dijitInline dijitButtonNode"><span waistate="labelledby-dijit_form_ToggleButton_0_label" wairole="button" dojoattachpoint="titleNode,focusNode" class="dijitReset dijitStretch dijitButtonContents" role="button" aria-labelledby="dijit_form_ToggleButton_0_label" id="dijit_form_ToggleButton_0" tabindex="-1" title="Bold" style="-moz-user-select: none;" aria-disabled="false" aria-pressed="false"><span dojoattachpoint="iconNode" class="dijitReset dijitInline dijitIcon dijitEditorIcon dijitEditorIconBold"></span><span class="dijitReset dijitToggleButtonIconChar">?</span><span dojoattachpoint="containerNode" id="dijit_form_ToggleButton_0_label" class="dijitReset dijitInline dijitButtonText dijitDisplayNone">Bold</span></span></span><input type="button" dojoattachpoint="valueNode" class="dijitOffScreen" value=""></span><span lang="" class="dijit dijitReset dijitInline dijitToggleButton" dir="ltr" widgetid="dijit_form_ToggleButton_1"><span dojoattachevent="ondijitclick:_onButtonClick" class="dijitReset dijitInline dijitButtonNode"><span waistate="labelledby-dijit_form_ToggleButton_1_label" wairole="button" dojoattachpoint="titleNode,focusNode" class="dijitReset dijitStretch dijitButtonContents" role="button" aria-labelledby="dijit_form_ToggleButton_1_label" id="dijit_form_ToggleButton_1" tabindex="-1" title="Italic" style="-moz-user-select: none;" aria-disabled="false" aria-pressed="false"><span dojoattachpoint="iconNode" class="dijitReset dijitInline dijitIcon dijitEditorIcon dijitEditorIconItalic"></span><span class="dijitReset dijitToggleButtonIconChar">?</span><span dojoattachpoint="containerNode" id="dijit_form_ToggleButton_1_label" class="dijitReset dijitInline dijitButtonText dijitDisplayNone">Italic</span></span></span><input type="button" dojoattachpoint="valueNode" class="dijitOffScreen" value=""></span><span lang="" class="dijit dijitReset dijitInline dijitToggleButton" dir="ltr" widgetid="dijit_form_ToggleButton_2"><span dojoattachevent="ondijitclick:_onButtonClick" class="dijitReset dijitInline dijitButtonNode"><span waistate="labelledby-dijit_form_ToggleButton_2_label" wairole="button" dojoattachpoint="titleNode,focusNode" class="dijitReset dijitStretch dijitButtonContents" role="button" aria-labelledby="dijit_form_ToggleButton_2_label" id="dijit_form_ToggleButton_2" tabindex="-1" title="Underline" style="-moz-user-select: none;" aria-disabled="false" aria-pressed="false"><span dojoattachpoint="iconNode" class="dijitReset dijitInline dijitIcon dijitEditorIcon dijitEditorIconUnderline"></span><span class="dijitReset dijitToggleButtonIconChar">?</span><span dojoattachpoint="containerNode" id="dijit_form_ToggleButton_2_label" class="dijitReset dijitInline dijitButtonText dijitDisplayNone">Underline</span></span></span><input type="button" dojoattachpoint="valueNode" class="dijitOffScreen" value=""></span><span lang="" class="dijit dijitReset dijitInline dijitToggleButton" dir="ltr" widgetid="dijit_form_ToggleButton_3"><span dojoattachevent="ondijitclick:_onButtonClick" class="dijitReset dijitInline dijitButtonNode"><span waistate="labelledby-dijit_form_ToggleButton_3_label" wairole="button" dojoattachpoint="titleNode,focusNode" class="dijitReset dijitStretch dijitButtonContents" role="button" aria-labelledby="dijit_form_ToggleButton_3_label" id="dijit_form_ToggleButton_3" tabindex="-1" title="Strikethrough" style="-moz-user-select: none;" aria-disabled="false" aria-pressed="false"><span dojoattachpoint="iconNode" class="dijitReset dijitInline dijitIcon dijitEditorIcon dijitEditorIconStrikethrough"></span><span class="dijitReset dijitToggleButtonIconChar">?</span><span dojoattachpoint="containerNode" id="dijit_form_ToggleButton_3_label" class="dijitReset dijitInline dijitButtonText dijitDisplayNone">Strikethrough</span></span></span><input type="button" dojoattachpoint="valueNode" class="dijitOffScreen" value=""></span><div wairole="presentation" class="dijitToolbarSeparator dijitInline" role="presentation" id="dijit_ToolbarSeparator_2" widgetid="dijit_ToolbarSeparator_2" style="-moz-user-select: none;"></div><span lang="" class="dijit dijitReset dijitInline dijitButton" dir="ltr" widgetid="dijit_form_Button_10"><span dojoattachevent="ondijitclick:_onButtonClick" class="dijitReset dijitInline dijitButtonNode"><span waistate="labelledby-dijit_form_Button_10_label" wairole="button" dojoattachpoint="titleNode,focusNode" class="dijitReset dijitStretch dijitButtonContents" role="button" aria-labelledby="dijit_form_Button_10_label" id="dijit_form_Button_10" tabindex="-1" title="Numbered List" style="-moz-user-select: none;" aria-disabled="false"><span dojoattachpoint="iconNode" class="dijitReset dijitInline dijitIcon dijitEditorIcon dijitEditorIconInsertOrderedList"></span><span class="dijitReset dijitToggleButtonIconChar">?</span><span dojoattachpoint="containerNode" id="dijit_form_Button_10_label" class="dijitReset dijitInline dijitButtonText dijitDisplayNone">Numbered List</span></span></span><input type="button" dojoattachpoint="valueNode" class="dijitOffScreen" value=""></span><span lang="" class="dijit dijitReset dijitInline dijitButton" dir="ltr" widgetid="dijit_form_Button_11"><span dojoattachevent="ondijitclick:_onButtonClick" class="dijitReset dijitInline dijitButtonNode"><span waistate="labelledby-dijit_form_Button_11_label" wairole="button" dojoattachpoint="titleNode,focusNode" class="dijitReset dijitStretch dijitButtonContents" role="button" aria-labelledby="dijit_form_Button_11_label" id="dijit_form_Button_11" tabindex="-1" title="Bullet List" style="-moz-user-select: none;" aria-disabled="false"><span dojoattachpoint="iconNode" class="dijitReset dijitInline dijitIcon dijitEditorIcon dijitEditorIconInsertUnorderedList"></span><span class="dijitReset dijitToggleButtonIconChar">?</span><span dojoattachpoint="containerNode" id="dijit_form_Button_11_label" class="dijitReset dijitInline dijitButtonText dijitDisplayNone">Bullet List</span></span></span><input type="button" dojoattachpoint="valueNode" class="dijitOffScreen" value=""></span><span lang="" class="dijit dijitReset dijitInline dijitButton" dir="ltr" widgetid="dijit_form_Button_12"><span dojoattachevent="ondijitclick:_onButtonClick" class="dijitReset dijitInline dijitButtonNode"><span waistate="labelledby-dijit_form_Button_12_label" wairole="button" dojoattachpoint="titleNode,focusNode" class="dijitReset dijitStretch dijitButtonContents" role="button" aria-labelledby="dijit_form_Button_12_label" id="dijit_form_Button_12" tabindex="-1" title="Indent" style="-moz-user-select: none;" aria-disabled="false"><span dojoattachpoint="iconNode" class="dijitReset dijitInline dijitIcon dijitEditorIcon dijitEditorIconIndent"></span><span class="dijitReset dijitToggleButtonIconChar">?</span><span dojoattachpoint="containerNode" id="dijit_form_Button_12_label" class="dijitReset dijitInline dijitButtonText dijitDisplayNone">Indent</span></span></span><input type="button" dojoattachpoint="valueNode" class="dijitOffScreen" value=""></span><span lang="" class="dijit dijitReset dijitInline dijitButton dijitButtonDisabled dijitDisabled" dir="ltr" widgetid="dijit_form_Button_13"><span dojoattachevent="ondijitclick:_onButtonClick" class="dijitReset dijitInline dijitButtonNode"><span waistate="labelledby-dijit_form_Button_13_label" wairole="button" dojoattachpoint="titleNode,focusNode" class="dijitReset dijitStretch dijitButtonContents" role="button" aria-labelledby="dijit_form_Button_13_label" id="dijit_form_Button_13" title="Outdent" style="-moz-user-select: none;" aria-disabled="true"><span dojoattachpoint="iconNode" class="dijitReset dijitInline dijitIcon dijitEditorIcon dijitEditorIconOutdent"></span><span class="dijitReset dijitToggleButtonIconChar">?</span><span dojoattachpoint="containerNode" id="dijit_form_Button_13_label" class="dijitReset dijitInline dijitButtonText dijitDisplayNone">Outdent</span></span></span><input type="button" dojoattachpoint="valueNode" class="dijitOffScreen" value="" disabled=""></span><div wairole="presentation" class="dijitToolbarSeparator dijitInline" role="presentation" id="dijit_ToolbarSeparator_3" widgetid="dijit_ToolbarSeparator_3" style="-moz-user-select: none;"></div><span lang="" class="dijit dijitReset dijitInline dijitButton" dir="ltr" widgetid="dijit_form_Button_14"><span dojoattachevent="ondijitclick:_onButtonClick" class="dijitReset dijitInline dijitButtonNode"><span waistate="labelledby-dijit_form_Button_14_label" wairole="button" dojoattachpoint="titleNode,focusNode" class="dijitReset dijitStretch dijitButtonContents" role="button" aria-labelledby="dijit_form_Button_14_label" id="dijit_form_Button_14" tabindex="-1" title="Align Left" style="-moz-user-select: none;" aria-disabled="false"><span dojoattachpoint="iconNode" class="dijitReset dijitInline dijitIcon dijitEditorIcon dijitEditorIconJustifyLeft"></span><span class="dijitReset dijitToggleButtonIconChar">?</span><span dojoattachpoint="containerNode" id="dijit_form_Button_14_label" class="dijitReset dijitInline dijitButtonText dijitDisplayNone">Align Left</span></span></span><input type="button" dojoattachpoint="valueNode" class="dijitOffScreen" value=""></span><span lang="" class="dijit dijitReset dijitInline dijitButton" dir="ltr" widgetid="dijit_form_Button_15"><span dojoattachevent="ondijitclick:_onButtonClick" class="dijitReset dijitInline dijitButtonNode"><span waistate="labelledby-dijit_form_Button_15_label" wairole="button" dojoattachpoint="titleNode,focusNode" class="dijitReset dijitStretch dijitButtonContents" role="button" aria-labelledby="dijit_form_Button_15_label" id="dijit_form_Button_15" tabindex="-1" title="Align Right" style="-moz-user-select: none;" aria-disabled="false"><span dojoattachpoint="iconNode" class="dijitReset dijitInline dijitIcon dijitEditorIcon dijitEditorIconJustifyRight"></span><span class="dijitReset dijitToggleButtonIconChar">?</span><span dojoattachpoint="containerNode" id="dijit_form_Button_15_label" class="dijitReset dijitInline dijitButtonText dijitDisplayNone">Align Right</span></span></span><input type="button" dojoattachpoint="valueNode" class="dijitOffScreen" value=""></span><span lang="" class="dijit dijitReset dijitInline dijitButton" dir="ltr" widgetid="dijit_form_Button_16"><span dojoattachevent="ondijitclick:_onButtonClick" class="dijitReset dijitInline dijitButtonNode"><span waistate="labelledby-dijit_form_Button_16_label" wairole="button" dojoattachpoint="titleNode,focusNode" class="dijitReset dijitStretch dijitButtonContents" role="button" aria-labelledby="dijit_form_Button_16_label" id="dijit_form_Button_16" tabindex="-1" title="Align Center" style="-moz-user-select: none;" aria-disabled="false"><span dojoattachpoint="iconNode" class="dijitReset dijitInline dijitIcon dijitEditorIcon dijitEditorIconJustifyCenter"></span><span class="dijitReset dijitToggleButtonIconChar">?</span><span dojoattachpoint="containerNode" id="dijit_form_Button_16_label" class="dijitReset dijitInline dijitButtonText dijitDisplayNone">Align Center</span></span></span><input type="button" dojoattachpoint="valueNode" class="dijitOffScreen" value=""></span><span lang="" class="dijit dijitReset dijitInline dijitButton" dir="ltr" widgetid="dijit_form_Button_17"><span dojoattachevent="ondijitclick:_onButtonClick" class="dijitReset dijitInline dijitButtonNode"><span waistate="labelledby-dijit_form_Button_17_label" wairole="button" dojoattachpoint="titleNode,focusNode" class="dijitReset dijitStretch dijitButtonContents" role="button" aria-labelledby="dijit_form_Button_17_label" id="dijit_form_Button_17" tabindex="-1" title="Justify" style="-moz-user-select: none;" aria-disabled="false"><span dojoattachpoint="iconNode" class="dijitReset dijitInline dijitIcon dijitEditorIcon dijitEditorIconJustifyFull"></span><span class="dijitReset dijitToggleButtonIconChar">?</span><span dojoattachpoint="containerNode" id="dijit_form_Button_17_label" class="dijitReset dijitInline dijitButtonText dijitDisplayNone">Justify</span></span></span><input type="button" dojoattachpoint="valueNode" class="dijitOffScreen" value=""></span></div></div><div class="dijitEditorIFrameContainer"><iframe frameborder="0" id="dijit_Editor_0_iframe" style="border: medium none; width: 100%; height: 300px;" src="javascript:parent.dijit.byId(&quot;dijit_Editor_0&quot;)._iframeSrc" class="dijitEditorIFrame"></iframe></div><div></div></div><textarea propercase="true" dojotype="dijit.Editor" name="mailContent" id="dijit_Editor_0" dir="ltr" class="dijitEditor" style="display: block; position: absolute; top: -1000px;"></textarea>

<span class="dijit dijitReset dijitInline dijitButton" dir="ltr" widgetid="save"><span dojoattachevent="ondijitclick:_onButtonClick" class="dijitReset dijitInline dijitButtonNode"><span waistate="labelledby-save_label" wairole="button" dojoattachpoint="titleNode,focusNode" class="dijitReset dijitStretch dijitButtonContents" role="button" aria-labelledby="save_label" id="save" tabindex="0" style="-moz-user-select: none;"><span dojoattachpoint="iconNode" class="dijitReset dijitInline dijitIcon"></span><span class="dijitReset dijitToggleButtonIconChar">?</span><span dojoattachpoint="containerNode" id="save_label" class="dijitReset dijitInline dijitButtonText">Save</span></span></span><input type="submit" dojoattachpoint="valueNode" class="dijitOffScreen" value="" name="save"></span>

+3
source share
7 answers

In jQuery, you can easily change idelements in an element quite easily:

var newForm = $('form')
                       .clone()
                       .find('[id]') // find elements with an id set
                           .attr('id', function(idx, oldId) { // dynamically set each of these elements
                               return 'new_' + oldId; // to new_ + the old id value
                           })
                       .end(); // return to the form selection

I am not familiar enough with Dojo to find out if something like this can be achieved, but I expect it to be possible.

+1
source

, , , , 100% , , Dojo. - Dojo:

var oldform = dojo.query('form'),
    newform = dojo.clone(oldform);

newform.query('[id]').forEach(function(el){
    el.id = 'new_' + el.id;
});
+1

dojo

                dojo.query('[id]', newform).forEach(function(e){
                e.id = 'new_' + e.id;
            });
+1

. Dijit - JS, DOM, DOM , .

(dojo.clone DOM node) , Dojo . , . dojo.parser.parse() / . , innerHTML parse()

id = "save", . id , , dojo.query. , , , , .

+1

, :

var i = 0; //somewhere globally

var formClone = document.frmMail.cloneNode(true);
formClone.save.id = "save" + i;
i++;
document.body.appendChild(formClone); // adding somewhere

DEMO (, jQuery ... ;))

: cloneNode(), :

, cloneNode !

, . .

0

You can do this with jQuery, but you will have to change the identifier in the same way as in your raw DOM impl.

var form = $('form');
var clonedForm = form.clone();
clonedForm.find('button').attr('id', 'save2');
form.after(clonedForm);

You can see it in action at http://jsfiddle.net/ydBpR/ .

0
source

We must use the parser if we want to convert dom nodes into widgets.

var oldform = dojo.query('form'),
var newform = dojo.clone(oldform);

newform.query('[id]').forEach(function(el){
    el.id = 'new_' + el.id;
});


dojo.body().appendChild(newform);
//force body to be parsed for widgets
dojo.parser.parse(dojo.body());
0
source

Source: https://habr.com/ru/post/1774726/


All Articles