-
Notifications
You must be signed in to change notification settings - Fork 2
/
ardublockly_design.js
552 lines (514 loc) · 20 KB
/
ardublockly_design.js
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
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
/**
* @license Licensed under the Apache License, Version 2.0 (the "License"):
* http://www.apache.org/licenses/LICENSE-2.0
*
* @fileoverview JavaScript to configure front end design for Ardublockly app.
*/
'use strict';
/** Create a namespace for the application. */
var Ardublockly = Ardublockly || {};
/** Initialises all the design related JavaScript. */
Ardublockly.designJsInit = function() {
Ardublockly.materializeJsInit();
Ardublockly.resizeToggleToolboxBotton();
Ardublockly.sketchNameSizeEffect();
Ardublockly.sketchNameSet();
};
/**
* Initialises all required components from materialize framework.
* The be executed on document ready.
*/
Ardublockly.materializeJsInit = function() {
// Navigation bar
$('.button-collapse').sideNav({
menuWidth: 240,
activationWidth: 70,
edge: 'left'
});
// Drop down menus
$('.dropdown-button').dropdown({hover: false});
// Overlay content panels using modals (android dialogs)
$('.modal-trigger').leanModal({
dismissible: true,
opacity: .5,
in_duration: 200,
out_duration: 250
});
// Pop-up tool tips
$('.tooltipped').tooltip({'delay': 50});
// Select menus
$('select').material_select();
$('#copy_ard_code').click(function(){
try{
var t = $("<textarea>");
// t.css({"visibility":"none"})
$("body").append(t);
t.val( Ardublockly.PREV_ARDUINO_CODE_ ).select();
document.execCommand("copy");
t.remove();
Materialize.toast('<span class="yellow-text"> Code copied! </span>', 2000, 'rounded')
}catch(e){}
})
// $(window).bind('hashchange', function() {
var source = window.location.hash.toLowerCase();
if( source.length > 3){
// if( $.head(''))
// './examples/'+ source.substr(1) +'.xml'
if(! Ardublockly.loadServerXmlFile('./examples/'+ source.substr(1) +'.xml') ){
window.history.back()
}
$('#sketch_name').val(source);
}
// console.log(source);
// var source = $(this).data('source-id');
// originalHash = newHash;
// });
$('.top_menu').click(function(){
var source = $(this).data('source-id');
Ardublockly.loadServerXmlFile('./examples/'+ source +'.xml');
$('#sketch_name').val(source);
window.location.hash = source;
})
};``
/** Binds the event listeners relevant to the page design. */
Ardublockly.bindDesignEventListeners = function() {
// Resize blockly workspace on window resize
window.addEventListener(
'resize', Ardublockly.resizeBlocklyWorkspace, false);
// Display/hide the XML load button when the XML collapsible header is clicked
document.getElementById('xml_collapsible_header').addEventListener(
'click', Ardublockly.buttonLoadXmlCodeDisplay);
// Toggle the content height on click to the IDE output collapsible header
document.getElementById('ide_output_collapsible_header').addEventListener(
'click', function() {
Ardublockly.contentHeightToggle();
});
// Display/hide the additional IDE buttons when mouse over/out of play button
$('#button_ide_large').mouseenter(function() {
Ardublockly.showExtraIdeButtons(true);
});
$('#ide_buttons_wrapper').mouseleave(function() {
Ardublockly.showExtraIdeButtons(false);
});
};
/**
* Displays or hides the 'load textarea xml' button based on the state of the
* collapsible 'xml_collapsible_body'.
*/
Ardublockly.buttonLoadXmlCodeDisplay = function() {
var xmlCollapsibleBody = document.getElementById('xml_collapsible_body');
// Waiting 400 ms to check status due to the animation delay (300 ms)
setTimeout(function() {
if (xmlCollapsibleBody.style.display == 'none') {
$('#button_load_xml').hide();
} else {
$('#button_load_xml').fadeIn('slow');
}
}, 400);
};
/**
* Changes the IDE launch buttons based on the option indicated in the argument.
* @param {!string} value One of the 3 possible values from the drop down select
* in the settings modal: 'upload', 'verify', or 'open'.
*/
Ardublockly.changeIdeButtonsDesign = function(value) {
var buttonLeft = document.getElementById('button_ide_left');
var iconLeft = document.getElementById('button_ide_left_icon');
var buttonMiddle = document.getElementById('button_ide_middle');
var iconMiddle = document.getElementById('button_ide_middle_icon');
var buttonLarge = document.getElementById('button_ide_large');
var iconLarge = document.getElementById('button_ide_large_icon');
if (value === 'upload') {
buttonLeft.className =
buttonLeft.className.replace(/arduino_\S+/, 'arduino_yellow');
iconLeft.className = 'mdi-action-open-in-browser';
buttonMiddle.className =
buttonMiddle.className.replace(/arduino_\S+/, 'arduino_teal');
iconMiddle.className = 'mdi-navigation-check';
buttonLarge.className =
buttonLarge.className.replace(/arduino_\S+/, 'arduino_orange');
iconLarge.className = 'mdi-av-play-arrow';
} else if (value === 'verify') {
buttonLeft.className =
buttonLeft.className.replace(/arduino_\S+/, 'arduino_yellow');
iconLeft.className = 'mdi-action-open-in-browser';
buttonMiddle.className =
buttonMiddle.className.replace(/arduino_\S+/, 'arduino_orange');
iconMiddle.className = 'mdi-av-play-arrow';
buttonLarge.className =
buttonLarge.className.replace(/arduino_\S+/, 'arduino_teal');
iconLarge.className = 'mdi-navigation-check';
} else if (value === 'open') {
buttonLeft.className =
buttonLeft.className.replace(/arduino_\S+/, 'arduino_teal');
iconLeft.className = 'mdi-navigation-check';
buttonMiddle.className =
buttonMiddle.className.replace(/arduino_\S+/, 'arduino_orange');
iconMiddle.className = 'mdi-av-play-arrow';
buttonLarge.className =
buttonLarge.className.replace(/arduino_\S+/, 'arduino_yellow');
iconLarge.className = 'mdi-action-open-in-browser';
}
};
/**
* Displays or hides the additional Arduino IDE action buttons.
* Hide/display effects done with CCS3 transitions on visibility and opacity.
* @param {!boolean} show Indicates if the extra buttons are to be shown.
*/
Ardublockly.showExtraIdeButtons = function(show) {
var IdeButtonLeft = document.getElementById('button_ide_left');
var IdeButtonMiddle = document.getElementById('button_ide_middle');
if (show) {
// prevent previously set time-out to hide buttons while trying to show them
clearTimeout(Ardublockly.outHoldtimeoutHandle);
clearTimeout(Ardublockly.hidetimeoutHandle);
IdeButtonMiddle.style.visibility = 'visible';
IdeButtonMiddle.style.opacity = '1';
Ardublockly.showtimeoutHandle = setTimeout(function() {
IdeButtonLeft.style.visibility = 'visible';
IdeButtonLeft.style.opacity = '1';
}, 50);
} else {
// As the mouse out can be accidental, only hide them after a delay
Ardublockly.outHoldtimeoutHandle = setTimeout(function() {
// Prevent show time-out to affect the hiding of the buttons
clearTimeout(Ardublockly.showtimeoutHandle);
IdeButtonLeft.style.visibility = 'hidden';
IdeButtonLeft.style.opacity = '0';
Ardublockly.hidetimeoutHandle = setTimeout(function() {
IdeButtonMiddle.style.visibility = 'hidden';
IdeButtonMiddle.style.opacity = '0';
}, 50);
}, 200);
}
};
/**
* Shows or hides the spinner around the large IDE button.
* @param {!boolean} active True turns ON the spinner, false OFF.
*/
Ardublockly.largeIdeButtonSpinner = function(active) {
var spinner = document.getElementById('button_ide_large_spinner');
var buttonIdeLarge = document.getElementById('button_ide_large');
var buttonClass = buttonIdeLarge.className;
if (active) {
spinner.style.display = 'block';
buttonIdeLarge.className = buttonIdeLarge.className + ' grey';
} else {
spinner.style.display = 'none';
buttonIdeLarge.className = buttonClass.replace(' grey', '');
}
};
/**
* Sets the toolbox HTML element to be display or not and change the visibility
* button to reflect the new state.
* When the toolbox is visible it should display the "visibility-off" icon with
* no background, and the opposite when toolbox is hidden.
* @param {!boolean} show Indicates if the toolbox should be set visible.
*/
Ardublockly.displayToolbox = function(show) {
var toolbox = $('.blocklyToolboxDiv');
var toolboxTree = $('.blocklyTreeRoot');
var button = document.getElementById('button_toggle_toolbox');
var buttonIcon = document.getElementById('button_toggle_toolbox_icon');
// Because firing multiple clicks can confuse the animation, create an overlay
// element to stop clicks (due to the materialize framework controlling the
// event listeners is better to do it this way for easy framework update).
var elLocation = $('#button_toggle_toolbox').offset();
jQuery('<div/>', {
id: 'toolboxButtonScreen',
css: {
position: 'fixed',
top: elLocation.top,
left: elLocation.left,
height: $('#button_toggle_toolbox').height(),
width: $('#button_toggle_toolbox').width(),
cursor: 'pointer',
zIndex: 12
},
}).appendTo('body');
var classOn = 'button_toggle_toolbox_on';
var classOff = 'button_toggle_toolbox_off';
var visOn = 'mdi-action-visibility';
var visOff = 'mdi-action-visibility-off';
if (show) {
toolbox.show();
button.className = button.className.replace(classOn, classOff);
buttonIcon.className = buttonIcon.className.replace(visOn, visOff);
toolbox.animate(
{height: document.getElementById('content_blocks').style.height}, 300,
function() {
toolboxTree.css('overflow-y', 'auto');
window.dispatchEvent(new Event('resize'));
$('#toolboxButtonScreen').remove();
});
} else {
toolboxTree.css('overflow-y', 'hidden');
buttonIcon.className = buttonIcon.className.replace(visOff, visOn);
toolbox.animate({height: 38}, 300, function() {
button.className = button.className.replace(classOff, classOn);
toolbox.fadeOut(350, 'linear', function() {
window.dispatchEvent(new Event('resize'));
setTimeout(function() { toolbox.height(38); }, 100);
$('#toolboxButtonScreen').remove();
});
});
}
};
/**
* Resizes the button to toggle the toolbox visibility to the width of the
* toolbox.
* The toolbox width does not change with workspace width, so safe to do once.
*/
Ardublockly.resizeToggleToolboxBotton = function() {
window.dispatchEvent(new Event('resize'));
var button = $('#button_toggle_toolbox');
// Sets the toolbox toggle button width to that of the toolbox
if (Ardublockly.isToolboxVisible() && Ardublockly.blocklyToolboxWidth()) {
// For some reason normal set style and getElementById didn't work
button.width(Ardublockly.blocklyToolboxWidth());
button[0].style.display = '';
}
};
/** Resizes the container for the Blockly workspace. */
Ardublockly.resizeBlocklyWorkspace = function() {
var contentBlocks = document.getElementById('content_blocks');
var wrapperPanelSize =
Ardublockly.getBBox_(document.getElementById('blocks_panel'));
contentBlocks.style.top = wrapperPanelSize.y + 'px';
contentBlocks.style.left = wrapperPanelSize.x + 'px';
// Height and width need to be set, read back, then set again to
// compensate for scrollbars.
contentBlocks.style.height = wrapperPanelSize.height + 'px';
contentBlocks.style.height =
(2 * wrapperPanelSize.height - contentBlocks.offsetHeight) + 'px';
contentBlocks.style.width = wrapperPanelSize.width + 'px';
contentBlocks.style.width =
(2 * wrapperPanelSize.width - contentBlocks.offsetWidth) + 'px';
};
/**
* Sets the text for a "Materialize Modal" (like an android Dialog) to have
* alert-like HTML messages.
* @param {!string} title HTML to include in title.
* @param {!element} body HTML to include in body.
* @param {boolean=} confirm Indicates if the user is shown and option to just
* 'Ok' or 'Ok and cancel'.
* @param {string=|function=} callback If confirm option is selected this would
* be the function called when clicked 'OK'.
*/
Ardublockly.materialAlert = function(title, body, confirm, callback) {
$('#gen_alert_title').text(title);
$('#gen_alert_body').text('');
$('#gen_alert_body').append(body);
if (confirm == true) {
$('#gen_alert_cancel_link').css({'display': 'block'});
if (callback) {
$('#gen_alert_ok_link').bind('click', callback);
}
} else {
$('#gen_alert_cancel_link').css({'display': 'none'});
$('#gen_alert_ok_link').unbind('click');
}
$('#gen_alert').openModal();
window.location.hash = '';
};
/** Opens the modal that displays the "not connected to server" message. */
Ardublockly.openNotConnectedModal = function() {
$('#not_running_dialog').openModal({
dismissible: true,
opacity: .5,
in_duration: 200,
out_duration: 250
});
};
/** Opens the modal that displays the Settings. */
Ardublockly.openSettingsModal = function() {
$('#settings_dialog').openModal({
dismissible: true,
opacity: .5,
in_duration: 200,
out_duration: 250
});
};
/**
* Opens the modal that allows selection on additional toolbox categories.
* @param {!element} htmlContent HTML to include in modal body.
*/
Ardublockly.openAdditionalBlocksModal = function(htmlContent) {
$('#blocks_menu_body').text('');
$('#blocks_menu_body').append(htmlContent);
$('#blocks_menu').openModal({
dismissible: true,
opacity: .5,
in_duration: 200,
out_duration: 250
});
};
/**
* Creates an HTML node with the blocks category information from arguments.
* @param {!string} title Text to include as category title.
* @param {!string} description TExt to include in as description.
* @param {!function} clickBind Function to bind to the tick box click.
* @return {!element} HTML element to display the category content.
*/
Ardublockly.createExtraBlocksCatHtml = function(title, description, clickBind) {
var tickId = title.replace(/\s+/g, '');
var tickHtml = document.createElement('input');
tickHtml.type = 'checkbox';
tickHtml.id = tickId;
tickHtml.addEventListener('click', function() {
clickBind(document.getElementById(tickId).checked);
});
var tickLabelHtml = document.createElement('label');
tickLabelHtml.htmlFor = tickId;
tickLabelHtml.className = 'modal_label_title';
tickLabelHtml.appendChild(document.createTextNode(title));
var separatorHtml = document.createElement('div');
separatorHtml.className = 'divider';
var descriptionHthml = document.createElement('div');
descriptionHthml.appendChild(document.createTextNode(description));
var htmlContent = document.createElement('div');
htmlContent.className = 'modal_section';
htmlContent.appendChild(tickHtml);
htmlContent.appendChild(tickLabelHtml);
htmlContent.appendChild(separatorHtml);
htmlContent.appendChild(descriptionHthml);
return htmlContent;
};
/**
* Displays a short message for 4 seconds in the form of a Materialize toast.
* @param {!string} message Text to be temporarily displayed.
*/
Ardublockly.MaterialToast = function(message) {
Materialize.toast(message, 4000);
};
/**
* Populates the Arduino IDE output content area and triggers the visual
* highlight to call for the user attention.
* @param {!element} bodyEl HTML to include into IDE output content area.
*/
Ardublockly.arduinoIdeOutput = function(bodyEl) {
var ideOuputContent = document.getElementById('content_ide_output');
ideOuputContent.innerHTML = '';
ideOuputContent.appendChild(bodyEl);
Ardublockly.highlightIdeOutputHeader();
};
/**
* Clears the content of the Arduino IDE output element to a default text.
* @param {!element} bodyEl HTML to include into IDE output content area.
*/
Ardublockly.resetIdeOutputContent = function(bodyEl) {
var ideOuputContent = document.getElementById('content_ide_output');
ideOuputContent.innerHTML = '<span class="arduino_dialog_out">' +
Ardublockly.getLocalStr('arduinoOpWaiting') + '</span>';
};
/**
* Initialises the sketch name input text JavaScript to dynamically adjust its
* width to the width of its contents.
*/
Ardublockly.sketchNameSizeEffect = function() {
var resizeInput = function() {
$(this).attr('size', $(this).val().length);
};
var correctInput = function() {
// If nothing in the input, add default name
if ($(this).val() == '') {
$(this).val('Sketch_Name');
$(this).attr('size', 10);
}
// Replace all spaces with underscores
$(this).val($(this).val().replace(/ /g, '_'));
};
var sketchNameInput = $('#sketch_name');
sketchNameInput.keydown(resizeInput).each(resizeInput);
sketchNameInput.blur(correctInput);
};
/**
* Sets a string to the SketchName input field and triggers the events set from
* Ardublockly.sketchNameSizeEffect().
* @param {string?} newName Optional string to place in the sketch_name input.
*/
Ardublockly.sketchNameSet = function(newName) {
var sketchNewName = newName || '';
var sketchNameInput = $('#sketch_name');
sketchNameInput.val(sketchNewName);
sketchNameInput.attr('size', sketchNewName.length);
sketchNameInput.keydown();
sketchNameInput.blur();
};
/** Creates a highlight animation to the Arduino IDE output header. */
Ardublockly.highlightIdeOutputHeader = function() {
var header = document.getElementById('ide_output_collapsible_header');
var h = 'ide_output_header_highlight';
var n = 'ide_output_header_normal';
header.className = header.className.replace(/ide_output_header_\S+/, h);
setTimeout(function() {
header.className = header.className.replace(/ide_output_header_\S+/, n);
setTimeout(function() {
header.className = header.className.replace(/ide_output_header_\S+/, h);
setTimeout(function() {
header.className = header.className.replace(/ide_output_header_\S+/, n);
}, 500);
}, 500);
}, 500);
};
/**
* Controls the height of the block and collapsible content between 2 states
* using CSS classes.
* It's state is dependent on the state of the IDE output collapsible. The
* collapsible functionality from Materialize framework adds the active class,
* so this class is consulted to shrink or expand the content height.
*/
Ardublockly.contentHeightToggle = function() {
var outputHeader = document.getElementById('ide_output_collapsible_header');
var blocks = document.getElementById('blocks_panel');
var arduino = document.getElementById('content_arduino');
var xml = document.getElementById('content_xml');
// Blockly doesn't resize with CSS3 transitions enabled, so do it manually
var timerId = setInterval(function() {
window.dispatchEvent(new Event('resize'));
}, 15);
setTimeout(function() {
clearInterval(timerId);
}, 400);
// Apart from checking if the output is visible, do not bother to shrink in
// small screens as the minimum height of the content will kick in and cause
// the content to be behind the IDE output data anyway.
if (!outputHeader.className.match('active') && $(window).height() > 800) {
blocks.className = 'content height_transition blocks_panel_small';
arduino.className = 'content height_transition content_arduino_small';
xml.className = 'content height_transition content_xml_small';
} else {
blocks.className = 'content height_transition blocks_panel_large';
arduino.className = 'content height_transition content_arduino_large';
xml.className = 'content height_transition content_xml_large';
}
// If the height transition CSS is left then blockly does not resize
setTimeout(function() {
blocks.className = blocks.className.replace('height_transition', '');
arduino.className = arduino.className.replace('height_transition', '');
xml.className = xml.className.replace('height_transition', '');
}, 400);
};
/**
* Compute the absolute coordinates and dimensions of an HTML element.
* @param {!Element} element Element to match.
* @return {!Object} Contains height, width, x, and y properties.
* @private
*/
Ardublockly.getBBox_ = function(element) {
var height = element.offsetHeight;
var width = element.offsetWidth;
var x = 0;
var y = 0;
do {
x += element.offsetLeft;
y += element.offsetTop;
element = element.offsetParent;
} while (element);
return {
height: height,
width: width,
x: x,
y: y
};
};