Browse Source

Scroll to setting position in file when edited

- Animate scrolling to the edited item’s position
- Sanity check file drops and show warnings
- Fix form init / refresh on new configuration drop
- Document the API methods that get/set defines
Scott Lahteine 10 years ago
parent
commit
9c0adae3cd
1 changed files with 164 additions and 81 deletions
  1. 164
    81
      Marlin/configurator/js/configurator.js

+ 164
- 81
Marlin/configurator/js/configurator.js View File

@@ -59,13 +59,16 @@ var configuratorApp = (function(){
59 59
   // private variables and functions go here
60 60
   var self,
61 61
       pi2 = Math.PI * 2,
62
+      has_boards = false, has_config = false, has_config_adv = false,
62 63
       boards_file = 'boards.h',
63 64
       config_file = 'Configuration.h',
64 65
       config_adv_file = 'Configuration_adv.h',
65 66
       $config = $('#config_text'),
66 67
       $config_adv = $('#config_adv_text'),
67 68
       boards_list = {},
68
-      therms_list = {};
69
+      therms_list = {},
70
+      total_config_lines,
71
+      total_config_adv_lines;
69 72
 
70 73
   // Return this anonymous object as configuratorApp
71 74
   return {
@@ -75,6 +78,9 @@ var configuratorApp = (function(){
75 78
     init: function() {
76 79
       self = this; // a 'this' for use when 'this' is something else
77 80
 
81
+      // Set up the form
82
+      this.setupConfigForm();
83
+
78 84
       // Make tabs for the fieldsets
79 85
       var $fset = $('#config_form fieldset');
80 86
       var $tabs = $('<ul>',{class:'tabs'}), ind = 1;
@@ -123,6 +129,7 @@ var configuratorApp = (function(){
123 129
         success: function(txt) {
124 130
           // Get all the boards and save them into an object
125 131
           self.initBoardsFromText(txt);
132
+          has_boards = true;
126 133
         },
127 134
         error: errFunc
128 135
       });
@@ -138,6 +145,7 @@ var configuratorApp = (function(){
138 145
           $config.text(txt);
139 146
           // Get thermistor info too
140 147
           self.initThermistorsFromText(txt);
148
+          has_config = true;
141 149
         },
142 150
         error: errFunc
143 151
       });
@@ -151,7 +159,8 @@ var configuratorApp = (function(){
151 159
         success: function(txt) {
152 160
           // File contents into the textarea
153 161
           $config_adv.text(txt);
154
-          self.setupConfigForm();
162
+          has_config_adv = true;
163
+          self.refreshConfigForm();
155 164
         },
156 165
         error: errFunc
157 166
       });
@@ -164,6 +173,8 @@ var configuratorApp = (function(){
164 173
       while((r = findDef.exec(txt)) !== null) {
165 174
         boards_list[r[1]] = r[2].prePad(3, '  ') + " — " + r[4].replace(/\).*/, ')');
166 175
       }
176
+      this.log("Loaded boards", 0);
177
+      this.log(boards_list, 0);
167 178
     },
168 179
 
169 180
     initThermistorsFromText: function(txt) {
@@ -180,19 +191,36 @@ var configuratorApp = (function(){
180 191
       file += '';
181 192
       var filename = $uploader.val().replace(/.*[\/\\](.*)$/, '$1');
182 193
       switch(filename) {
183
-        case config_file:
184
-          $config.text(file);
185
-          this.initThermistorsFromText(file);
186
-          this.refreshConfigForm();
187
-          break;
188
-        case config_adv_file:
189
-          $config_adv.text(file);
190
-          this.refreshConfigForm();
191
-          break;
192 194
         case boards_file:
193 195
           this.initBoardsFromText(file);
196
+          has_boards = true;
194 197
           $('#MOTHERBOARD').html('').addOptions(boards_list);
195
-          this.initField('MOTHERBOARD');
198
+          if (has_config) this.initField('MOTHERBOARD');
199
+          break;
200
+        case config_file:
201
+          if (has_boards) {
202
+            $config.text(file);
203
+            has_config = true;
204
+            total_config_lines = file.replace(/[^\n]+/g, '').length;
205
+            this.initThermistorsFromText(file);
206
+            this.purgeDefineInfo(false);
207
+            this.refreshConfigForm();
208
+          }
209
+          else {
210
+            alert("Upload a " + boards_file + " file first!");
211
+          }
212
+          break;
213
+        case config_adv_file:
214
+          if (has_config) {
215
+            $config_adv.text(file);
216
+            has_config_adv = true;
217
+            total_config_adv_lines = file.replace(/[^\n]+/g, '').length;
218
+            this.purgeDefineInfo(true);
219
+            this.refreshConfigForm();
220
+          }
221
+          else {
222
+            alert("Upload a " + config_file + " file first!");
223
+          }
196 224
           break;
197 225
         default:
198 226
           this.log("Can't parse "+filename, 1);
@@ -232,11 +260,23 @@ var configuratorApp = (function(){
232 260
 
233 261
       $('#SERIAL_PORT').addOptions([0,1,2,3,4,5,6,7]);
234 262
       $('#BAUDRATE').addOptions([2400,9600,19200,38400,57600,115200,250000]);
235
-      $('#MOTHERBOARD').addOptions(boards_list);
236 263
       $('#EXTRUDERS').addOptions([1,2,3,4]);
237 264
       $('#POWER_SUPPLY').addOptions({'1':'ATX','2':'Xbox 360'});
238 265
 
239
-      this.refreshConfigForm();
266
+      $('#serial_stepper').jstepper({
267
+        min: 0,
268
+        max: 7,
269
+        val: $('#SERIAL_PORT').val(),
270
+        arrowWidth: '18px',
271
+        arrowHeight: '15px',
272
+        color: '#FFF',
273
+        acolor: '#F70',
274
+        hcolor: '#FF0',
275
+        id: 'select-me',
276
+        textStyle: {width:'1.5em',fontSize:'120%',textAlign:'center'},
277
+        onChange: function(v) { $('#SERIAL_PORT').val(v).trigger('change'); }
278
+      });
279
+
240 280
     },
241 281
 
242 282
     refreshConfigForm: function() {
@@ -259,6 +299,7 @@ var configuratorApp = (function(){
259 299
 
260 300
       this.initField('BTENABLED');
261 301
 
302
+      $('#MOTHERBOARD').html('').addOptions(boards_list);
262 303
       this.initField('MOTHERBOARD');
263 304
 
264 305
       this.initField('CUSTOM_MENDEL_NAME');
@@ -282,20 +323,6 @@ var configuratorApp = (function(){
282 323
 
283 324
       this.initField('TEMP_RESIDENCY_TIME');
284 325
 
285
-      $('#serial_stepper').jstepper({
286
-        min: 0,
287
-        max: 7,
288
-        val: $('#SERIAL_PORT').val(),
289
-        arrowWidth: '18px',
290
-        arrowHeight: '15px',
291
-        color: '#FFF',
292
-        acolor: '#F70',
293
-        hcolor: '#FF0',
294
-        id: 'select-me',
295
-        textStyle: {width:'1.5em',fontSize:'120%',textAlign:'center'},
296
-        onChange: function(v) { $('#SERIAL_PORT').val(v).trigger('change'); }
297
-      });
298
-
299 326
       // prettyPrint();
300 327
     },
301 328
 
@@ -303,17 +330,18 @@ var configuratorApp = (function(){
303 330
      * initField - make a field responsive and get info
304 331
      * about its configuration file define
305 332
      */
306
-    initField: function(name) {
333
+    initField: function(name, adv) {
334
+      this.log("initField:"+name,3);
307 335
       var $elm = $('#'+name), elm = $elm[0];
308
-      if (elm.configInfo === undefined) {
309
-        elm.configInfo = this.getDefineInfo(name);
336
+      if (elm.defineInfo === undefined) {
337
+        elm.defineInfo = this.getDefineInfo(name, adv);
310 338
         $elm.on($elm.attr('type') == 'text' ? 'input' : 'change', this.handleChange);
311 339
       }
312 340
       this.setFieldFromDefine(name);
313 341
     },
314 342
 
315 343
     handleChange: function(e) {
316
-      self.updateDefineForField(e.target.id);
344
+      self.updateDefineFromField(e.target.id);
317 345
     },
318 346
 
319 347
     handleSwitch: function(e) {
@@ -323,12 +351,43 @@ var configuratorApp = (function(){
323 351
       self.setDefineEnabled($prev[0].id, on);
324 352
     },
325 353
 
354
+    /**
355
+     * Get the current value of a #define (from the config text)
356
+     */
357
+    defineValue: function(name) {
358
+      this.log('defineValue:'+name,4);
359
+      var $elm = $('#'+name), elm = $elm[0], inf = elm.defineInfo;
360
+      var result = inf.regex.exec($(inf.field).text());
361
+
362
+      this.log(result,2);
363
+
364
+      return inf.type == 'switch' ? result[inf.val_i] != '//' : result[inf.val_i];
365
+    },
366
+
367
+    /**
368
+     * Get the current enabled state of a #define (from the config text)
369
+     */
370
+    defineIsEnabled: function(name) {
371
+      this.log('defineIsEnabled:'+name,4);
372
+      var $elm = $('#'+name), elm = $elm[0], inf = elm.defineInfo;
373
+      var result = inf.regex.exec($(inf.field).text());
374
+
375
+      this.log(result,2);
376
+
377
+      var on = result !== null ? result[1].trim() != '//' : true;
378
+      this.log(name + ' = ' + on, 4);
379
+
380
+      return on;
381
+    },
382
+
383
+    /**
384
+     * Set a #define enabled or disabled by altering the config text
385
+     */
326 386
     setDefineEnabled: function(name, val) {
327
-      this.log('setDefineEnabled:'+name,3);
387
+      this.log('setDefineEnabled:'+name,4);
328 388
 
329
-      var $elm = $('#'+name), elm = $elm[0], inf = elm.configInfo;
330
-      var $c = $config; // for now
331
-      var txt = $c.text();
389
+      var $elm = $('#'+name), elm = $elm[0], inf = elm.defineInfo;
390
+      var $c = $(inf.field), txt = $c.text();
332 391
 
333 392
       var slash = val ? '' : '//';
334 393
       var newline = inf.line
@@ -342,25 +401,13 @@ var configuratorApp = (function(){
342 401
       $c.text(txt);
343 402
     },
344 403
 
345
-    defineIsEnabled: function(name, adv) {
346
-      this.log('defineIsEnabled:'+name,4);
347
-      var $elm = $('#'+name), elm = $elm[0];
348
-      var $c = adv ? $config_adv : $config;
349
-
350
-      var result = elm.configInfo.regex.exec($c.text());
351
-      this.log(result,2);
352
-
353
-      var on = result !== null ? result[1].trim() != '//' : true;
354
-      this.log(name + ' = ' + on, 4);
355
-
356
-      return on;
357
-    },
358
-
359
-    updateDefineForField: function(name, adv) {
360
-      this.log('updateDefineForField:'+name,4);
361
-      var $elm = $('#'+name), elm = $elm[0], inf = elm.configInfo;
362
-      var $c = adv ? $config_adv : $config;
363
-      var txt = $c.text();
404
+    /**
405
+     * Update a #define (from the form) by altering the config text
406
+     */
407
+    updateDefineFromField: function(name) {
408
+      this.log('updateDefineFromField:'+name,4);
409
+      var $elm = $('#'+name), elm = $elm[0], inf = elm.defineInfo;
410
+      var $c = $(inf.field), txt = $c.text();
364 411
 
365 412
       // var result = inf.repl.exec(txt);
366 413
       // this.log(result, 2);
@@ -401,27 +448,71 @@ var configuratorApp = (function(){
401 448
       this.log(newline, 2);
402 449
 
403 450
       $c.text(txt);
451
+
452
+      // Scroll to the altered text if it isn't visible
453
+      var halfHeight = $c.height()/2, scrollHeight = $c.prop('scrollHeight'),
454
+          textScrollY = inf.lineNum * scrollHeight/(inf.adv ? total_config_adv_lines : total_config_lines) - halfHeight;
455
+
456
+      if (textScrollY < 0)
457
+        textScrollY = 0;
458
+      else if (textScrollY > scrollHeight)
459
+        textScrollY = scrollHeight - 1;
460
+
461
+      if (Math.abs($c.prop('scrollTop') - textScrollY) > halfHeight)
462
+        $c.animate({ scrollTop: textScrollY < 0 ? 0 : textScrollY });
463
+
404 464
     },
405 465
 
406
-    setFieldFromDefine: function(name, adv) {
407
-      var $elm = $('#'+name), elm = $elm[0];
408
-      var isCheck = $elm.attr('type') == 'checkbox';
409
-      var val = this.defineValue(name, adv);
466
+    /**
467
+     * Set a form field to the current #define value in the config text
468
+     */
469
+    setFieldFromDefine: function(name) {
470
+      var $elm = $('#'+name), val = this.defineValue(name);
410 471
 
411 472
       this.log('setFieldFromDefine:' + name + ' to ' + val, 4);
412 473
 
413
-      isCheck ? $elm.prop('checked', val) : $elm.val("" + val);
474
+      // Set the field value
475
+      $elm.attr('type') == 'checkbox' ? $elm.prop('checked', val) : $elm.val(''+val);
414 476
 
415 477
       // If the item has a checkbox then set enabled state too
416 478
       var $cb = $('#'+name+'-switch');
417 479
       if ($cb.length) {
418
-        var on = self.defineIsEnabled(name,adv);
419
-        $elm.attr('disabled', !on);
420
-        $cb.prop('checked', on);
480
+        var on = self.defineIsEnabled(name);
481
+        $elm.attr('disabled', !on); // enable/disable the form field (could also dim it)
482
+        $cb.prop('checked', on);    // check/uncheck the checkbox
421 483
       }
422 484
     },
423 485
 
486
+    /**
487
+     * Purge #define information for one of the config files
488
+     */
489
+    purgeDefineInfo: function(adv) {
490
+      if (typeof adv == 'undefined') adv = false;
491
+      $('[defineInfo]').each(function() {
492
+        if (adv === this.defineInfo.adv) $(this).removeProp('defineInfo');
493
+      });
494
+    },
495
+
496
+    /**
497
+     * Update #define information for one of the config files
498
+     */
499
+    refreshDefineInfo: function(adv) {
500
+      if (typeof adv == 'undefined') adv = false;
501
+      $('[defineInfo]').each(function() {
502
+        if (adv == this.defineInfo.adv) this.defineInfo = self.getDefineInfo(this.id, adv);
503
+      });
504
+    },
505
+
506
+    /**
507
+     * Get information about a #define from configuration file text:
508
+     *
509
+     *   Pre-examine the #define for its prefix, value position, suffix, etc.
510
+     *   Construct a regex for the #define to quickly find (and replace) values.
511
+     *   Store the existing #define line as the key to finding it later.
512
+     *   Determine the line number of the #define so it can be scrolled to.
513
+     */
424 514
     getDefineInfo: function(name, adv) {
515
+      if (typeof adv == 'undefined') adv = false;
425 516
       this.log('getDefineInfo:'+name,4);
426 517
       var $elm = $('#'+name), elm = $elm[0];
427 518
       var $c = adv ? $config_adv : $config;
@@ -431,14 +522,14 @@ var configuratorApp = (function(){
431 522
       var result = findDef.exec($c.text());
432 523
       if (result !== null) {
433 524
         var info = {
434
-          type: 'switch',
525
+          type:'switch', adv:adv, field:$c[0], val_i: 1,
435 526
           line: result[0], // whole line
436 527
           pre: result[1] === undefined ? '' : result[1].replace('//',''),
437 528
           define: result[2],
438 529
           post: result[3] === undefined ? '' : result[3]
439 530
         };
440
-        info.regex = new RegExp('(.*//)?(.*' + info.define.regEsc() + info.post.regEsc() + ')', 'm');
441
-        info.repl = info.regex;
531
+        info.repl = info.regex = new RegExp('(.*//)?(.*' + info.define.regEsc() + info.post.regEsc() + ')', 'm');
532
+        info.lineNum = this.getLineInText(info.line, $c.text());
442 533
         this.log(info,2);
443 534
         return info;
444 535
       }
@@ -448,7 +539,7 @@ var configuratorApp = (function(){
448 539
       result = findDef.exec($c.text());
449 540
       if (result !== null) {
450 541
         var info = {
451
-          type: 'quoted',
542
+          type:'quoted', adv:adv, field:$c[0], val_i: 2,
452 543
           line: result[0],
453 544
           pre: result[1] === undefined ? '' : result[1].replace('//',''),
454 545
           define: result[2],
@@ -456,6 +547,7 @@ var configuratorApp = (function(){
456 547
         };
457 548
         info.regex = new RegExp('(.*//)?.*' + info.define.regEsc() + '"([^"]*)"' + info.post.regEsc(), 'm');
458 549
         info.repl  = new RegExp('((.*//)?.*' + info.define.regEsc() + '")[^"]*("' + info.post.regEsc() + ')', 'm');
550
+        info.lineNum = this.getLineInText(info.line, $c.text());
459 551
         this.log(info,2);
460 552
         return info;
461 553
       }
@@ -465,7 +557,7 @@ var configuratorApp = (function(){
465 557
       result = findDef.exec($c.text());
466 558
       if (result !== null) {
467 559
         var info = {
468
-          type: 'plain',
560
+          type:'plain', adv:adv, field:$c[0], val_i: 2,
469 561
           line: result[0],
470 562
           pre: result[1] === undefined ? '' : result[1].replace('//',''),
471 563
           define: result[2],
@@ -473,6 +565,7 @@ var configuratorApp = (function(){
473 565
         };
474 566
         info.regex = new RegExp('(.*//)?.*' + info.define.regEsc() + '(\\S*)' + info.post.regEsc(), 'm');
475 567
         info.repl = new RegExp('((.*//)?.*' + info.define.regEsc() + ')\\S*(' + info.post.regEsc() + ')', 'm');
568
+        info.lineNum = this.getLineInText(info.line, $c.text());
476 569
         this.log(info,2);
477 570
         return info;
478 571
       }
@@ -480,19 +573,9 @@ var configuratorApp = (function(){
480 573
       return null;
481 574
     },
482 575
 
483
-    defineValue: function(name, adv) {
484
-      this.log('defineValue:'+name,4);
485
-      var $elm = $('#'+name), elm = $elm[0];
486
-      var $c = adv ? $config_adv : $config;
487
-      var inf = elm.configInfo;
488
-
489
-      var result = inf.regex.exec($c.text());
490
-      this.log(result,2);
491
-      switch(inf.type) {
492
-        case 'switch': return result[1] != '//';
493
-        case 'quoted': return result[2];
494
-        case 'plain':  return result[2];
495
-      }
576
+    getLineInText: function(line, txt) {
577
+      var pos = txt.indexOf(line);
578
+      return (pos < 0) ? pos : txt.substr(0, pos).replace(/[^\n]+/g, '').length;
496 579
     },
497 580
 
498 581
     log: function(o,l) {

Loading…
Cancel
Save