Browse Source

Use api.github to get Configurations

- Make api.github the default source for configurations
- Remove configurations from the configurator
- Continuation and enhancements
Scott Lahteine 10 years ago
parent
commit
ba24a09f24

+ 59
- 11
Marlin/configurator/css/configurator.css View File

2
 /* Styles for Marlin Configurator */
2
 /* Styles for Marlin Configurator */
3
 
3
 
4
 body { margin: 0; padding: 0; background: #56A; color: #FFC; font-family: sans-serif; }
4
 body { margin: 0; padding: 0; background: #56A; color: #FFC; font-family: sans-serif; }
5
-
5
+fieldset { height: 16.1em; overflow: auto; margin-top: 10px; }
6
 #main { max-width: 1000px; margin: 0 auto; }
6
 #main { max-width: 1000px; margin: 0 auto; }
7
 #main { padding: 0 4%; width: 92%; }
7
 #main { padding: 0 4%; width: 92%; }
8
 #main { font-family: monospace; }
8
 #main { font-family: monospace; }
19
 img { display: none; }
19
 img { display: none; }
20
 label, input, select, textarea { display: block; float: left; margin: 1px 0; }
20
 label, input, select, textarea { display: block; float: left; margin: 1px 0; }
21
 label.newline, textarea, fieldset { clear: both; }
21
 label.newline, textarea, fieldset { clear: both; }
22
-label { width: 130px; height: 1em; padding: 10px 480px 10px 1em; margin-right: -470px; text-align: right; }
22
+label {
23
+	width: 140px; /* label area */
24
+	height: 1em;
25
+	padding: 10px 460px 10px 1em;
26
+	margin-right: -450px;
27
+	text-align: right;
28
+	}
23
 input[type="text"], select { margin: 0.75em 0 0; }
29
 input[type="text"], select { margin: 0.75em 0 0; }
24
 input[type="checkbox"], input[type="radio"], input[type="file"] { margin: 1em 0 0; }
30
 input[type="checkbox"], input[type="radio"], input[type="file"] { margin: 1em 0 0; }
25
-#config_form { display: block; background: #DDD; padding: 20px; color: #000; position: relative; }
31
+#config_form {
32
+	display: block;
33
+	background: #EEE;
34
+	padding: 6px 20px 20px;
35
+	color: #000;
36
+	position: relative;
37
+	}
38
+
26
 /*#config_text, #config_adv_text { font-family: "Andale mono", monospace; clear: both; }*/
39
 /*#config_text, #config_adv_text { font-family: "Andale mono", monospace; clear: both; }*/
27
 #config_text, #config_adv_text {
40
 #config_text, #config_adv_text {
28
 	height: 25em;
41
 	height: 25em;
32
 	overflow: auto;
45
 	overflow: auto;
33
 	background-color: #FFF;
46
 	background-color: #FFF;
34
 	color: #000;
47
 	color: #000;
48
+	font-family: "Fira Mono";
49
+	font-size: small;
35
 	}
50
 	}
36
 input[type="checkbox"], input[type="radio"].enabler { margin-left: 1em; }
51
 input[type="checkbox"], input[type="radio"].enabler { margin-left: 1em; }
37
 input:disabled { color: #BBB; }
52
 input:disabled { color: #BBB; }
39
 h1, h2, h3, h4, h5, h6 { clear: both; }
54
 h1, h2, h3, h4, h5, h6 { clear: both; }
40
 h2 { margin: 0; padding: 1em 0 0; }
55
 h2 { margin: 0; padding: 1em 0 0; }
41
 
56
 
42
-ul.tabs { display: inline; list-style: none; }
57
+ul.tabs { padding: 0; list-style: none; }
43
 ul.tabs li { display: inline; }
58
 ul.tabs li { display: inline; }
44
 ul.tabs li a,
59
 ul.tabs li a,
45
 ul.tabs li a.active:hover,
60
 ul.tabs li a.active:hover,
46
 ul.tabs li a.active:active {
61
 ul.tabs li a.active:active {
47
 	display: block;
62
 	display: block;
48
 	float: left;
63
 	float: left;
49
-	background: #666;
64
+	background: #1E4059;
50
 	color: #CCC;
65
 	color: #CCC;
51
-	font-size: 150%;
66
+	font-size: 110%;
52
 	border-radius: 0.25em 0.25em 0 0;
67
 	border-radius: 0.25em 0.25em 0 0;
53
 	margin: 0 4px 0 0;
68
 	margin: 0 4px 0 0;
54
-	padding: 2px 4px;
69
+	padding: 2px 8px;
55
 	text-decoration: none;
70
 	text-decoration: none;
71
+	font-family: georgia,"times new roman",times;
56
 	}
72
 	}
57
 ul.tabs li a.active:link,
73
 ul.tabs li a.active:link,
58
 ul.tabs li a.active:visited {
74
 ul.tabs li a.active:visited {
59
 	background: #DDD;
75
 	background: #DDD;
60
-	color: #900;
76
+	color: #06F;
61
 	cursor: default;
77
 	cursor: default;
78
+	margin-top: -4px;
79
+	padding-bottom: 4px;
80
+	padding-top: 4px;
62
 	}
81
 	}
63
 ul.tabs li a:hover,
82
 ul.tabs li a:hover,
64
 ul.tabs li a:active {
83
 ul.tabs li a:active {
65
-	background: #777;
66
-	color: #DDD;
84
+	background: #000;
85
+	color: #FFF;
67
 	}
86
 	}
68
 
87
 
69
 fieldset { display: none; border: 1px solid #AAA; border-radius: 1em; }
88
 fieldset { display: none; border: 1px solid #AAA; border-radius: 1em; }
117
 	bottom: -10px;
136
 	bottom: -10px;
118
 	left: 20px;
137
 	left: 20px;
119
 	}
138
 	}
120
-#tooltip>strong { color: #00B; }
139
+#tooltip>strong { color: #00B; }
140
+
141
+span.disclose {
142
+	float: right;
143
+	margin-top: -10px;
144
+	width: 0;
145
+	height: 0;
146
+	cursor: pointer;
147
+	border-left: 8px solid transparent;
148
+	border-right: 8px solid transparent;
149
+	border-top: 10px solid #000;
150
+	}
151
+span.disclose.closed {
152
+	margin: -14px 4px 0 0;
153
+	border-top: 8px solid transparent;
154
+	border-bottom: 8px solid transparent;
155
+	border-right: 10px solid #000;
156
+	}
157
+span.disclose.almost {
158
+    -ms-transform: rotate(45deg); /* IE 9 */
159
+    -webkit-transform: rotate(45deg); /* Chrome, Safari, Opera */
160
+    transform: rotate(45deg);
161
+	}
162
+span.disclose.closed.almost {
163
+    -ms-transform: rotate(315deg); /* IE 9 */
164
+    -webkit-transform: rotate(315deg); /* Chrome, Safari, Opera */
165
+    transform: rotate(315deg);
166
+	}
167
+#tipson { float: right; font-weight: bold; font-size: 100%; font-family: helvetica; }
168
+#tipson input { float: none; display: inline; }

+ 18
- 3
Marlin/configurator/index.html View File

3
   <head>
3
   <head>
4
     <meta charset="UTF-8">
4
     <meta charset="UTF-8">
5
     <title>Marlin Configurator</title>
5
     <title>Marlin Configurator</title>
6
+    <link href='http://fonts.googleapis.com/css?family=Fira+Mono&amp;subset=latin,latin-ext' rel='stylesheet' type='text/css' />
6
     <script src="js/jquery-2.1.3.min.js"></script>
7
     <script src="js/jquery-2.1.3.min.js"></script>
7
     <script src="js/binarystring.js"></script>
8
     <script src="js/binarystring.js"></script>
8
     <script src="js/binaryfileuploader.js"></script>
9
     <script src="js/binaryfileuploader.js"></script>
26
         <div id="tooltip"></div>
27
         <div id="tooltip"></div>
27
 
28
 
28
         <label>Drop Files Here:</label><input type="file" id="file-upload" />
29
         <label>Drop Files Here:</label><input type="file" id="file-upload" />
30
+        <div id="tipson"><input type="checkbox" checked /> ?</div>
31
+        <br class="clear" />
29
 
32
 
30
         <fieldset id="machine">
33
         <fieldset id="machine">
31
           <legend>Machine</legend>
34
           <legend>Machine</legend>
64
           <label>Max Diff:</label>
67
           <label>Max Diff:</label>
65
             <input name="MAX_REDUNDANT_TEMP_SENSOR_DIFF" type="text" size="3" maxlength="2" />
68
             <input name="MAX_REDUNDANT_TEMP_SENSOR_DIFF" type="text" size="3" maxlength="2" />
66
 
69
 
67
-          <label class="newline">Temp Residency Time (s):</label>
70
+          <label>Temp Residency Time (s):</label>
68
             <input name="TEMP_RESIDENCY_TIME" type="text" size="3" maxlength="2" />
71
             <input name="TEMP_RESIDENCY_TIME" type="text" size="3" maxlength="2" />
69
         </fieldset>
72
         </fieldset>
70
 
73
 
71
-        <h2>Marlin/Configuration.h</h2>
74
+        <fieldset id="hotends">
75
+          <legend>Hot Ends</legend>
76
+        </fieldset>
77
+
78
+        <fieldset id="heatbed">
79
+          <legend>Heated Bed</legend>
80
+        </fieldset>
81
+
82
+        <fieldset id="more">
83
+          <legend>More…</legend>
84
+        </fieldset>
85
+
86
+        <h2>Marlin/Configuration.h</h2><span class="disclose"></span>
72
         <pre id="config_text" class="hilightable"></pre>
87
         <pre id="config_text" class="hilightable"></pre>
73
-        <h2>Marlin/Configuration_adv.h</h2>
88
+        <h2>Marlin/Configuration_adv.h</h2><span class="disclose"></span>
74
         <pre id="config_adv_text" class="hilightable"></pre>
89
         <pre id="config_adv_text" class="hilightable"></pre>
75
 
90
 
76
         <br class="clear" />
91
         <br class="clear" />

+ 196
- 144
Marlin/configurator/js/configurator.js View File

16
 
16
 
17
 $(function(){
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
 String.prototype.lpad = function(len, chr) {
22
 String.prototype.lpad = function(len, chr) {
23
   if (chr === undefined) { chr = '&nbsp;'; }
23
   if (chr === undefined) { chr = '&nbsp;'; }
24
   var s = this+'', need = len - s.length;
24
   var s = this+'', need = len - s.length;
29
 String.prototype.zeroPad = function(len)     { return this.prePad(len, '0'); };
29
 String.prototype.zeroPad = function(len)     { return this.prePad(len, '0'); };
30
 String.prototype.toHTML = function()         { return jQuery('<div>').text(this).html(); };
30
 String.prototype.toHTML = function()         { return jQuery('<div>').text(this).html(); };
31
 String.prototype.regEsc = function()         { return this.replace(/[.?*+^$[\]\\(){}|-]/g, "\\$&"); }
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
  * selectField.addOptions takes an array or keyed object
41
  * selectField.addOptions takes an array or keyed object
56
       boards_file = 'boards.h',
62
       boards_file = 'boards.h',
57
       config_file = 'Configuration.h',
63
       config_file = 'Configuration.h',
58
       config_adv_file = 'Configuration_adv.h',
64
       config_adv_file = 'Configuration_adv.h',
65
+      $form = $('#config_form'),
59
       $tooltip = $('#tooltip'),
66
       $tooltip = $('#tooltip'),
60
       $config = $('#config_text'),
67
       $config = $('#config_text'),
61
       $config_adv = $('#config_adv_text'),
68
       $config_adv = $('#config_adv_text'),
69
+      define_list = [[],[]],
62
       boards_list = {},
70
       boards_list = {},
63
       therms_list = {},
71
       therms_list = {},
64
       total_config_lines,
72
       total_config_lines,
74
     init: function() {
82
     init: function() {
75
       self = this; // a 'this' for use when 'this' is something else
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
       this.initConfigForm();
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
       // Make a droppable file uploader, if possible
91
       // Make a droppable file uploader, if possible
105
       var $uploader = $('#file-upload');
92
       var $uploader = $('#file-upload');
110
       if (!fileUploader.hasFileUploaderSupport())
97
       if (!fileUploader.hasFileUploaderSupport())
111
         this.setMessage("Your browser doesn't support the file reading API.", 'error');
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
       // Read boards.h, Configuration.h, Configuration_adv.h
109
       // Read boards.h, Configuration.h, Configuration_adv.h
114
       var ajax_count = 0, success_count = 0;
110
       var ajax_count = 0, success_count = 0;
115
       var loaded_items = {};
111
       var loaded_items = {};
116
       var config_files = [boards_file, config_file, config_adv_file];
112
       var config_files = [boards_file, config_file, config_adv_file];
113
+      var isGithub = marlin_config.match('api.github');
117
       $.each(config_files, function(i,fname){
114
       $.each(config_files, function(i,fname){
118
         $.ajax({
115
         $.ajax({
119
           url: marlin_config+'/'+fname,
116
           url: marlin_config+'/'+fname,
120
           type: 'GET',
117
           type: 'GET',
118
+          dataType: isGithub ? 'jsonp' : 'script',
121
           async: true,
119
           async: true,
122
           cache: false,
120
           cache: false,
123
           success: function(txt) {
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
             success_count++;
123
             success_count++;
126
           },
124
           },
127
           complete: function() {
125
           complete: function() {
128
             ajax_count++;
126
             ajax_count++;
129
             if (ajax_count >= 3) {
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
               if (success_count < ajax_count)
129
               if (success_count < ajax_count)
133
                 self.setMessage('Unable to load configurations. Use the upload field instead.', 'error');
130
                 self.setMessage('Unable to load configurations. Use the upload field instead.', 'error');
134
             }
131
             }
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
      * Init the boards array from a boards.h file
138
      * Init the boards array from a boards.h file
170
      */
139
      */
174
       while((r = findDef.exec(txt)) !== null) {
143
       while((r = findDef.exec(txt)) !== null) {
175
         boards_list[r[1]] = r[2].prePad(3, '  ') + " — " + r[4].replace(/\).*/, ')');
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
       has_boards = true;
147
       has_boards = true;
179
     },
148
     },
180
 
149
 
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
      * Handle a file being dropped on the file field
203
      * Handle a file being dropped on the file field
196
      */
204
      */
197
     handleFileLoad: function(txt, $uploader) {
205
     handleFileLoad: function(txt, $uploader) {
204
           this.fileLoaded(filename, txt);
212
           this.fileLoaded(filename, txt);
205
           break;
213
           break;
206
         default:
214
         default:
207
-          this.log("Can't parse "+filename, 1);
215
+          this.setMessage("Can't parse '"+filename+"'!");
208
           break;
216
           break;
209
       }
217
       }
210
     },
218
     },
214
      */
222
      */
215
     fileLoaded: function(filename, txt) {
223
     fileLoaded: function(filename, txt) {
216
       this.log("fileLoaded:"+filename,4);
224
       this.log("fileLoaded:"+filename,4);
225
+      var err;
217
       switch(filename) {
226
       switch(filename) {
218
         case boards_file:
227
         case boards_file:
219
           this.initBoardsFromText(txt);
228
           this.initBoardsFromText(txt);
220
           $('#MOTHERBOARD').html('').addOptions(boards_list);
229
           $('#MOTHERBOARD').html('').addOptions(boards_list);
221
           if (has_config) this.initField('MOTHERBOARD');
230
           if (has_config) this.initField('MOTHERBOARD');
222
-          this.setMessage(boards_file+' loaded successfully.');
223
           break;
231
           break;
224
         case config_file:
232
         case config_file:
225
           if (has_boards) {
233
           if (has_boards) {
227
             total_config_lines = txt.lineCount();
235
             total_config_lines = txt.lineCount();
228
             this.initThermistorsFromText(txt);
236
             this.initThermistorsFromText(txt);
229
             this.purgeDefineInfo(false);
237
             this.purgeDefineInfo(false);
238
+            define_list[0] = this.getDefinesFromText(txt);
239
+            this.log(define_list[0], 2);
240
+            this.createFieldsForDefines(0);
230
             this.refreshConfigForm();
241
             this.refreshConfigForm();
231
-            this.setMessage(config_file+' loaded successfully.');
232
             has_config = true;
242
             has_config = true;
233
           }
243
           }
234
           else {
244
           else {
235
-            this.setMessage("Upload a " + boards_file + " file first!", 'error');
245
+            err = boards_file;
236
           }
246
           }
237
           break;
247
           break;
238
         case config_adv_file:
248
         case config_adv_file:
240
             $config_adv.text(txt);
250
             $config_adv.text(txt);
241
             total_config_adv_lines = txt.lineCount();
251
             total_config_adv_lines = txt.lineCount();
242
             this.purgeDefineInfo(true);
252
             this.purgeDefineInfo(true);
253
+            define_list[1] = this.getDefinesFromText(txt);
254
+            this.log(define_list[1], 2);
243
             this.refreshConfigForm();
255
             this.refreshConfigForm();
244
-            this.setMessage(config_adv_file+' loaded successfully.');
245
             has_config_adv = true;
256
             has_config_adv = true;
246
           }
257
           }
247
           else {
258
           else {
248
-            this.setMessage("Upload a " + config_file + " file first!", 'error');
259
+            err = config_file;
249
           }
260
           }
250
           break;
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
       // while(!$config.text() == null) {}
279
       // while(!$config.text() == null) {}
265
 
280
 
266
       // Go through all form items with names
281
       // Go through all form items with names
267
-      $('#config_form').find('[name]').each(function() {
282
+      $form.find('[name]').each(function() {
268
         // Set its id to its name
283
         // Set its id to its name
269
         var name = $(this).attr('name');
284
         var name = $(this).attr('name');
270
         $(this).attr({id: name});
285
         $(this).attr({id: name});
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
      * Update all fields on the form after loading a configuration
352
      * Update all fields on the form after loading a configuration
309
      */
353
      */
310
     refreshConfigForm: function() {
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
       $('#MOTHERBOARD').html('').addOptions(boards_list);
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
       $('#TEMP_SENSOR_0, #TEMP_SENSOR_1, #TEMP_SENSOR_2, #TEMP_SENSOR_BED').html('').addOptions(therms_list);
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
         var inf = elm.defineInfo = this.getDefineInfo(name, adv);
384
         var inf = elm.defineInfo = this.getDefineInfo(name, adv);
363
         $elm.on($elm.attr('type') == 'text' ? 'input' : 'change', this.handleChange);
385
         $elm.on($elm.attr('type') == 'text' ? 'input' : 'change', this.handleChange);
364
 
386
 
365
-        if (inf.comment) {
387
+        if (inf.tooltip) {
366
           var $tipme = $elm.prev('label');
388
           var $tipme = $elm.prev('label');
367
           if ($tipme.length) {
389
           if ($tipme.length) {
368
             $tipme.hover(
390
             $tipme.hover(
369
               function() {
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
               function() {
404
               function() {
410
      */
434
      */
411
     defineValue: function(name) {
435
     defineValue: function(name) {
412
       this.log('defineValue:'+name,4);
436
       this.log('defineValue:'+name,4);
413
-      var $elm = $('#'+name), elm = $elm[0], inf = elm.defineInfo;
437
+      var inf = $('#'+name)[0].defineInfo;
414
       if (inf == null) return 'n/a';
438
       if (inf == null) return 'n/a';
415
       var result = inf.regex.exec($(inf.field).text());
439
       var result = inf.regex.exec($(inf.field).text());
416
 
440
 
424
      */
448
      */
425
     defineIsEnabled: function(name) {
449
     defineIsEnabled: function(name) {
426
       this.log('defineIsEnabled:'+name,4);
450
       this.log('defineIsEnabled:'+name,4);
427
-      var $elm = $('#'+name), elm = $elm[0], inf = elm.defineInfo;
451
+      var inf = $('#'+name)[0].defineInfo;
428
       if (inf == null) return false;
452
       if (inf == null) return false;
429
       var result = inf.regex.exec($(inf.field).text());
453
       var result = inf.regex.exec($(inf.field).text());
430
 
454
 
441
      */
465
      */
442
     setDefineEnabled: function(name, val) {
466
     setDefineEnabled: function(name, val) {
443
       this.log('setDefineEnabled:'+name,4);
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
      */
509
      */
487
     setDefineLine: function(name, newline) {
510
     setDefineLine: function(name, newline) {
488
       this.log('setDefineLine:'+name+'\n'+newline,4);
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
       var $c = $(inf.field), txt = $c.text();
513
       var $c = $(inf.field), txt = $c.text();
491
 
514
 
492
       var hilite_token = '[HIGHLIGHTER-TOKEN]';
515
       var hilite_token = '[HIGHLIGHTER-TOKEN]';
501
       $c.html(html);
524
       $c.html(html);
502
 
525
 
503
       // Scroll to reveal the define
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
      */
532
      */
510
     scrollToDefine: function(name, always) {
533
     scrollToDefine: function(name, always) {
511
       this.log('scrollToDefine:'+name,4);
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
       // Scroll to the altered text if it isn't visible
537
       // Scroll to the altered text if it isn't visible
515
       var halfHeight = $c.height()/2, scrollHeight = $c.prop('scrollHeight'),
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
     setFieldFromDefine: function(name) {
551
     setFieldFromDefine: function(name) {
531
       var $elm = $('#'+name), val = this.defineValue(name);
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
       // Set the field value
556
       // Set the field value
536
       $elm.attr('type') == 'checkbox' ? $elm.prop('checked', val) : $elm.val(''+val);
557
       $elm.attr('type') == 'checkbox' ? $elm.prop('checked', val) : $elm.val(''+val);
569
     /**
590
     /**
570
      * Get information about a #define from configuration file text:
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
     getDefineInfo: function(name, adv) {
599
     getDefineInfo: function(name, adv) {
578
       if (adv === undefined) adv = false;
600
       if (adv === undefined) adv = false;
579
       this.log('getDefineInfo:'+name,4);
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
           txt = $c.text();
603
           txt = $c.text();
583
 
604
 
584
       // a switch line with no value
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
           result = findDef.exec(txt),
607
           result = findDef.exec(txt),
587
           info = { type:0, adv:adv, field:$c[0], val_i: 2 };
608
           info = { type:0, adv:adv, field:$c[0], val_i: 2 };
588
       if (result !== null) {
609
       if (result !== null) {
599
       }
620
       }
600
       else {
621
       else {
601
         // a define with quotes
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
         result = findDef.exec(txt);
624
         result = findDef.exec(txt);
604
         if (result !== null) {
625
         if (result !== null) {
605
           $.extend(info, {
626
           $.extend(info, {
614
         }
635
         }
615
         else {
636
         else {
616
           // a define with no quotes
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
           result = findDef.exec(txt);
639
           result = findDef.exec(txt);
619
           if (result !== null) {
640
           if (result !== null) {
620
             $.extend(info, {
641
             $.extend(info, {
632
 
653
 
633
       if (info.type) {
654
       if (info.type) {
634
         // Get the end-of-line comment, if there is one
655
         // Get the end-of-line comment, if there is one
635
-        var comment = '';
656
+        var tooltip = '';
636
         findDef = new RegExp('.*#define[ \\t].*/[/*]+[ \\t]*(.*)');
657
         findDef = new RegExp('.*#define[ \\t].*/[/*]+[ \\t]*(.*)');
637
         if (info.line.search(findDef) >= 0)
658
         if (info.line.search(findDef) >= 0)
638
-          comment = info.line.replace(findDef, '$1');
659
+          tooltip = info.line.replace(findDef, '$1');
639
 
660
 
640
         // Get all the comments immediately before the item
661
         // Get all the comments immediately before the item
641
         var r, s;
662
         var r, s;
644
           findDef = new RegExp('^[ \\t]*//+[ \\t]*(.*)[ \\t]*$', 'gm');
665
           findDef = new RegExp('^[ \\t]*//+[ \\t]*(.*)[ \\t]*$', 'gm');
645
           while((s = findDef.exec(r[1])) !== null) {
666
           while((s = findDef.exec(r[1])) !== null) {
646
             if (s[1].match(/^#define[ \\t]/) != null) {
667
             if (s[1].match(/^#define[ \\t]/) != null) {
647
-              comment = '';
668
+              tooltip = '';
648
               break;
669
               break;
649
             }
670
             }
650
-            comment += ' ' + s[1] + "\n";
671
+            tooltip += ' ' + s[1] + "\n";
651
           }
672
           }
652
         }
673
         }
653
 
674
 
654
         findDef = new RegExp('^[ \\t]*'+name+'[ \\t]*', 'm');
675
         findDef = new RegExp('^[ \\t]*'+name+'[ \\t]*', 'm');
655
         $.extend(info, {
676
         $.extend(info, {
656
-          comment: '<strong>'+name+'</strong> '+comment.replace(findDef,'').trim().toHTML(),
677
+          tooltip: '<strong>'+name+'</strong> '+tooltip.replace(findDef,'').trim().toHTML(),
657
           lineNum: this.getLineNumberOfText(info.line, txt)
678
           lineNum: this.getLineNumberOfText(info.line, txt)
658
         });
679
         });
659
       }
680
       }
673
       return (pos < 0) ? pos : txt.substr(0, pos).lineCount();
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
     log: function(o,l) {
728
     log: function(o,l) {
677
       if (l === undefined) l = 0;
729
       if (l === undefined) l = 0;
678
       if (this.logging>=l*1) console.log(o);
730
       if (this.logging>=l*1) console.log(o);

Loading…
Cancel
Save