म्यूटर

म्यूटेटर एक मिक्सइन होता है. यह किसी ब्लॉक में अतिरिक्त सीरियलाइज़ेशन (अतिरिक्त स्थिति, जिसे सेव और लोड किया जाता है) जोड़ता है. उदाहरण के लिए, बिल्ट-इन controls_if और list_create_with ब्लॉक को अतिरिक्त सीरियलाइज़ेशन की ज़रूरत होती है, ताकि वे यह सेव कर सकें कि उनके पास कितने इनपुट हैं. यह ब्लॉक के आकार को बदलने के लिए, यूज़र इंटरफ़ेस (यूआई) भी जोड़ सकता है.

सूची बनाने वाले ब्लॉक के तीन म्यूटेशन: कोई इनपुट नहीं, तीन इनपुट, और पाँच इनपुट.

if/do ब्लॉक के दो म्यूटेशन: if-do और if-do-else-if-do-else.

ध्यान दें कि ब्लॉक का आकार बदलने का मतलब यह ज़रूरी नहीं है कि आपको अतिरिक्त सीरियलाइज़ेशन की ज़रूरत हो. उदाहरण के लिए, math_number_property ब्लॉक का आकार बदलता है. हालांकि, यह ड्रॉपडाउन फ़ील्ड के आधार पर ऐसा करता है. इस फ़ील्ड की वैल्यू पहले से ही क्रम से लगाई जाती है. इसलिए, यह सिर्फ़ फ़ील्ड वैलडेटर का इस्तेमाल कर सकता है. इसे म्यूटेटर की ज़रूरत नहीं होती.

`math_number_property` ब्लॉक में ड्रॉपडाउन को `even` पर सेट किया गया है. इसमें एक वैल्यू इनपुट है. `math_number_property`
ब्लॉक में ड्रॉपडाउन को `divisible by` पर सेट किया गया है. इसमें दो वैल्यू
इनपुट हैं.

आपको म्यूटेटर की ज़रूरत कब होती है और कब नहीं, इस बारे में ज़्यादा जानने के लिए सीरियलाइज़ेशन पेज देखें.

अगर आपने कुछ वैकल्पिक तरीके दिए हैं, तो म्यूटेटर उपयोगकर्ताओं को ब्लॉक के आकार बदलने के लिए, पहले से मौजूद यूज़र इंटरफ़ेस (यूआई) भी उपलब्ध कराते हैं.

सीरियलाइज़ेशन हुक

म्यूटेटर के पास, सीरियलाइज़ेशन हुक के दो जोड़े होते हैं. हुक का एक पेयर, JSON सीरियलाइज़ेशन के नए सिस्टम के साथ काम करता है. वहीं, दूसरा पेयर, XML सीरियलाइज़ेशन के पुराने सिस्टम के साथ काम करता है. आपको इनमें से कम से कम एक जोड़ी देनी होगी.

saveExtraState और loadExtraState

saveExtraState और loadExtraState, सीरियलाइज़ेशन हुक हैं. ये JSON सीरियलाइज़ेशन के नए सिस्टम के साथ काम करते हैं. saveExtraState, JSON में सेव की जा सकने वाली वैल्यू दिखाता है. यह वैल्यू, ब्लॉक की अतिरिक्त स्थिति को दिखाती है. साथ ही, loadExtraState, JSON में सेव की जा सकने वाली उसी वैल्यू को स्वीकार करता है और उसे ब्लॉक पर लागू करता है.

// These are the serialization hooks for the lists_create_with block.
saveExtraState: function() {
  return {
    'itemCount': this.itemCount_,
  };
},

loadExtraState: function(state) {
  this.itemCount_ = state['itemCount'];
  // This is a helper function which adds or removes inputs from the block.
  this.updateShape_();
},

इससे मिलने वाला JSON ऐसा दिखेगा:

{
  "type": "lists_create_with",
  "extraState": {
    "itemCount": 3 // or whatever the count is
  }
}

किसी भी राज्य की जानकारी नहीं है

अगर सीरियलाइज़ किए जाने पर आपका ब्लॉक डिफ़ॉल्ट स्थिति में है, तो आपका saveExtraState तरीका यह दिखाने के लिए null को वापस ला सकता है. अगर आपकी saveExtraState विधि null दिखाती है, तो JSON में कोई extraState प्रॉपर्टी नहीं जोड़ी जाती. इससे आपकी सेव की गई फ़ाइल का साइज़ कम रहता है.

पूरा सीरियललाइज़ेशन और डेटा का बैक अप लेना

saveExtraState को एक वैकल्पिक doFullSerialization पैरामीटर भी मिलता है. इस कुकी का इस्तेमाल उन ब्लॉक के लिए किया जाता है जो किसी दूसरे सीरियलाइज़र (जैसे कि बैकिंग डेटा मॉडल) से सीरियल किए गए स्टेट को रेफ़रंस करते हैं. यह पैरामीटर यह सिग्नल देता है कि ब्लॉक को डिसिरियलाइज़ करने पर, रेफ़र की गई स्थिति उपलब्ध नहीं होगी. इसलिए, ब्लॉक को अपनी सभी बैकिंग स्थिति को खुद ही सीरियललाइज़ करना चाहिए. उदाहरण के लिए, ऐसा तब होता है, जब किसी ब्लॉक को क्रम से लगाया जाता है या जब किसी ब्लॉक को कॉपी करके चिपकाया जाता है.

इसके दो सामान्य इस्तेमाल के उदाहरण यहां दिए गए हैं:

  • जब किसी ब्लॉक को ऐसे वर्कस्पेस में लोड किया जाता है जहां बैकिंग डेटा मॉडल मौजूद नहीं है, तो उसके पास नया डेटा मॉडल बनाने के लिए ज़रूरी जानकारी होती है.
  • किसी ब्लॉक को कॉपी करके चिपकाने पर, मौजूदा डेटा मॉडल का रेफ़रंस देने के बजाय हमेशा एक नया डेटा मॉडल बनता है.

इस सुविधा का इस्तेमाल करने वाले कुछ ब्लॉक, @blockly/block-shareable-procedures ब्लॉक हैं. आम तौर पर, ये बैकअप डेटा मॉडल के रेफ़रंस को क्रम से लगाते हैं. यह मॉडल, इनकी स्थिति को सेव करता है. हालांकि, अगर doFullSerialization पैरामीटर सही है, तो वे अपने सभी स्टेट को क्रम से लगाते हैं. शेयर किए जा सकने वाले प्रोसीज़र ब्लॉक, इसका इस्तेमाल यह पक्का करने के लिए करते हैं कि कॉपी-पेस्ट किए जाने पर, वे किसी मौजूदा मॉडल को रेफ़रंस करने के बजाय, एक नया बैकिंग डेटा मॉडल बनाएं.

mutationToDom और domToMutation

mutationToDom और domToMutation, सीरियलाइज़ेशन हुक हैं. ये पुराने XML सीरियलाइज़ेशन सिस्टम के साथ काम करते हैं. इन हुक का इस्तेमाल सिर्फ़ तब करें, जब ऐसा करना ज़रूरी हो.उदाहरण के लिए, अगर आपको ऐसे पुराने कोड-बेस पर काम करना है जिसे अब तक माइग्रेट नहीं किया गया है. ऐसा न होने पर, saveExtraState और loadExtraState का इस्तेमाल करें.

mutationToDom एक एक्सएमएल नोड दिखाता है. यह नोड, ब्लॉक की अतिरिक्त स्थिति को दिखाता है. साथ ही, domToMutation उसी एक्सएमएल नोड को स्वीकार करता है और ब्लॉक पर स्थिति लागू करता है.

// These are the old XML serialization hooks for the lists_create_with block.
mutationToDom: function() {
  // You *must* create a <mutation></mutation> element.
  // This element can have children.
  var container = Blockly.utils.xml.createElement('mutation');
  container.setAttribute('items', this.itemCount_);
  return container;
},

domToMutation: function(xmlElement) {
  this.itemCount_ = parseInt(xmlElement.getAttribute('items'), 10);
  // This is a helper function which adds or removes inputs from the block.
  this.updateShape_();
},

इससे मिलने वाला XML कुछ ऐसा दिखेगा:

<block type="lists_create_with">
  <mutation items="3"></mutation>
</block>

अगर आपके mutationToDom फ़ंक्शन से कोई वैल्यू नहीं मिलती है, तो एक्सएमएल में कोई अतिरिक्त एलिमेंट नहीं जोड़ा जाएगा.

यूज़र इंटरफ़ेस (यूआई) हुक

अगर आपने अपने म्यूटेटर के हिस्से के तौर पर कुछ फ़ंक्शन दिए हैं, तो Blockly आपके ब्लॉक में डिफ़ॉल्ट "म्यूटेटर" यूज़र इंटरफ़ेस (यूआई) जोड़ देगा.

म्यूटेटर बबल खुला होने के साथ if-do ब्लॉक. इससे उपयोगकर्ताओं को if-do ब्लॉक में else-if और else क्लॉज़ जोड़ने की सुविधा मिलती है.

अगर आपको अतिरिक्त सीरियलाइज़ेशन जोड़ना है, तो इस यूज़र इंटरफ़ेस (यूआई) का इस्तेमाल करना ज़रूरी नहीं है. आपके पास blocks-plus-minus प्लगिन जैसे कस्टम यूज़र इंटरफ़ेस (यूआई) का इस्तेमाल करने का विकल्प होता है. इसके अलावा, यूज़र इंटरफ़ेस (यूआई) का इस्तेमाल न करने का भी विकल्प होता है!

जोड़ना और तोड़ना

डिफ़ॉल्ट यूज़र इंटरफ़ेस (यूआई), compose और decompose फ़ंक्शन पर निर्भर करता है.

decompose ब्लॉक को छोटे-छोटे सब-ब्लॉक में "एक्सप्लोड" करता है. इन सब-ब्लॉक को इधर-उधर ले जाया जा सकता है, जोड़ा जा सकता है, और मिटाया जा सकता है. इस फ़ंक्शन को "टॉप ब्लॉक" दिखाना चाहिए. यह म्यूटेटर वर्कस्पेस में मौजूद मुख्य ब्लॉक होता है, जिससे सब-ब्लॉक कनेक्ट होते हैं.

compose इसके बाद, सब-ब्लॉक के कॉन्फ़िगरेशन की व्याख्या करता है और उनका इस्तेमाल मुख्य ब्लॉक में बदलाव करने के लिए करता है. इस फ़ंक्शन को "टॉप ब्लॉक" को पैरामीटर के तौर पर स्वीकार करना चाहिए. यह "टॉप ब्लॉक" decompose ने दिखाया था.

ध्यान दें कि इन फ़ंक्शन को "बदले जा रहे" ब्लॉक में "मिक्स कर दिया जाता है". इसलिए, this का इस्तेमाल उस ब्लॉक को रेफ़र करने के लिए किया जा सकता है.

// These are the decompose and compose functions for the lists_create_with block.
decompose: function(workspace) {
  // This is a special sub-block that only gets created in the mutator UI.
  // It acts as our "top block"
  var topBlock = workspace.newBlock('lists_create_with_container');
  topBlock.initSvg();

  // Then we add one sub-block for each item in the list.
  var connection = topBlock.getInput('STACK').connection;
  for (var i = 0; i < this.itemCount_; i++) {
    var itemBlock = workspace.newBlock('lists_create_with_item');
    itemBlock.initSvg();
    connection.connect(itemBlock.previousConnection);
    connection = itemBlock.nextConnection;
  }

  // And finally we have to return the top-block.
  return topBlock;
},

// The container block is the top-block returned by decompose.
compose: function(topBlock) {
  // First we get the first sub-block (which represents an input on our main block).
  var itemBlock = topBlock.getInputTargetBlock('STACK');

  // Then we collect up all of the connections of on our main block that are
  // referenced by our sub-blocks.
  // This relates to the saveConnections hook (explained below).
  var connections = [];
  while (itemBlock && !itemBlock.isInsertionMarker()) {  // Ignore insertion markers!
    connections.push(itemBlock.valueConnection_);
    itemBlock = itemBlock.nextConnection &&
        itemBlock.nextConnection.targetBlock();
  }

  // Then we disconnect any children where the sub-block associated with that
  // child has been deleted/removed from the stack.
  for (var i = 0; i < this.itemCount_; i++) {
    var connection = this.getInput('ADD' + i).connection.targetConnection;
    if (connection && connections.indexOf(connection) == -1) {
      connection.disconnect();
    }
  }

  // Then we update the shape of our block (removing or adding iputs as necessary).
  // `this` refers to the main block.
  this.itemCount_ = connections.length;
  this.updateShape_();

  // And finally we reconnect any child blocks.
  for (var i = 0; i < this.itemCount_; i++) {
    connections[i].reconnect(this, 'ADD' + i);
  }
},

saveConnections

वैकल्पिक तौर पर, saveConnections फ़ंक्शन को भी तय किया जा सकता है. यह डिफ़ॉल्ट यूज़र इंटरफ़ेस (यूआई) के साथ काम करता है. इस फ़ंक्शन की मदद से, अपने मुख्य ब्लॉक (जो मुख्य वर्कस्पेस पर मौजूद है) के बच्चों को उन सब-ब्लॉक से जोड़ा जा सकता है जो आपके म्यूटेटर वर्कस्पेस में मौजूद हैं. इसके बाद, इस डेटा का इस्तेमाल करके यह पक्का किया जा सकता है कि जब आपके सब-ब्लॉक को फिर से व्यवस्थित किया जाता है, तब आपका compose फ़ंक्शन आपके मुख्य ब्लॉक के बच्चों को सही तरीके से फिर से कनेक्ट करता है.

saveConnections को, आपके decompose फ़ंक्शन से मिले "टॉप ब्लॉक" को पैरामीटर के तौर पर स्वीकार करना चाहिए. अगर saveConnections फ़ंक्शन को तय किया गया है, तो Blockly, compose को कॉल करने से पहले इसे कॉल करेगा.

saveConnections: function(topBlock) {
  // First we get the first sub-block (which represents an input on our main block).
  var itemBlock = topBlock.getInputTargetBlock('STACK');

  // Then we go through and assign references to connections on our main block
  // (input.connection.targetConnection) to properties on our sub blocks
  // (itemBlock.valueConnection_).
  var i = 0;
  while (itemBlock) {
    // `this` refers to the main block (which is being "mutated").
    var input = this.getInput('ADD' + i);
    // This is the important line of this function!
    itemBlock.valueConnection_ = input && input.connection.targetConnection;
    i++;
    itemBlock = itemBlock.nextConnection &&
        itemBlock.nextConnection.targetBlock();
  }
},

पंजीकृत किया जा रहा है

म्यूटेटर, मिक्सइन का एक खास टाइप है. इसलिए, आपको इन्हें भी रजिस्टर करना होगा. इसके बाद ही, इनका इस्तेमाल अपने ब्लॉक टाइप की JSON परिभाषा में किया जा सकेगा.

// Function signature.
Blockly.Extensions.registerMutator(name, mixinObj, opt_helperFn, opt_blockList);

// Example call.
Blockly.Extensions.registerMutator(
    'controls_if_mutator',
    { /* mutator methods */ },
    undefined,
    ['controls_if_elseif', 'controls_if_else']);
  • name: यह एक स्ट्रिंग है, जिसे म्यूटेटर से जोड़ा जाता है. इससे JSON में इसका इस्तेमाल किया जा सकता है.
  • mixinObj: यह एक ऐसा ऑब्जेक्ट होता है जिसमें म्यूटेशन के अलग-अलग तरीके शामिल होते हैं. उदाहरण के लिए, saveExtraState और loadExtraState.
  • opt_helperFn: यह एक वैकल्पिक सहायक फ़ंक्शन है. यह मिक्सइन के मिक्स होने के बाद ब्लॉक पर चलेगा.
  • opt_blockList: यह ब्लॉक टाइप (स्ट्रिंग के तौर पर) का एक वैकल्पिक कलेक्शन है. अगर यूज़र इंटरफ़ेस (यूआई) के तरीके भी तय किए गए हैं, तो इन्हें डिफ़ॉल्ट म्यूटेटर यूज़र इंटरफ़ेस (यूआई) में फ़्लायआउट में जोड़ा जाएगा.

ध्यान दें कि एक्सटेंशन के उलट, हर ब्लॉक टाइप में सिर्फ़ एक म्यूटेटर हो सकता है.

{
  //...
  "mutator": "controls_if_mutator"
}

हेल्पर फ़ंक्शन

मिक्सइन के साथ-साथ, म्यूटेटर एक हेल्पर फ़ंक्शन रजिस्टर कर सकता है. यह फ़ंक्शन, दिए गए टाइप के हर ब्लॉक पर तब चलता है, जब उसे बना दिया जाता है और mixinObj जोड़ दिया जाता है. इसका इस्तेमाल, म्यूटेशन में अतिरिक्त ट्रिगर या इफ़ेक्ट जोड़ने के लिए किया जा सकता है.

उदाहरण के लिए, सूची जैसे ब्लॉक में एक हेल्पर जोड़ा जा सकता है. यह हेल्पर, आइटम की शुरुआती संख्या सेट करता है:

var helper = function() {
  this.itemCount_ = 5;
  this.updateShape();
}