म्यूटर

म्यूटेट करने वाला एक मिक्सिन है, जो किसी ब्लॉक में अतिरिक्त सीरियलाइज़ेशन (सेव और लोड होने वाला अतिरिक्त स्टेटस) जोड़ता है. उदाहरण के लिए, पहले से मौजूद 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` ब्लॉक, जिसका ड्रॉपडाउन `इससे भागा जा सकता है` पर सेट है. इसमें दो वैल्यू इनपुट हैं.

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

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

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

म्यूटेटर में सीरियलाइज़ेशन हुक के दो जोड़े होते हैं. हुक का एक जोड़ा, 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 पैरामीटर भी मिलता है. इसका इस्तेमाल उन ब्लॉक में किया जाता है जो किसी दूसरे serializer (जैसे, बैकिंग डेटा मॉडल) से सीरियलाइज़ की गई स्थिति का रेफ़रंस देते हैं. पैरामीटर से पता चलता है कि ब्लॉक को डिसीरियलाइज़ करने पर, रेफ़रंस की गई स्थिति उपलब्ध नहीं होगी. इसलिए, ब्लॉक को बैकिंग की सभी स्थितियों को खुद ही सीरियलाइज़ करना चाहिए. उदाहरण के लिए, ऐसा तब होता है, जब किसी ब्लॉक को सीरियल किया जाता है या ब्लॉक को कॉपी करके चिपकाया जाता है.

इसका इस्तेमाल इन दो सामान्य तरीकों से किया जाता है:

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

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

mutationToDom और domToMutation

mutationToDom और domToMutation, सीरियलाइज़ेशन हुक हैं, जो पुराने एक्सएमएल सीरियलाइज़ेशन सिस्टम के साथ काम करते हैं. इन हुक का इस्तेमाल सिर्फ़ तब करें, जब ज़रूरी हो.उदाहरण के लिए, जब किसी ऐसे पुराने कोड-बेस पर काम किया जा रहा हो जिसे अब तक माइग्रेट नहीं किया गया है. इसके अलावा, 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_();
},

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

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

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

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

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

अगर-तो ब्लॉक, जिसमें म्यूटेटर बबल खुला है. इससे, उपयोगकर्ताओं को 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();
}