एक्सटेंशन और म्यूटर

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

म्यूटेटर एक खास तरह का एक्सटेंशन है, जो ब्लॉक में कस्टम क्रम और कभी-कभी यूज़र इंटरफ़ेस (यूआई) जोड़ता है.

एक्सटेंशन

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

// This extension sets the block's tooltip to be a function which displays
// the parent block's tooltip (if it exists).
Blockly.Extensions.register(
    'parent_tooltip_extension',
    function() { // this refers to the block that the extension is being run on
      var thisBlock = this;
      this.setTooltip(function() {
        var parent = thisBlock.getParent();
        return (parent && parent.getInputsInline() && parent.tooltip) ||
            Blockly.Msg.MATH_NUMBER_TOOLTIP;
      });
    });

एक्सटेंशन को "रजिस्टर करना" ज़रूरी है, ताकि उन्हें किसी स्ट्रिंग कुंजी से जोड़ा जा सके. इसके बाद, ब्लॉक में एक्सटेंशन लागू करने के लिए, इस स्ट्रिंग कुंजी को अपने ब्लॉक टाइप की JSON परिभाषा की extensions प्रॉपर्टी में असाइन किया जा सकता है.

{
 //...,
 "extensions": ["parent_tooltip_extension",]
}

आप एक साथ कई एक्सटेंशन भी जोड़ सकते हैं. ध्यान दें कि extensions प्रॉपर्टी एक कलेक्शन होनी चाहिए. भले ही, आपने सिर्फ़ एक एक्सटेंशन लागू किया हो.

{
  //...,
  "extensions": ["parent_tooltip_extension", "break_warning_extension"],
}

मिक्स

ब्लॉकली, उन स्थितियों के लिए भी एक आसान तरीका उपलब्ध कराता है जहां आपको कुछ प्रॉपर्टी/हेल्पर फ़ंक्शन को ब्लॉक में जोड़ना है, लेकिन उन्हें तुरंत नहीं चलाना. ऐसा करने से, आपको ऐसा मिक्सिन ऑब्जेक्ट रजिस्टर करने की अनुमति मिलती है जिसमें आपकी सभी अतिरिक्त प्रॉपर्टी/तरीका होता है. इसके बाद, मिक्सिन ऑब्जेक्ट को किसी फ़ंक्शन में रैप किया जाता है. इससे, दिए गए ब्लॉक टाइप का कोई इंस्टेंस बनने पर, मिक्सिन लागू होता है.

Blockly.Extensions.registerMixin('my_mixin', {
  someProperty: 'a cool value',

  someMethod: function() {
    // Do something cool!
  }
))`

किसी दूसरे एक्सटेंशन की तरह ही, मिक्सिन से जुड़ी स्ट्रिंग कुंजियों का भी JSON में रेफ़रंस दिया जा सकता है.

{
 //...,
 "extensions": ["my_mixin"],
}

म्यूटेटर

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

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

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

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

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

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

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-proceds ब्लॉक हैं. आम तौर पर, वे बैकिंग डेटा मॉडल के रेफ़रंस को सीरीज़ के तौर पर सेट करते हैं, जो अपनी स्थिति सेव करता है. हालांकि, अगर doFullSerialization पैरामीटर सही है, तो वे अपनी पूरी स्थिति को क्रम से लगाते हैं. शेयर किए जा सकने वाले प्रोसेस ब्लॉक इसका इस्तेमाल यह पक्का करने के लिए करते हैं कि जब उन्हें कॉपी करके चिपकाया जाता है, तो वे किसी मौजूदा मॉडल का रेफ़रंस देने के बजाय, एक नया बैकिंग डेटा मॉडल बनाते हैं.

म्यूटेशनToDom और 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 आपके ब्लॉक में एक डिफ़ॉल्ट "म्यूटेटर" यूज़र इंटरफ़ेस (यूआई) जोड़ देगा.

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

लिखें और डीकंपोज़ करें

डिफ़ॉल्ट यूज़र इंटरफ़ेस (यूआई), 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 फ़ंक्शन तय किया गया है, तो 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"
}

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

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

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

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