|
@@ -16,9 +16,9 @@
|
16
|
16
|
|
17
|
17
|
$(function(){
|
18
|
18
|
|
19
|
|
-var marlin_config = 'config';
|
|
19
|
+var marlin_config = 'https://api.github.com/repos/MarlinFirmware/Marlin/contents/Marlin';
|
20
|
20
|
|
21
|
|
-// Extend String
|
|
21
|
+// Extend builtins
|
22
|
22
|
String.prototype.lpad = function(len, chr) {
|
23
|
23
|
if (chr === undefined) { chr = ' '; }
|
24
|
24
|
var s = this+'', need = len - s.length;
|
|
@@ -29,7 +29,13 @@ String.prototype.prePad = function(len, chr) { return len ? this.lpad(len, chr)
|
29
|
29
|
String.prototype.zeroPad = function(len) { return this.prePad(len, '0'); };
|
30
|
30
|
String.prototype.toHTML = function() { return jQuery('<div>').text(this).html(); };
|
31
|
31
|
String.prototype.regEsc = function() { return this.replace(/[.?*+^$[\]\\(){}|-]/g, "\\$&"); }
|
32
|
|
-String.prototype.lineCount = function() { var len = this.split(/\r?\n|\r/).length; return len > 0 ? len - 1 : len; };
|
|
32
|
+String.prototype.lineCount = function() { var len = this.split(/\r?\n|\r/).length; return len > 0 ? len - 1 : 0; };
|
|
33
|
+String.prototype.toLabel = function() { return this.replace(/_/g, ' ').toTitleCase(); }
|
|
34
|
+String.prototype.toTitleCase = function() { return this.replace(/([A-Z])(\w+)/gi, function(m,p1,p2) { return p1.toUpperCase() + p2.toLowerCase(); }); }
|
|
35
|
+Number.prototype.limit = function(m1, m2) {
|
|
36
|
+ if (m2 == null) return this > m1 ? m1 : this;
|
|
37
|
+ return this < m1 ? m1 : this > m2 ? m2 : this;
|
|
38
|
+};
|
33
|
39
|
|
34
|
40
|
/**
|
35
|
41
|
* selectField.addOptions takes an array or keyed object
|
|
@@ -56,9 +62,11 @@ var configuratorApp = (function(){
|
56
|
62
|
boards_file = 'boards.h',
|
57
|
63
|
config_file = 'Configuration.h',
|
58
|
64
|
config_adv_file = 'Configuration_adv.h',
|
|
65
|
+ $form = $('#config_form'),
|
59
|
66
|
$tooltip = $('#tooltip'),
|
60
|
67
|
$config = $('#config_text'),
|
61
|
68
|
$config_adv = $('#config_adv_text'),
|
|
69
|
+ define_list = [[],[]],
|
62
|
70
|
boards_list = {},
|
63
|
71
|
therms_list = {},
|
64
|
72
|
total_config_lines,
|
|
@@ -74,32 +82,11 @@ var configuratorApp = (function(){
|
74
|
82
|
init: function() {
|
75
|
83
|
self = this; // a 'this' for use when 'this' is something else
|
76
|
84
|
|
77
|
|
- // Set up the form
|
|
85
|
+ // Set up the form, creating fields and fieldsets as-needed
|
78
|
86
|
this.initConfigForm();
|
79
|
87
|
|
80
|
|
- // Make tabs for the fieldsets
|
81
|
|
- var $fset = $('#config_form fieldset');
|
82
|
|
- var $tabs = $('<ul>',{class:'tabs'}), ind = 1;
|
83
|
|
- $('#config_form fieldset').each(function(){
|
84
|
|
- var tabID = 'TAB'+ind;
|
85
|
|
- $(this).addClass(tabID);
|
86
|
|
- var $leg = $(this).find('legend');
|
87
|
|
- var $link = $('<a>',{href:'#'+ind,id:tabID}).text($leg.text());
|
88
|
|
- $tabs.append($('<li>').append($link));
|
89
|
|
- $link.click(function(e){
|
90
|
|
- e.preventDefault;
|
91
|
|
- var ind = this.id;
|
92
|
|
- $tabs.find('.active').removeClass('active');
|
93
|
|
- $(this).addClass('active');
|
94
|
|
- $fset.hide();
|
95
|
|
- $fset.filter('.'+this.id).show();
|
96
|
|
- return false;
|
97
|
|
- });
|
98
|
|
- ind++;
|
99
|
|
- });
|
100
|
|
- $tabs.appendTo('#tabs');
|
101
|
|
- $('<br>',{class:'clear'}).appendTo('#tabs');
|
102
|
|
- $tabs.find('a:first').trigger('click');
|
|
88
|
+ // Make tabs for all the fieldsets
|
|
89
|
+ this.makeTabsForFieldsets();
|
103
|
90
|
|
104
|
91
|
// Make a droppable file uploader, if possible
|
105
|
92
|
var $uploader = $('#file-upload');
|
|
@@ -110,25 +97,35 @@ var configuratorApp = (function(){
|
110
|
97
|
if (!fileUploader.hasFileUploaderSupport())
|
111
|
98
|
this.setMessage("Your browser doesn't support the file reading API.", 'error');
|
112
|
99
|
|
|
100
|
+ // Make the disclosure items work
|
|
101
|
+ $('.disclose').click(function(){
|
|
102
|
+ var $dis = $(this), $pre = $dis.next('pre');
|
|
103
|
+ var didAnim = function() {$dis.toggleClass('closed almost');};
|
|
104
|
+ $dis.addClass('almost').hasClass('closed')
|
|
105
|
+ ? $pre.slideDown(500, didAnim)
|
|
106
|
+ : $pre.slideUp(500, didAnim);
|
|
107
|
+ });
|
|
108
|
+
|
113
|
109
|
// Read boards.h, Configuration.h, Configuration_adv.h
|
114
|
110
|
var ajax_count = 0, success_count = 0;
|
115
|
111
|
var loaded_items = {};
|
116
|
112
|
var config_files = [boards_file, config_file, config_adv_file];
|
|
113
|
+ var isGithub = marlin_config.match('api.github');
|
117
|
114
|
$.each(config_files, function(i,fname){
|
118
|
115
|
$.ajax({
|
119
|
116
|
url: marlin_config+'/'+fname,
|
120
|
117
|
type: 'GET',
|
|
118
|
+ dataType: isGithub ? 'jsonp' : 'script',
|
121
|
119
|
async: true,
|
122
|
120
|
cache: false,
|
123
|
121
|
success: function(txt) {
|
124
|
|
- loaded_items[fname] = function(){ self.fileLoaded(fname, txt); };
|
|
122
|
+ loaded_items[fname] = function(){ self.fileLoaded(fname, isGithub ? atob(txt.data.content) : txt); };
|
125
|
123
|
success_count++;
|
126
|
124
|
},
|
127
|
125
|
complete: function() {
|
128
|
126
|
ajax_count++;
|
129
|
127
|
if (ajax_count >= 3) {
|
130
|
|
- $.each(config_files, function(i,fname){ if (loaded_items[fname] !== undefined) loaded_items[fname](); });
|
131
|
|
- self.refreshConfigForm();
|
|
128
|
+ $.each(config_files, function(){ if (loaded_items[this]) loaded_items[this](); });
|
132
|
129
|
if (success_count < ajax_count)
|
133
|
130
|
self.setMessage('Unable to load configurations. Use the upload field instead.', 'error');
|
134
|
131
|
}
|
|
@@ -137,34 +134,6 @@ var configuratorApp = (function(){
|
137
|
134
|
});
|
138
|
135
|
},
|
139
|
136
|
|
140
|
|
- setMessage: function(msg,type) {
|
141
|
|
- if (msg) {
|
142
|
|
- if (type === undefined) type = 'message';
|
143
|
|
- var $err = $('<p class="'+type+'">'+msg+'</p>'), err = $err[0];
|
144
|
|
- $('#message').prepend($err);
|
145
|
|
- var baseColor = $err.css('color').replace(/rgba?\(([^),]+,[^),]+,[^),]+).*/, 'rgba($1,');
|
146
|
|
- var d = new Date();
|
147
|
|
- err.pulse_offset = (pulse_offset += 200);
|
148
|
|
- err.startTime = d.getTime() + pulse_offset;
|
149
|
|
- err.pulser = setInterval(function(){
|
150
|
|
- d = new Date();
|
151
|
|
- var pulse_time = d.getTime() + err.pulse_offset;
|
152
|
|
- $err.css({color:baseColor+(0.5+Math.sin(pulse_time/200)*0.4)+')'});
|
153
|
|
- if (pulse_time - err.startTime > 5000) {
|
154
|
|
- clearInterval(err.pulser);
|
155
|
|
- $err.remove();
|
156
|
|
- }
|
157
|
|
- }, 50);
|
158
|
|
- }
|
159
|
|
- else {
|
160
|
|
- $('#message p.error, #message p.warning').each(function() {
|
161
|
|
- if (this.pulser !== undefined && this.pulser)
|
162
|
|
- clearInterval(this.pulser);
|
163
|
|
- $(this).remove();
|
164
|
|
- });
|
165
|
|
- }
|
166
|
|
- },
|
167
|
|
-
|
168
|
137
|
/**
|
169
|
138
|
* Init the boards array from a boards.h file
|
170
|
139
|
*/
|
|
@@ -174,7 +143,7 @@ var configuratorApp = (function(){
|
174
|
143
|
while((r = findDef.exec(txt)) !== null) {
|
175
|
144
|
boards_list[r[1]] = r[2].prePad(3, ' ') + " — " + r[4].replace(/\).*/, ')');
|
176
|
145
|
}
|
177
|
|
- this.log("Loaded boards", 3); this.log(boards_list, 3);
|
|
146
|
+ this.log("Loaded boards " + boards_list.join(' '), 3);
|
178
|
147
|
has_boards = true;
|
179
|
148
|
},
|
180
|
149
|
|
|
@@ -192,6 +161,45 @@ var configuratorApp = (function(){
|
192
|
161
|
},
|
193
|
162
|
|
194
|
163
|
/**
|
|
164
|
+ * Get all the unique define names
|
|
165
|
+ */
|
|
166
|
+ getDefinesFromText: function(txt) {
|
|
167
|
+ // Get all the unique #define's and save them in an array
|
|
168
|
+ var r, define_obj = {}, findDef = new RegExp('#define[ \\t]+(\\w+)', 'gm');
|
|
169
|
+ var cnt = 0;
|
|
170
|
+ while((r = findDef.exec(txt)) !== null) {
|
|
171
|
+ if (cnt++ && !(r[1] in define_obj)) define_obj[r[1]] = null;
|
|
172
|
+ }
|
|
173
|
+ this.log(Object.keys(define_obj), 2);
|
|
174
|
+ return Object.keys(define_obj);
|
|
175
|
+ },
|
|
176
|
+
|
|
177
|
+ /**
|
|
178
|
+ * Create placeholder fields for defines, as needed
|
|
179
|
+ */
|
|
180
|
+ createFieldsForDefines: function(adv) {
|
|
181
|
+ var e = adv ? 1 : 0, n = 0;
|
|
182
|
+ var fail_list = [];
|
|
183
|
+ $.each(define_list[e], function(i,name) {
|
|
184
|
+ if (!$('#'+name).length) {
|
|
185
|
+ var $ff = $('#more');
|
|
186
|
+ var inf = self.getDefineInfo(name, adv);
|
|
187
|
+ if (inf) {
|
|
188
|
+ var $newlabel = $('<label>',{for:name}).text(name.toLabel());
|
|
189
|
+ // if (!(++n % 3))
|
|
190
|
+ $newlabel.addClass('newline');
|
|
191
|
+ var $newfield = inf.type == 'switch' ? $('<input>',{type:'checkbox'}) : $('<input>',{type:'text',size:10,maxlength:40});
|
|
192
|
+ $newfield.attr({id:name,name:name}).prop({defineInfo:inf});
|
|
193
|
+ $ff.append($newlabel, $newfield);
|
|
194
|
+ }
|
|
195
|
+ else
|
|
196
|
+ fail_list.push(name);
|
|
197
|
+ }
|
|
198
|
+ });
|
|
199
|
+ if (fail_list) this.log('Unable to parse:\n' + fail_list.join('\n'), 2);
|
|
200
|
+ },
|
|
201
|
+
|
|
202
|
+ /**
|
195
|
203
|
* Handle a file being dropped on the file field
|
196
|
204
|
*/
|
197
|
205
|
handleFileLoad: function(txt, $uploader) {
|
|
@@ -204,7 +212,7 @@ var configuratorApp = (function(){
|
204
|
212
|
this.fileLoaded(filename, txt);
|
205
|
213
|
break;
|
206
|
214
|
default:
|
207
|
|
- this.log("Can't parse "+filename, 1);
|
|
215
|
+ this.setMessage("Can't parse '"+filename+"'!");
|
208
|
216
|
break;
|
209
|
217
|
}
|
210
|
218
|
},
|
|
@@ -214,12 +222,12 @@ var configuratorApp = (function(){
|
214
|
222
|
*/
|
215
|
223
|
fileLoaded: function(filename, txt) {
|
216
|
224
|
this.log("fileLoaded:"+filename,4);
|
|
225
|
+ var err;
|
217
|
226
|
switch(filename) {
|
218
|
227
|
case boards_file:
|
219
|
228
|
this.initBoardsFromText(txt);
|
220
|
229
|
$('#MOTHERBOARD').html('').addOptions(boards_list);
|
221
|
230
|
if (has_config) this.initField('MOTHERBOARD');
|
222
|
|
- this.setMessage(boards_file+' loaded successfully.');
|
223
|
231
|
break;
|
224
|
232
|
case config_file:
|
225
|
233
|
if (has_boards) {
|
|
@@ -227,12 +235,14 @@ var configuratorApp = (function(){
|
227
|
235
|
total_config_lines = txt.lineCount();
|
228
|
236
|
this.initThermistorsFromText(txt);
|
229
|
237
|
this.purgeDefineInfo(false);
|
|
238
|
+ define_list[0] = this.getDefinesFromText(txt);
|
|
239
|
+ this.log(define_list[0], 2);
|
|
240
|
+ this.createFieldsForDefines(0);
|
230
|
241
|
this.refreshConfigForm();
|
231
|
|
- this.setMessage(config_file+' loaded successfully.');
|
232
|
242
|
has_config = true;
|
233
|
243
|
}
|
234
|
244
|
else {
|
235
|
|
- this.setMessage("Upload a " + boards_file + " file first!", 'error');
|
|
245
|
+ err = boards_file;
|
236
|
246
|
}
|
237
|
247
|
break;
|
238
|
248
|
case config_adv_file:
|
|
@@ -240,15 +250,20 @@ var configuratorApp = (function(){
|
240
|
250
|
$config_adv.text(txt);
|
241
|
251
|
total_config_adv_lines = txt.lineCount();
|
242
|
252
|
this.purgeDefineInfo(true);
|
|
253
|
+ define_list[1] = this.getDefinesFromText(txt);
|
|
254
|
+ this.log(define_list[1], 2);
|
243
|
255
|
this.refreshConfigForm();
|
244
|
|
- this.setMessage(config_adv_file+' loaded successfully.');
|
245
|
256
|
has_config_adv = true;
|
246
|
257
|
}
|
247
|
258
|
else {
|
248
|
|
- this.setMessage("Upload a " + config_file + " file first!", 'error');
|
|
259
|
+ err = config_file;
|
249
|
260
|
}
|
250
|
261
|
break;
|
251
|
262
|
}
|
|
263
|
+ this.setMessage(err
|
|
264
|
+ ? 'Please upload a "' + boards_file + '" file first!'
|
|
265
|
+ : '"' + filename + '" loaded successfully.', err ? 'error' : 'message'
|
|
266
|
+ );
|
252
|
267
|
},
|
253
|
268
|
|
254
|
269
|
/**
|
|
@@ -264,7 +279,7 @@ var configuratorApp = (function(){
|
264
|
279
|
// while(!$config.text() == null) {}
|
265
|
280
|
|
266
|
281
|
// Go through all form items with names
|
267
|
|
- $('#config_form').find('[name]').each(function() {
|
|
282
|
+ $form.find('[name]').each(function() {
|
268
|
283
|
// Set its id to its name
|
269
|
284
|
var name = $(this).attr('name');
|
270
|
285
|
$(this).attr({id: name});
|
|
@@ -305,51 +320,58 @@ var configuratorApp = (function(){
|
305
|
320
|
},
|
306
|
321
|
|
307
|
322
|
/**
|
|
323
|
+ * Make tabs to switch between fieldsets
|
|
324
|
+ */
|
|
325
|
+ makeTabsForFieldsets: function() {
|
|
326
|
+ // Make tabs for the fieldsets
|
|
327
|
+ var $fset = $form.find('fieldset');
|
|
328
|
+ var $tabs = $('<ul>',{class:'tabs'}), ind = 1;
|
|
329
|
+ $fset.each(function(){
|
|
330
|
+ var tabID = 'TAB'+ind;
|
|
331
|
+ $(this).addClass(tabID);
|
|
332
|
+ var $leg = $(this).find('legend');
|
|
333
|
+ var $link = $('<a>',{href:'#'+ind,id:tabID}).text($leg.text());
|
|
334
|
+ $tabs.append($('<li>').append($link));
|
|
335
|
+ $link.click(function(e){
|
|
336
|
+ e.preventDefault;
|
|
337
|
+ var ind = this.id;
|
|
338
|
+ $tabs.find('.active').removeClass('active');
|
|
339
|
+ $(this).addClass('active');
|
|
340
|
+ $fset.hide();
|
|
341
|
+ $fset.filter('.'+this.id).show();
|
|
342
|
+ return false;
|
|
343
|
+ });
|
|
344
|
+ ind++;
|
|
345
|
+ });
|
|
346
|
+ $('#tabs').html('').append($tabs);
|
|
347
|
+ $('<br>',{class:'clear'}).appendTo('#tabs');
|
|
348
|
+ $tabs.find('a:first').trigger('click');
|
|
349
|
+ },
|
|
350
|
+
|
|
351
|
+ /**
|
308
|
352
|
* Update all fields on the form after loading a configuration
|
309
|
353
|
*/
|
310
|
354
|
refreshConfigForm: function() {
|
311
|
355
|
|
312
|
356
|
/**
|
313
|
|
- * For now I'm manually creating these references
|
314
|
|
- * but I should be able to parse Configuration.h
|
315
|
|
- * and iterate the #defines.
|
|
357
|
+ * Any manually-created form elements will remain
|
|
358
|
+ * where they are. Unknown defines (currently most)
|
|
359
|
+ * are added to the "More..." tab for now.
|
316
|
360
|
*
|
317
|
|
- * For any #ifdef blocks I can create field groups
|
318
|
|
- * which can be dimmed together when the option
|
319
|
|
- * is disabled.
|
|
361
|
+ * Specific exceptions can be managed by applying
|
|
362
|
+ * classes to the associated form fields.
|
|
363
|
+ * Sorting and arrangement can come from an included
|
|
364
|
+ * js file that describes the configuration in JSON.
|
320
|
365
|
*
|
321
|
|
- * Then we only need to specify exceptions to
|
322
|
|
- * standard behavior, (which is to add a text field)
|
|
366
|
+ * For now I'm trying to derive information
|
|
367
|
+ * about options directly from the config file.
|
323
|
368
|
*/
|
324
|
|
- this.initField('SERIAL_PORT');
|
325
|
|
-
|
326
|
|
- this.initField('BAUDRATE');
|
327
|
|
-
|
328
|
|
- this.initField('BTENABLED');
|
329
|
369
|
|
330
|
370
|
$('#MOTHERBOARD').html('').addOptions(boards_list);
|
331
|
|
- this.initField('MOTHERBOARD');
|
332
|
|
-
|
333
|
|
- this.initField('CUSTOM_MENDEL_NAME');
|
334
|
|
-
|
335
|
|
- this.initField('MACHINE_UUID');
|
336
|
|
-
|
337
|
|
- this.initField('EXTRUDERS');
|
338
|
|
-
|
339
|
|
- this.initField('POWER_SUPPLY');
|
340
|
|
-
|
341
|
|
- this.initField('PS_DEFAULT_OFF');
|
342
|
371
|
|
343
|
372
|
$('#TEMP_SENSOR_0, #TEMP_SENSOR_1, #TEMP_SENSOR_2, #TEMP_SENSOR_BED').html('').addOptions(therms_list);
|
344
|
|
- this.initField('TEMP_SENSOR_0');
|
345
|
|
- this.initField('TEMP_SENSOR_1');
|
346
|
|
- this.initField('TEMP_SENSOR_2');
|
347
|
|
- this.initField('TEMP_SENSOR_BED');
|
348
|
373
|
|
349
|
|
- this.initField('TEMP_SENSOR_1_AS_REDUNDANT');
|
350
|
|
- this.initField('MAX_REDUNDANT_TEMP_SENSOR_DIFF');
|
351
|
|
-
|
352
|
|
- this.initField('TEMP_RESIDENCY_TIME');
|
|
374
|
+ $.each(define_list, function() { $.each(this, function() { if ($('#'+this).length) self.initField(this); }); });
|
353
|
375
|
},
|
354
|
376
|
|
355
|
377
|
/**
|
|
@@ -362,19 +384,21 @@ var configuratorApp = (function(){
|
362
|
384
|
var inf = elm.defineInfo = this.getDefineInfo(name, adv);
|
363
|
385
|
$elm.on($elm.attr('type') == 'text' ? 'input' : 'change', this.handleChange);
|
364
|
386
|
|
365
|
|
- if (inf.comment) {
|
|
387
|
+ if (inf.tooltip) {
|
366
|
388
|
var $tipme = $elm.prev('label');
|
367
|
389
|
if ($tipme.length) {
|
368
|
390
|
$tipme.hover(
|
369
|
391
|
function() {
|
370
|
|
- var pos = $tipme.position();
|
371
|
|
- $tooltip.html(inf.comment)
|
372
|
|
- .append('<span>')
|
373
|
|
- .css({bottom:($tooltip.parent().outerHeight()-pos.top)+'px',left:(pos.left+70)+'px'})
|
374
|
|
- .show();
|
375
|
|
- if (hover_timer) {
|
376
|
|
- clearTimeout(hover_timer);
|
377
|
|
- hover_timer = null;
|
|
392
|
+ if ($('#tipson input').prop('checked')) {
|
|
393
|
+ var pos = $tipme.position();
|
|
394
|
+ $tooltip.html(inf.tooltip)
|
|
395
|
+ .append('<span>')
|
|
396
|
+ .css({bottom:($tooltip.parent().outerHeight()-pos.top)+'px',left:(pos.left+70)+'px'})
|
|
397
|
+ .show();
|
|
398
|
+ if (hover_timer) {
|
|
399
|
+ clearTimeout(hover_timer);
|
|
400
|
+ hover_timer = null;
|
|
401
|
+ }
|
378
|
402
|
}
|
379
|
403
|
},
|
380
|
404
|
function() {
|
|
@@ -410,7 +434,7 @@ var configuratorApp = (function(){
|
410
|
434
|
*/
|
411
|
435
|
defineValue: function(name) {
|
412
|
436
|
this.log('defineValue:'+name,4);
|
413
|
|
- var $elm = $('#'+name), elm = $elm[0], inf = elm.defineInfo;
|
|
437
|
+ var inf = $('#'+name)[0].defineInfo;
|
414
|
438
|
if (inf == null) return 'n/a';
|
415
|
439
|
var result = inf.regex.exec($(inf.field).text());
|
416
|
440
|
|
|
@@ -424,7 +448,7 @@ var configuratorApp = (function(){
|
424
|
448
|
*/
|
425
|
449
|
defineIsEnabled: function(name) {
|
426
|
450
|
this.log('defineIsEnabled:'+name,4);
|
427
|
|
- var $elm = $('#'+name), elm = $elm[0], inf = elm.defineInfo;
|
|
451
|
+ var inf = $('#'+name)[0].defineInfo;
|
428
|
452
|
if (inf == null) return false;
|
429
|
453
|
var result = inf.regex.exec($(inf.field).text());
|
430
|
454
|
|
|
@@ -441,15 +465,14 @@ var configuratorApp = (function(){
|
441
|
465
|
*/
|
442
|
466
|
setDefineEnabled: function(name, val) {
|
443
|
467
|
this.log('setDefineEnabled:'+name,4);
|
444
|
|
- var $elm = $('#'+name), inf = $elm[0].defineInfo;
|
445
|
|
- if (inf == null) return;
|
446
|
|
-
|
447
|
|
- var slash = val ? '' : '//';
|
448
|
|
- var newline = inf.line
|
449
|
|
- .replace(/^([ \t]*)(\/\/)([ \t]*)/, '$1$3') // remove slashes
|
450
|
|
- .replace(inf.pre+inf.define, inf.pre+slash+inf.define); // add them back
|
451
|
|
-
|
452
|
|
- this.setDefineLine(name, newline);
|
|
468
|
+ var inf = $('#'+name)[0].defineInfo;
|
|
469
|
+ if (inf) {
|
|
470
|
+ var slash = val ? '' : '//';
|
|
471
|
+ var newline = inf.line
|
|
472
|
+ .replace(/^([ \t]*)(\/\/)([ \t]*)/, '$1$3') // remove slashes
|
|
473
|
+ .replace(inf.pre+inf.define, inf.pre+slash+inf.define); // add them back
|
|
474
|
+ this.setDefineLine(name, newline);
|
|
475
|
+ }
|
453
|
476
|
},
|
454
|
477
|
|
455
|
478
|
/**
|
|
@@ -486,7 +509,7 @@ var configuratorApp = (function(){
|
486
|
509
|
*/
|
487
|
510
|
setDefineLine: function(name, newline) {
|
488
|
511
|
this.log('setDefineLine:'+name+'\n'+newline,4);
|
489
|
|
- var $elm = $('#'+name), elm = $elm[0], inf = elm.defineInfo;
|
|
512
|
+ var inf = $('#'+name)[0].defineInfo;
|
490
|
513
|
var $c = $(inf.field), txt = $c.text();
|
491
|
514
|
|
492
|
515
|
var hilite_token = '[HIGHLIGHTER-TOKEN]';
|
|
@@ -501,7 +524,7 @@ var configuratorApp = (function(){
|
501
|
524
|
$c.html(html);
|
502
|
525
|
|
503
|
526
|
// Scroll to reveal the define
|
504
|
|
- this.scrollToDefine(name);
|
|
527
|
+ if ($c.is(':visible')) this.scrollToDefine(name);
|
505
|
528
|
},
|
506
|
529
|
|
507
|
530
|
/**
|
|
@@ -509,19 +532,17 @@ var configuratorApp = (function(){
|
509
|
532
|
*/
|
510
|
533
|
scrollToDefine: function(name, always) {
|
511
|
534
|
this.log('scrollToDefine:'+name,4);
|
512
|
|
- var $elm = $('#'+name), inf = $elm[0].defineInfo, $c = $(inf.field);
|
|
535
|
+ var inf = $('#'+name)[0].defineInfo, $c = $(inf.field);
|
513
|
536
|
|
514
|
537
|
// Scroll to the altered text if it isn't visible
|
515
|
538
|
var halfHeight = $c.height()/2, scrollHeight = $c.prop('scrollHeight'),
|
516
|
|
- textScrollY = inf.lineNum * scrollHeight/(inf.adv ? total_config_adv_lines : total_config_lines) - halfHeight;
|
517
|
|
-
|
518
|
|
- if (textScrollY < 0)
|
519
|
|
- textScrollY = 0;
|
520
|
|
- else if (textScrollY > scrollHeight)
|
521
|
|
- textScrollY = scrollHeight - 1;
|
|
539
|
+ lineHeight = scrollHeight/(inf.adv ? total_config_adv_lines : total_config_lines),
|
|
540
|
+ textScrollY = (inf.lineNum * lineHeight - halfHeight).limit(0, scrollHeight - 1);
|
522
|
541
|
|
523
|
|
- if (always == true || Math.abs($c.prop('scrollTop') - textScrollY) > halfHeight)
|
524
|
|
- $c.animate({ scrollTop: textScrollY < 0 ? 0 : textScrollY });
|
|
542
|
+ if (always || Math.abs($c.prop('scrollTop') - textScrollY) > halfHeight) {
|
|
543
|
+ $c.find('span').height(lineHeight);
|
|
544
|
+ $c.animate({ scrollTop: textScrollY });
|
|
545
|
+ }
|
525
|
546
|
},
|
526
|
547
|
|
527
|
548
|
/**
|
|
@@ -530,7 +551,7 @@ var configuratorApp = (function(){
|
530
|
551
|
setFieldFromDefine: function(name) {
|
531
|
552
|
var $elm = $('#'+name), val = this.defineValue(name);
|
532
|
553
|
|
533
|
|
- this.log('setFieldFromDefine:' + name + ' to ' + val, 4);
|
|
554
|
+ this.log('setFieldFromDefine:' + name + ' to ' + val, 2);
|
534
|
555
|
|
535
|
556
|
// Set the field value
|
536
|
557
|
$elm.attr('type') == 'checkbox' ? $elm.prop('checked', val) : $elm.val(''+val);
|
|
@@ -569,20 +590,20 @@ var configuratorApp = (function(){
|
569
|
590
|
/**
|
570
|
591
|
* Get information about a #define from configuration file text:
|
571
|
592
|
*
|
572
|
|
- * Pre-examine the #define for its prefix, value position, suffix, etc.
|
573
|
|
- * Construct a regex for the #define to quickly find (and replace) values.
|
574
|
|
- * Store the existing #define line as the key to finding it later.
|
575
|
|
- * Determine the line number of the #define so it can be scrolled to.
|
|
593
|
+ * - Pre-examine the #define for its prefix, value position, suffix, etc.
|
|
594
|
+ * - Construct RegExp's for the #define to quickly find (and replace) values.
|
|
595
|
+ * - Store the existing #define line as a fast key to finding it later.
|
|
596
|
+ * - Determine the line number of the #define so it can be scrolled to.
|
|
597
|
+ * - Gather nearby comments to be used as tooltips.
|
576
|
598
|
*/
|
577
|
599
|
getDefineInfo: function(name, adv) {
|
578
|
600
|
if (adv === undefined) adv = false;
|
579
|
601
|
this.log('getDefineInfo:'+name,4);
|
580
|
|
- var $elm = $('#'+name), elm = $elm[0],
|
581
|
|
- $c = adv ? $config_adv : $config,
|
|
602
|
+ var $c = adv ? $config_adv : $config,
|
582
|
603
|
txt = $c.text();
|
583
|
604
|
|
584
|
605
|
// a switch line with no value
|
585
|
|
- var findDef = new RegExp('^([ \\t]*//)?([ \\t]*#define[ \\t]+' + elm.id + ')([ \\t]*/[*/].*)?$', 'm'),
|
|
606
|
+ var findDef = new RegExp('^([ \\t]*//)?([ \\t]*#define[ \\t]+' + name + ')([ \\t]*/[*/].*)?$', 'm'),
|
586
|
607
|
result = findDef.exec(txt),
|
587
|
608
|
info = { type:0, adv:adv, field:$c[0], val_i: 2 };
|
588
|
609
|
if (result !== null) {
|
|
@@ -599,7 +620,7 @@ var configuratorApp = (function(){
|
599
|
620
|
}
|
600
|
621
|
else {
|
601
|
622
|
// a define with quotes
|
602
|
|
- findDef = new RegExp('^(.*//)?(.*#define[ \\t]+' + elm.id + '[ \\t]+)("[^"]*")([ \\t]*/[*/].*)?$', 'm');
|
|
623
|
+ findDef = new RegExp('^(.*//)?(.*#define[ \\t]+' + name + '[ \\t]+)("[^"]*")([ \\t]*/[*/].*)?$', 'm');
|
603
|
624
|
result = findDef.exec(txt);
|
604
|
625
|
if (result !== null) {
|
605
|
626
|
$.extend(info, {
|
|
@@ -614,7 +635,7 @@ var configuratorApp = (function(){
|
614
|
635
|
}
|
615
|
636
|
else {
|
616
|
637
|
// a define with no quotes
|
617
|
|
- findDef = new RegExp('^([ \\t]*//)?([ \\t]*#define[ \\t]+' + elm.id + '[ \\t]+)(\\S*)([ \\t]*/[*/].*)?$', 'm');
|
|
638
|
+ findDef = new RegExp('^([ \\t]*//)?([ \\t]*#define[ \\t]+' + name + '[ \\t]+)(\\S*)([ \\t]*/[*/].*)?$', 'm');
|
618
|
639
|
result = findDef.exec(txt);
|
619
|
640
|
if (result !== null) {
|
620
|
641
|
$.extend(info, {
|
|
@@ -632,10 +653,10 @@ var configuratorApp = (function(){
|
632
|
653
|
|
633
|
654
|
if (info.type) {
|
634
|
655
|
// Get the end-of-line comment, if there is one
|
635
|
|
- var comment = '';
|
|
656
|
+ var tooltip = '';
|
636
|
657
|
findDef = new RegExp('.*#define[ \\t].*/[/*]+[ \\t]*(.*)');
|
637
|
658
|
if (info.line.search(findDef) >= 0)
|
638
|
|
- comment = info.line.replace(findDef, '$1');
|
|
659
|
+ tooltip = info.line.replace(findDef, '$1');
|
639
|
660
|
|
640
|
661
|
// Get all the comments immediately before the item
|
641
|
662
|
var r, s;
|
|
@@ -644,16 +665,16 @@ var configuratorApp = (function(){
|
644
|
665
|
findDef = new RegExp('^[ \\t]*//+[ \\t]*(.*)[ \\t]*$', 'gm');
|
645
|
666
|
while((s = findDef.exec(r[1])) !== null) {
|
646
|
667
|
if (s[1].match(/^#define[ \\t]/) != null) {
|
647
|
|
- comment = '';
|
|
668
|
+ tooltip = '';
|
648
|
669
|
break;
|
649
|
670
|
}
|
650
|
|
- comment += ' ' + s[1] + "\n";
|
|
671
|
+ tooltip += ' ' + s[1] + "\n";
|
651
|
672
|
}
|
652
|
673
|
}
|
653
|
674
|
|
654
|
675
|
findDef = new RegExp('^[ \\t]*'+name+'[ \\t]*', 'm');
|
655
|
676
|
$.extend(info, {
|
656
|
|
- comment: '<strong>'+name+'</strong> '+comment.replace(findDef,'').trim().toHTML(),
|
|
677
|
+ tooltip: '<strong>'+name+'</strong> '+tooltip.replace(findDef,'').trim().toHTML(),
|
657
|
678
|
lineNum: this.getLineNumberOfText(info.line, txt)
|
658
|
679
|
});
|
659
|
680
|
}
|
|
@@ -673,6 +694,37 @@ var configuratorApp = (function(){
|
673
|
694
|
return (pos < 0) ? pos : txt.substr(0, pos).lineCount();
|
674
|
695
|
},
|
675
|
696
|
|
|
697
|
+ /**
|
|
698
|
+ * Add a temporary message to the page
|
|
699
|
+ */
|
|
700
|
+ setMessage: function(msg,type) {
|
|
701
|
+ if (msg) {
|
|
702
|
+ if (type === undefined) type = 'message';
|
|
703
|
+ var $err = $('<p class="'+type+'">'+msg+'</p>'), err = $err[0];
|
|
704
|
+ $('#message').prepend($err);
|
|
705
|
+ var baseColor = $err.css('color').replace(/rgba?\(([^),]+,[^),]+,[^),]+).*/, 'rgba($1,');
|
|
706
|
+ var d = new Date();
|
|
707
|
+ err.pulse_offset = (pulse_offset += 200);
|
|
708
|
+ err.startTime = d.getTime() + pulse_offset;
|
|
709
|
+ err.pulser = setInterval(function(){
|
|
710
|
+ d = new Date();
|
|
711
|
+ var pulse_time = d.getTime() + err.pulse_offset;
|
|
712
|
+ $err.css({color:baseColor+(0.5+Math.sin(pulse_time/200)*0.4)+')'});
|
|
713
|
+ if (pulse_time - err.startTime > 5000) {
|
|
714
|
+ clearInterval(err.pulser);
|
|
715
|
+ $err.remove();
|
|
716
|
+ }
|
|
717
|
+ }, 50);
|
|
718
|
+ }
|
|
719
|
+ else {
|
|
720
|
+ $('#message p.error, #message p.warning').each(function() {
|
|
721
|
+ if (this.pulser !== undefined && this.pulser)
|
|
722
|
+ clearInterval(this.pulser);
|
|
723
|
+ $(this).remove();
|
|
724
|
+ });
|
|
725
|
+ }
|
|
726
|
+ },
|
|
727
|
+
|
676
|
728
|
log: function(o,l) {
|
677
|
729
|
if (l === undefined) l = 0;
|
678
|
730
|
if (this.logging>=l*1) console.log(o);
|