Creazione di un nuovo tipo di campo

Prima di creare un nuovo tipo di campo, valuta se uno degli altri metodi per personalizzare i campi è adatto alle tue esigenze. Se la tua applicazione deve archiviare un nuovo tipo di valore o se vuoi creare una nuova UI per un tipo di valore esistente, probabilmente devi creare un nuovo tipo di campo.

Per creare un nuovo campo:

  1. Implementa un costruttore.
  2. Registrare una chiave JSON e implementare fromJson.
  3. Gestisci l'inizializzazione dell'interfaccia utente e degli ascoltatori di eventi sul blocco.
  4. Gestisci lo smaltimento dei listener di eventi (lo smaltimento dell'UI viene gestito automaticamente).
  5. Implementa la gestione del valore.
  6. Aggiungi una rappresentazione testuale del valore del campo per l'accessibilità.
  7. Aggiungi ulteriori funzionalità, ad esempio:
  8. Configura aspetti aggiuntivi del tuo campo, ad esempio:

Questa sezione presuppone che tu abbia letto e abbia familiarità con i contenuti della sezione Struttura di un campo.

Per un esempio di campo personalizzato, consulta la demo dei campi personalizzati.

Implementazione di un costruttore

Il costruttore del campo è responsabile della configurazione del valore iniziale del campo e, facoltativamente, di uno strumento di convalida locale. Il costruttore del campo personalizzato viene chiamato durante l'inizializzazione del blocco di origine, indipendentemente dal fatto che il blocco di origine sia definito in JSON o JavaScript. Quindi, il campo personalizzato non ha accesso al blocco di origine durante la creazione.

Il seguente esempio di codice crea un campo personalizzato denominato GenericField:

class GenericField extends Blockly.Field {
  constructor(value, validator) {
    super(value, validator);

    this.SERIALIZABLE = true;
  }
}

Firma del metodo

I costruttori di campi in genere accettano un valore e uno strumento di convalida locale. Il valore è facoltativo e se non passi un valore (o passi un valore che non supera la convalida della classe), verrà utilizzato il valore predefinito della superclasse. Per la classe Field predefinita, questo valore è null. Se non vuoi questo valore predefinito, assicurati di passare un valore adatto. Il parametro dello strumento di convalida è presente solo per i campi modificabili e in genere è contrassegnato come facoltativo. Scopri di più sugli strumenti di convalida nella documentazione relativa ai convalidatori.

Struttura

La logica all'interno del costruttore deve seguire questo flusso:

  1. Chiama il super costruttore ereditato (tutti i campi personalizzati devono ereditare da Blockly.Field o da una delle sue sottoclassi) per inizializzare correttamente il valore e impostare lo strumento di convalida locale per il tuo campo.
  2. Se il campo è serializzabile, imposta la proprietà corrispondente nel costruttore. I campi modificabili devono essere serializzabili e i campi possono essere modificabili per impostazione predefinita, quindi probabilmente dovresti impostare questa proprietà su true a meno che tu non sappia che non deve essere serializzabile.
  3. (Facoltativo) Applica un'ulteriore personalizzazione (ad esempio, i campi etichetta consentono il trasferimento di una classe CSS, che viene poi applicata al testo).

JSON e registrazione

Nelle definizioni dei blocchi JSON, i campi sono descritti da una stringa (ad es. field_number, field_textinput). Blockly gestisce una mappa da queste stringhe agli oggetti dei campi e chiama fromJson sull'oggetto appropriato durante la costruzione.

Richiama Blockly.fieldRegistry.register per aggiungere il tipo di campo a questa mappa, passando nella classe del campo come secondo argomento:

Blockly.fieldRegistry.register('field_generic', GenericField);

Devi inoltre definire la funzione fromJson. L'implementazione deve innanzitutto rimuovere i riferimenti alle tabelle di stringhe utilizzando replaceMessageReferences, quindi passare i valori al costruttore.

GenericField.fromJson = function(options) {
  const value = Blockly.utils.parsing.replaceMessageReferences(
      options['value']);
  return new CustomFields.GenericField(value);
};

In fase di inizializzazione

Una volta creato, il campo contiene fondamentalmente solo un valore. L'inizializzazione è il luogo in cui viene creato il DOM, il modello (se il campo possiede un modello) e gli eventi sono associati.

Display On-Block

Durante l'inizializzazione, sei responsabile della creazione di tutto ciò di cui hai bisogno per la visualizzazione on-block del campo.

Valori predefiniti, sfondo e testo

La funzione initView predefinita crea un elemento rect di colore chiaro e un elemento text. Se vuoi che il campo abbia entrambi questi elementi, oltre ad altri extra, chiama la funzione initView della superclasse prima di aggiungere il resto degli elementi DOM. Se vuoi che il campo abbia uno di questi elementi, ma non entrambi, puoi utilizzare le funzioni createBorderRect_ o createTextElement_.

Personalizzazione della creazione del DOM

Se il campo è un campo di testo generico (ad es. Input di testo), la creazione del DOM verrà gestita per te. In caso contrario, dovrai eseguire l'override della funzione initView per creare gli elementi DOM necessari durante il rendering futuro del campo.

Ad esempio, un campo a discesa può contenere sia immagini che testo. In initView crea un singolo elemento immagine e un singolo elemento di testo. Durante render_, mostra l'elemento attivo e nasconde l'altro, in base al tipo di opzione selezionata.

La creazione di elementi DOM può essere eseguita utilizzando il metodo Blockly.utils.dom.createSvgElement oppure i metodi tradizionali di creazione DOM.

I requisiti per la visualizzazione sul blocco di un campo sono:

  • Tutti gli elementi DOM devono essere secondari del valore fieldGroup_ del campo. Il gruppo di campi viene creato automaticamente.
  • Tutti gli elementi DOM devono rimanere all'interno delle dimensioni del campo segnalate.

Consulta la sezione Rendering per ulteriori dettagli sulla personalizzazione e sull'aggiornamento del display on-block.

Aggiunta di simboli di testo

Se vuoi aggiungere simboli al testo di un campo (ad esempio il simbolo dei gradi del campo Angolo), puoi aggiungere l'elemento simbolo (solitamente contenuto in un <tspan>) direttamente al valore textElement_ del campo.

Eventi di input

Per impostazione predefinita, i campi registrano gli eventi della descrizione comando e gli eventi mousedown (da utilizzare per mostrare gli editor). Se vuoi rimanere in ascolto di altri tipi di eventi (ad esempio, se vuoi gestire il trascinamento su un campo), devi sostituire la funzione bindEvents_ del campo.

bindEvents_() {
  // Call the superclass function to preserve the default behavior as well.
  super.bindEvents_();

  // Then register your own additional event listeners.
  this.mouseDownWrapper_ =
  Blockly.browserEvents.conditionalBind(this.getClickTarget_(), 'mousedown', this,
      function(event) {
        this.originalMouseX_ = event.clientX;
        this.isMouseDown_ = true;
        this.originalValue_ = this.getValue();
        event.stopPropagation();
      }
  );
  this.mouseMoveWrapper_ =
    Blockly.browserEvents.conditionalBind(document, 'mousemove', this,
      function(event) {
        if (!this.isMouseDown_) {
          return;
        }
        var delta = event.clientX - this.originalMouseX_;
        this.setValue(this.originalValue_ + delta);
      }
  );
  this.mouseUpWrapper_ =
    Blockly.browserEvents.conditionalBind(document, 'mouseup', this,
      function(_event) {
        this.isMouseDown_ = false;
      }
  );
}

In genere, per eseguire l'associazione a un evento devi utilizzare la funzione Blockly.utils.browserEvents.conditionalBind. Questo metodo di associazione degli eventi filtra i tocchi secondari durante i trascinamenti. Se vuoi che il gestore venga eseguito anche nel bel mezzo di un trascinamento in corso, puoi utilizzare la funzione Blockly.browserEvents.bind.

Smaltimento

Se hai registrato listener di eventi personalizzati all'interno della funzione bindEvents_ del campo, sarà necessario annullarne la registrazione all'interno della funzione dispose.

Se hai inizializzato correttamente la visualizzazione del campo (aggiungendo tutti gli elementi DOM a fieldGroup_), il DOM del campo verrà eliminato automaticamente.

Gestione dei valori

→ Per informazioni sul valore di un campo e sul relativo testo, consulta Struttura di un campo.

Ordine di convalida

Diagramma di flusso che descrive l&#39;ordine di esecuzione dei convalidatori

Implementare uno strumento di convalida dei corsi

I campi devono accettare solo determinati valori. Ad esempio, i campi numerici devono accettare solo numeri, i campi colore devono accettare solo colori e così via. Ciò viene garantito tramite convalide di classe e locali. Lo strumento di convalida delle classi segue le stesse regole degli strumenti di convalida locali, tranne per il fatto che viene eseguito anche nel constructor e, di conseguenza, non deve fare riferimento al blocco di origine.

Per implementare lo strumento di convalida delle classi del campo, esegui l'override della funzione doClassValidation_.

doClassValidation_(newValue) {
  if (typeof newValue != 'string') {
    return null;
  }
  return newValue;
};

Gestione di valori validi

Se il valore trasmesso a un campo con setValue è valido, riceverai un callback di doValueUpdate_. Per impostazione predefinita, la funzione doValueUpdate_:

  • Imposta la proprietà value_ su newValue.
  • Imposta la proprietà isDirty_ su true.

Se devi semplicemente archiviare il valore e non vuoi eseguire una gestione personalizzata, non è necessario sostituire doValueUpdate_.

In caso contrario, se vuoi, ad esempio:

  • Spazio di archiviazione personalizzato di newValue.
  • Modifica altre proprietà in base a newValue.
  • Indica se il valore corrente è valido o meno.

Dovrai eseguire l'override di doValueUpdate_:

doValueUpdate_(newValue) {
  super.doValueUpdate_(newValue);
  this.displayValue_ = newValue;
  this.isValueValid_ = true;
}

Gestione dei valori non validi

Se il valore trasmesso al campo con setValue non è valido, riceverai un callback di doValueInvalid_. Per impostazione predefinita, la funzione doValueInvalid_ non esegue alcuna operazione. Ciò significa che, per impostazione predefinita, i valori non validi non verranno mostrati. Significa inoltre che il campo non verrà sottoposto nuovamente a rendering, perché la proprietà isDirty_ non verrà impostata.

Per visualizzare valori non validi, devi sostituire doValueInvalid_. Nella maggior parte dei casi, devi impostare una proprietà displayValue_ sul valore non valido, impostare isDirty_ su true e eseguire l'override del rendering_ affinché la visualizzazione su blocco venga aggiornata in base a displayValue_ anziché a value_.

doValueInvalid_(newValue) {
  this.displayValue_ = newValue;
  this.isDirty_ = true;
  this.isValueValid_ = false;
}

Valori in più parti

Quando il campo contiene un valore con più parti (ad es. elenchi, vettori, oggetti), è consigliabile che le parti vengano gestite come valori singoli.

doClassValidation_(newValue) {
  if (FieldTurtle.PATTERNS.indexOf(newValue.pattern) == -1) {
    newValue.pattern = null;
  }

  if (FieldTurtle.HATS.indexOf(newValue.hat) == -1) {
    newValue.hat = null;
  }

  if (FieldTurtle.NAMES.indexOf(newValue.turtleName) == -1) {
    newValue.turtleName = null;
  }

  if (!newValue.pattern || !newValue.hat || !newValue.turtleName) {
    this.cachedValidatedValue_ = newValue;
    return null;
  }
  return newValue;
}

Nell'esempio precedente, ogni proprietà di newValue viene convalidata singolarmente. Al termine della funzione doClassValidation_, se una singola proprietà non è valida, il valore viene memorizzato nella cache nella proprietà cacheValidatedValue_ prima di restituire null (non valido). La memorizzazione nella cache dell'oggetto con proprietà convalidate singolarmente consente alla funzione doValueInvalid_ di gestirle separatamente, semplicemente eseguendo un controllo !this.cacheValidatedValue_.property, invece di convalidare nuovamente ogni proprietà singolarmente.

Questo pattern per la convalida di valori multiparte può essere utilizzato anche negli strumenti di convalida locali, ma al momento non è possibile applicare questo pattern.

isDirty_

isDirty_ è un flag utilizzato nella funzione setValue e in altre parti del campo per indicare se il campo deve essere sottoposto nuovamente a rendering. Se il valore visualizzato del campo è cambiato, in genere isDirty_ deve essere impostato su true.

Testo

→ Per informazioni su dove viene utilizzato il testo di un campo e su come è diverso dal valore del campo, vedi Struttura di un campo.

Se il testo del campo è diverso dal valore del campo, devi sostituire la funzione getText per fornire il testo corretto.

getText() {
  let text = this.value_.turtleName + ' wearing a ' + this.value_.hat;
  if (this.value_.hat == 'Stovepipe' || this.value_.hat == 'Propeller') {
    text += ' hat';
  }
  return text;
}

Creazione di un editor

Se definisci la funzione showEditor_, Blockly rimane in ascolto dei clic e chiama showEditor_ al momento opportuno. Puoi visualizzare qualsiasi codice HTML nell'editor inserendo uno dei due tag div speciali, denominati DropDownDiv, e WidgetDiv, che appaiono sopra il resto dell'interfaccia utente di Blockly.

DropDownDiv viene utilizzato per fornire editor che risiedono all'interno di una casella collegata a un campo. Si posiziona automaticamente vicino al campo senza superare i limiti visibili. Il selettore dell'angolo e il selettore colori sono buoni esempi di DropDownDiv.

Immagine del selettore angolare

La WidgetDiv viene utilizzata per fornire editor che non risiedono all'interno di una casella. I campi numerici utilizzano WidgetDiv per coprire il campo con una casella di immissione di testo HTML. Al contrario di DropDownDiv gestisce il posizionamento per te, mentre WidgetDiv no. Gli elementi dovranno essere posizionati manualmente. Il sistema di coordinate è espresso in pixel rispetto alla parte superiore sinistra della finestra. L'editor di input di testo è un buon esempio di WidgetDiv.

Immagine dell&#39;editor di input di testo

showEditor_() {
  // Create the widget HTML
  this.editor_ = this.dropdownCreate_();
  Blockly.DropDownDiv.getContentDiv().appendChild(this.editor_);

  // Set the dropdown's background colour.
  // This can be used to make it match the colour of the field.
  Blockly.DropDownDiv.setColour('white', 'silver');

  // Show it next to the field. Always pass a dispose function.
  Blockly.DropDownDiv.showPositionedByField(
      this, this.disposeWidget_.bind(this));
}

Codice di esempio WidgetDiv

showEditor_() {
  // Show the div. This automatically closes the dropdown if it is open.
  // Always pass a dispose function.
  Blockly.WidgetDiv.show(
    this, this.sourceBlock_.RTL, this.widgetDispose_.bind(this));

  // Create the widget HTML.
  var widget = this.createWidget_();
  Blockly.WidgetDiv.getDiv().appendChild(widget);
}

Eseguire la pulizia

Sia DropDownDiv sia WidgetDiv gestiscono l'eliminazione degli elementi HTML del widget, ma devi eliminare manualmente i listener di eventi applicati a questi elementi.

widgetDispose_() {
  for (let i = this.editorListeners_.length, listener;
      listener = this.editorListeners_[i]; i--) {
    Blockly.browserEvents.unbind(listener);
    this.editorListeners_.pop();
  }
}

La funzione dispose viene richiamata in un contesto null sul DropDownDiv. Su WidgetDiv viene chiamata nel contesto di WidgetDiv. In entrambi i casi, è meglio utilizzare la funzione bind quando passi una funzione di eliminazione, come mostrato negli esempi DropDownDiv e WidgetDiv precedenti.

→ Per informazioni sull'eliminazione non specifica di quella degli editor, consulta la sezione Smaltimento.

Aggiornamento del display nel blocco

La funzione render_ viene utilizzata per aggiornare la visualizzazione sul blocco del campo in modo che corrisponda al suo valore interno.

Ecco alcuni esempi comuni:

  • Modificare il testo (elenco a discesa)
  • Modifica il colore (colore)

Valori predefiniti

La funzione render_ predefinita imposta il testo visualizzato sul risultato della funzione getDisplayText_. La funzione getDisplayText_ restituisce il cast della proprietà value_ del campo a una stringa, dopo che è stata troncata per rispettare la lunghezza massima del testo.

Se utilizzi la visualizzazione on-block predefinita e il comportamento del testo predefinito funziona per il tuo campo, non è necessario eseguire l'override di render_.

Se il comportamento del testo predefinito funziona per il tuo campo, ma la visualizzazione sul blocco del campo contiene altri elementi statici, puoi chiamare la funzione render_ predefinita, ma dovrai comunque eseguirne l'override per aggiornare la dimensione del campo.

Se il comportamento del testo predefinito non funziona per il tuo campo o se la visualizzazione sul blocco del campo contiene elementi dinamici aggiuntivi, devi personalizzare la funzione render_.

Diagramma di flusso che descrive come decidere se eseguire l&#39;override del rendering

Personalizzazione del rendering

Se il comportamento di rendering predefinito non funziona per il tuo campo, dovrai definire un comportamento di rendering personalizzato. Ciò può comportare qualsiasi cosa, dall'impostazione di un testo visualizzato personalizzato, alla modifica degli elementi dell'immagine, all'aggiornamento dei colori dello sfondo.

Tutte le modifiche agli attributi DOM sono legali, le uniche due cose da ricordare sono:

  1. Poiché è più efficiente, la creazione del DOM deve essere gestita durante l'inizializzazione.
  2. Devi sempre aggiornare la proprietà size_ in modo che corrisponda alle dimensioni del display sul blocco.
render_() {
  switch(this.value_.hat) {
    case 'Stovepipe':
      this.stovepipe_.style.display = '';
      break;
    case 'Crown':
      this.crown_.style.display = '';
      break;
    case 'Mask':
      this.mask_.style.display = '';
      break;
    case 'Propeller':
      this.propeller_.style.display = '';
      break;
    case 'Fedora':
      this.fedora_.style.display = '';
      break;
  }

  switch(this.value_.pattern) {
    case 'Dots':
      this.shellPattern_.setAttribute('fill', 'url(#polkadots)');
      break;
    case 'Stripes':
      this.shellPattern_.setAttribute('fill', 'url(#stripes)');
      break;
    case 'Hexagons':
      this.shellPattern_.setAttribute('fill', 'url(#hexagons)');
      break;
  }

  this.textContent_.nodeValue = this.value_.turtleName;

  this.updateSize_();
}

Aggiornamento delle dimensioni in corso...

L'aggiornamento della proprietà size_ di un campo è molto importante perché indica al codice di rendering del blocco come posizionare il campo. Il modo migliore per capire esattamente come dovrebbe essere size_ è fare delle prove.

updateSize_() {
  const bbox = this.movableGroup_.getBBox();
  let width = bbox.width;
  let height = bbox.height;
  if (this.borderRect_) {
    width += this.constants_.FIELD_BORDER_RECT_X_PADDING * 2;
    height += this.constants_.FIELD_BORDER_RECT_X_PADDING * 2;
    this.borderRect_.setAttribute('width', width);
    this.borderRect_.setAttribute('height', height);
  }
  // Note how both the width and the height can be dynamic.
  this.size_.width = width;
  this.size_.height = height;
}

Colori del blocco corrispondenti

Se vuoi che gli elementi del campo corrispondano ai colori del blocco a cui sono associati, devi sostituire il metodo applyColour. Dovrai accedere al colore tramite la proprietà di stile del blocco.

applyColour() {
  const sourceBlock = this.sourceBlock_;
  if (sourceBlock.isShadow()) {
    this.arrow_.style.fill = sourceBlock.style.colourSecondary;
  } else {
    this.arrow_.style.fill = sourceBlock.style.colourPrimary;
  }
}

Aggiornamento della modificabilità in corso...

La funzione updateEditable può essere utilizzata per modificare l'aspetto del campo a seconda che sia modificabile o meno. La funzione predefinita fa in modo che lo sfondo abbia/non abbia una risposta al passaggio del mouse (bordo) se è modificabile o non è modificabile. Le dimensioni del display sul blocco non devono cambiare in base alla possibilità di modifica, ma sono consentite tutte le altre modifiche.

updateEditable() {
  if (!this.fieldGroup_) {
    // Not initialized yet.
    return;
  }
  super.updateEditable();

  const group = this.getClickTarget_();
  if (!this.isCurrentlyEditable()) {
    group.style.cursor = 'not-allowed';
  } else {
    group.style.cursor = this.CURSOR;
  }
}

Serializzazione

La serializzazione comporta il salvataggio dello stato del campo in modo che possa essere ricaricato nell'area di lavoro in un secondo momento.

Lo stato dell'area di lavoro include sempre il valore del campo, ma potrebbe includere anche altri stati, come lo stato dell'interfaccia utente del campo. Ad esempio, se il campo era una mappa zoomabile che consentiva all'utente di selezionare i paesi, potresti serializzare il livello di zoom.

Se il campo è serializzabile, devi impostare la proprietà SERIALIZABLE su true.

Blockly fornisce due set di hook di serializzazione per i campi. Una coppia di hook funziona con il nuovo sistema di serializzazione JSON, mentre l'altra coppia funziona con il precedente sistema di serializzazione XML.

saveState e loadState

saveState e loadState sono hook di serializzazione che funzionano con il nuovo sistema di serializzazione JSON.

In alcuni casi non è necessario fornirli, perché le implementazioni predefinite funzioneranno. Se (1) il tuo campo è una sottoclasse diretta della classe Blockly.Field di base, (2) il tuo valore è un tipo serializzabile JSON e (3) devi solo serializzare il valore, l'implementazione predefinita andrà bene.

In caso contrario, la funzione saveState dovrebbe restituire un valore/oggetto serializzabile JSON che rappresenta lo stato del campo. Inoltre, la funzione loadState deve accettare lo stesso oggetto/valore JSON serializzabile e applicarlo al campo.

saveState() {
  return {
    'country': this.getValue(),  // Value state
    'zoom': this.getZoomLevel(), // UI state
  };
}

loadState(state) {
  this.setValue(state['country']);
  this.setZoomLevel(state['zoom']);
}

Serializzazione e backup completi dei dati

saveState riceve anche un parametro facoltativo doFullSerialization. Viene utilizzato dai campi che normalmente fanno riferimento a uno stato serializzato da un diverso serializzatore (come modelli di dati di supporto). Il parametro indica che lo stato a cui viene fatto riferimento non sarà disponibile quando il blocco viene deserializzato, quindi il campo dovrebbe eseguire tutte le operazioni di serializzazione. Ciò vale, ad esempio, quando un singolo blocco viene serializzato o quando un blocco viene copiato e incollato.

Ecco due casi d'uso comuni:

  • Quando un singolo blocco viene caricato in un'area di lavoro in cui il modello dei dati di supporto non esiste, il campo dispone di informazioni sufficienti nel proprio stato per creare un nuovo modello dei dati.
  • Quando un blocco viene copiato e incollato, il campo crea sempre un nuovo modello di dati di supporto invece di fare riferimento a uno esistente.

Un campo che lo utilizza è quello della variabile integrata. Normalmente serializza l'ID della variabile a cui fa riferimento, ma se doFullSerialization è true, serializza tutto il suo stato.

saveState(doFullSerialization) {
  const state = {'id': this.variable_.getId()};
  if (doFullSerialization) {
    state['name'] = this.variable_.name;
    state['type'] = this.variable_.type;
  }
  return state;
}

loadState(state) {
  const variable = Blockly.Variables.getOrCreateVariablePackage(
      this.getSourceBlock().workspace,
      state['id'],
      state['name'],   // May not exist.
      state['type']);  // May not exist.
  this.setValue(variable.getId());
}

In questo modo, il campo Variabile garantisce che, se viene caricato in un'area di lavoro in cui la relativa variabile non esiste, possa creare una nuova variabile a cui fare riferimento.

toXml e fromXml

toXml e fromXml sono hook di serializzazione che funzionano con il vecchio sistema di serializzazione XML. Utilizza questi hook solo se necessario (ad esempio se stai lavorando su un vecchio codebase di cui non è stata ancora eseguita la migrazione), altrimenti usa saveState e loadState.

La funzione toXml deve restituire un nodo XML che rappresenta lo stato del campo. Inoltre, la funzione fromXml deve accettare lo stesso nodo XML e applicarlo al campo.

toXml(fieldElement) {
  fieldElement.textContent = this.getValue();
  fieldElement.setAttribute('zoom', this.getZoomLevel());
  return fieldElement;
}

fromXml(fieldElement) {
  this.setValue(fieldElement.textContent);
  this.setZoomLevel(fieldElement.getAttribute('zoom'));
}

Proprietà modificabili e serializzabili

La proprietà EDITABLE determina se il campo deve avere una UI per indicare che è possibile interagire. Il valore predefinito è true.

La proprietà SERIALIZABLE determina se il campo deve essere serializzato. Il valore predefinito è false. Se questa proprietà è true, potresti dover fornire le funzioni di serializzazione e deserializzazione (vedi Serializzazione).

Personalizzazione del cursore

La proprietà CURSOR determina il cursore visualizzato dagli utenti quando passano il mouse sopra il campo. Deve essere una stringa di cursore CSS valida. Per impostazione predefinita, viene utilizzato il cursore definito da .blocklyDraggable, ovvero il cursore di trascinamento.