summaryrefslogtreecommitdiff
path: root/blockly/demos/blocklyfactory/block_exporter_controller.js
blob: e0c192fb3873e51288524328dca07291182bed27 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
/**
 * @license
 * Visual Blocks Editor
 *
 * Copyright 2016 Google Inc.
 * https://developers.google.com/blockly/
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * You may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @fileoverview Javascript for the Block Exporter Controller class. Allows
 * users to export block definitions and generator stubs of their saved blocks
 * easily using a visual interface. Depends on Block Exporter View and Block
 * Exporter Tools classes. Interacts with Export Settings in the index.html.
 *
 * @author quachtina96 (Tina Quach)
 */

'use strict';

goog.provide('BlockExporterController');

goog.require('FactoryUtils');
goog.require('StandardCategories');
goog.require('BlockExporterView');
goog.require('BlockExporterTools');
goog.require('goog.dom.xml');

/**
 * BlockExporter Controller Class
 * @constructor
 *
 * @param {!BlockLibrary.Storage} blockLibStorage - Block Library Storage.
 */
BlockExporterController = function(blockLibStorage) {
  // BlockLibrary.Storage object containing user's saved blocks.
  this.blockLibStorage = blockLibStorage;
  // Utils for generating code to export.
  this.tools = new BlockExporterTools();
  // The ID of the block selector, a div element that will be populated with the
  // block options.
  this.selectorID = 'blockSelector';
  // Map of block types stored in block library to their corresponding Block
  // Option objects.
  this.blockOptions = this.tools.createBlockSelectorFromLib(
      this.blockLibStorage, this.selectorID);
  // View provides the block selector and export settings UI.
  this.view = new BlockExporterView(this.blockOptions);
};

/**
 * Set the block library storage object from which exporter exports.
 *
 * @param {!BlockLibraryStorage} blockLibStorage - Block Library Storage object
 *    that stores the blocks.
 */
BlockExporterController.prototype.setBlockLibraryStorage =
    function(blockLibStorage) {
  this.blockLibStorage = blockLibStorage;
};

/**
 * Get the block library storage object from which exporter exports.
 *
 * @return {!BlockLibraryStorage} blockLibStorage - Block Library Storage object
 *    that stores the blocks.
 */
BlockExporterController.prototype.getBlockLibraryStorage =
    function(blockLibStorage) {
  return this.blockLibStorage;
};

/**
 * Get selected blocks from block selector, pulls info from the Export
 * Settings form in Block Exporter, and downloads code accordingly.
 */
BlockExporterController.prototype.export = function() {
  // Get selected blocks' information.
  var blockTypes = this.view.getSelectedBlockTypes();
  var blockXmlMap = this.blockLibStorage.getBlockXmlMap(blockTypes);

  // Pull block definition(s) settings from the Export Settings form.
  var wantBlockDef = document.getElementById('blockDefCheck').checked;
  var definitionFormat = document.getElementById('exportFormat').value;
  var blockDef_filename = document.getElementById('blockDef_filename').value;

  // Pull block generator stub(s) settings from the Export Settings form.
  var wantGenStub = document.getElementById('genStubCheck').checked;
  var language = document.getElementById('exportLanguage').value;
  var generatorStub_filename = document.getElementById(
      'generatorStub_filename').value;

  if (wantBlockDef) {
    // User wants to export selected blocks' definitions.
    if (!blockDef_filename) {
      // User needs to enter filename.
      alert('Please enter a filename for your block definition(s) download.');
    } else {
      // Get block definition code in the selected format for the blocks.
      var blockDefs = this.tools.getBlockDefinitions(blockXmlMap,
          definitionFormat);
      // Download the file, using .js file ending for JSON or Javascript.
      FactoryUtils.createAndDownloadFile(
          blockDefs, blockDef_filename, 'javascript');
    }
  }

  if (wantGenStub) {
    // User wants to export selected blocks' generator stubs.
    if (!generatorStub_filename) {
      // User needs to enter filename.
      alert('Please enter a filename for your generator stub(s) download.');
    } else {
      // Get generator stub code in the selected language for the blocks.
      var genStubs = this.tools.getGeneratorCode(blockXmlMap,
          language);
      // Get the correct file extension.
      if (language == 'JavaScript') {
        var fileType = 'javascript';
      } else {
        var fileType = 'plain';
      }
      // Download the file.
      FactoryUtils.createAndDownloadFile(
          genStubs, generatorStub_filename, fileType);
    }
  }

};

/**
 * Update the Exporter's block selector with block options generated from blocks
 * stored in block library.
 */
BlockExporterController.prototype.updateSelector = function() {
  // Get previously selected block types.
  var oldSelectedTypes = this.view.getSelectedBlockTypes();

  // Generate options from block library and assign to view.
  this.blockOptions = this.tools.createBlockSelectorFromLib(
      this.blockLibStorage, this.selectorID);
  this.addBlockOptionSelectHandlers();
  this.view.setBlockOptions(this.blockOptions);

  // Select all previously selected blocks.
  for (var i = 0, blockType; blockType = oldSelectedTypes[i]; i++) {
    if (this.blockOptions[blockType]) {
      this.view.select(blockType);
    }
  }

  this.view.listSelectedBlocks();
};

/**
 * Tied to the 'Clear Selected Blocks' button in the Block Exporter.
 * Deselects all blocks in the selector and updates text accordingly.
 */
BlockExporterController.prototype.clearSelectedBlocks = function() {
  this.view.deselectAllBlocks();
  this.view.listSelectedBlocks();
};

/**
 * Tied to the 'All Stored' button in the Block Exporter 'Select' dropdown.
 * Selects all blocks stored in block library for export.
 */
BlockExporterController.prototype.selectAllBlocks = function() {
  var allBlockTypes = this.blockLibStorage.getBlockTypes();
  for (var i = 0, blockType; blockType = allBlockTypes[i]; i++) {
    this.view.select(blockType);
  }
  this.view.listSelectedBlocks();
};

/**
 * Returns the category xml containing all blocks in the block library.
 *
 * @return {Element} Xml for a category to be used in toolbox.
 */
BlockExporterController.prototype.getBlockLibraryCategory = function() {
  return this.tools.generateCategoryFromBlockLib(this.blockLibStorage);
};

/**
 * Add select handlers to each block option to update the view and the selected
 * blocks accordingly.
 */
BlockExporterController.prototype.addBlockOptionSelectHandlers = function() {
  var self = this;

  // Click handler for a block option. Toggles whether or not it's selected and
  // updates helper text accordingly.
  var updateSelectedBlockTypes_ = function(blockOption) {
    // Toggle selected.
    blockOption.setSelected(!blockOption.isSelected());

    // Show currently selected blocks in helper text.
    self.view.listSelectedBlocks();
  };

  // Returns a block option select handler.
  var makeBlockOptionSelectHandler_ = function(blockOption) {
    return function() {
      updateSelectedBlockTypes_(blockOption);
      self.updatePreview();
    };
  };

  // Assign a click handler to each block option.
  for (var blockType in this.blockOptions) {
    var blockOption = this.blockOptions[blockType];
    // Use an additional closure to correctly assign the tab callback.
    blockOption.dom.addEventListener(
        'click', makeBlockOptionSelectHandler_(blockOption));
  }
};

/**
 * Tied to the 'All Used' button in the Block Exporter's 'Select' button.
 * Selects all blocks stored in block library and used in workspace factory.
 */
BlockExporterController.prototype.selectUsedBlocks = function() {
  // Deselect all blocks.
  this.view.deselectAllBlocks();

  // Get list of block types that are in block library and used in workspace
  // factory.
  var storedBlockTypes = this.blockLibStorage.getBlockTypes();
  var sharedBlockTypes = [];
  // Keep list of custom block types used but not in library.
  var unstoredCustomBlockTypes = [];

  for (var i = 0, blockType; blockType = this.usedBlockTypes[i]; i++) {
    if (storedBlockTypes.indexOf(blockType) != -1) {
      sharedBlockTypes.push(blockType);
    } else if (StandardCategories.coreBlockTypes.indexOf(blockType) == -1) {
      unstoredCustomBlockTypes.push(blockType);
    }
  }

  // Select each shared block type.
  for (var i = 0, blockType; blockType = sharedBlockTypes[i]; i++) {
    this.view.select(blockType);
  }
  this.view.listSelectedBlocks();

  if (unstoredCustomBlockTypes.length > 0){
    // Warn user to import block defifnitions and generator code for blocks
    // not in their Block Library nor Blockly's standard library.
    var blockTypesText = unstoredCustomBlockTypes.join(', ');
    var customWarning = 'Custom blocks used in workspace factory but not ' +
        'stored in block library:\n ' + blockTypesText +
        '\n\nDon\'t forget to include block definitions and generator code ' +
        'for these blocks.';
    alert(customWarning);
  }
};

/**
 * Set the array that holds the block types used in workspace factory.
 *
 * @param {!Array.<!string>} usedBlockTypes - Block types used in
 */
BlockExporterController.prototype.setUsedBlockTypes =
    function(usedBlockTypes) {
  this.usedBlockTypes = usedBlockTypes;
};

/**
 * Updates preview code (block definitions and generator stubs) in the exporter
 * preview to reflect selected blocks.
 */
BlockExporterController.prototype.updatePreview = function() {
  // Generate preview code for selected blocks.
  var blockDefs = this.getBlockDefinitionsOfSelected();
  var genStubs = this.getGeneratorStubsOfSelected();

  // Update the text areas containing the code.
  FactoryUtils.injectCode(blockDefs, 'blockDefs_textArea');
  FactoryUtils.injectCode(genStubs, 'genStubs_textArea');
};

/**
 * Returns a map of each selected block's type to its corresponding xml.
 *
 * @return {!Object} a map of each selected block's type (a string) to its
 * corresponding xml element.
 */
BlockExporterController.prototype.getSelectedBlockXmlMap = function() {
  var blockTypes = this.view.getSelectedBlockTypes();
  return this.blockLibStorage.getBlockXmlMap(blockTypes);
};

/**
 * Get block definition code in the selected format for selected blocks.
 *
 * @return {!string} The concatenation of each selected block's language code
 * in the format specified in export settings.
 */
BlockExporterController.prototype.getBlockDefinitionsOfSelected = function() {
  // Get selected blocks' information.
  var blockXmlMap = this.getSelectedBlockXmlMap();

  // Get block definition code in the selected format for the blocks.
  var definitionFormat = document.getElementById('exportFormat').value;
  return this.tools.getBlockDefinitions(blockXmlMap, definitionFormat);
};

/**
 * Get generator stubs in the selected language for selected blocks.
 *
 * @return {!string} The concatenation of each selected block's generator stub
 * in the language specified in export settings.
 */
BlockExporterController.prototype.getGeneratorStubsOfSelected = function() {
  // Get selected blocks' information.
  var blockXmlMap = this.getSelectedBlockXmlMap();

  // Get generator stub code in the selected language for the blocks.
  var language = document.getElementById('exportLanguage').value;
  return this.tools.getGeneratorCode(blockXmlMap, language);
};