浏览代码

Highlight the edited line

- Add a span to the edited text line to provide a highlight
- Scroll and highlight for switch checkboxes also
- Clean up initialization
- More API documentation
- Smarter handling of asynchronous file loading during init
Scott Lahteine 10 年前
父节点
当前提交
4bb72f9480
共有 3 个文件被更改,包括 179 次插入114 次删除
  1. 19
    3
      Marlin/configurator/css/configurator.css
  2. 6
    3
      Marlin/configurator/index.html
  3. 154
    108
      Marlin/configurator/js/configurator.js

+ 19
- 3
Marlin/configurator/css/configurator.css 查看文件

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
 #main { max-width: 1000px; margin: 0 auto; }
6
 #main { max-width: 1000px; margin: 0 auto; }
6
 #main { padding: 0 4%; width: 92%; }
7
 #main { padding: 0 4%; width: 92%; }
7
 #main { font-family: monospace; }
8
 #main { font-family: monospace; }
9
+
10
+#message { width: 80%; margin: 0 auto 0.25em; color: #FF0; text-align: center; }
11
+#message p { padding: 2px 0; }
12
+#message p.error, #message p.message { color: #F00; background: #FF4; font-weight: bold; border-radius: 0.8em; }
13
+#message p.message { color: #080; background: #CFC; }
14
+
8
 .info { color: #AAF; }
15
 .info { color: #AAF; }
9
 .info span { color: #FFF; }
16
 .info span { color: #FFF; }
10
 .info span span { color: #000; font-weight: bold; }
17
 .info span span { color: #000; font-weight: bold; }
11
-p { width: 80%; color: #FF0; }
12
 #help strong { color: #0DD; }
18
 #help strong { color: #0DD; }
13
 img { display: none; }
19
 img { display: none; }
14
 label, input, select, textarea { display: block; float: left; margin: 1px 0; }
20
 label, input, select, textarea { display: block; float: left; margin: 1px 0; }
24
 .clear { clear: both; }
30
 .clear { clear: both; }
25
 h1, h2, h3, h4, h5, h6 { clear: both; }
31
 h1, h2, h3, h4, h5, h6 { clear: both; }
26
 h2 { margin: 0; padding: 1em 0 0; }
32
 h2 { margin: 0; padding: 1em 0 0; }
27
-#serial_stepper { padding-top: 0.75em; display: block; float: left; }
28
-#SERIAL_PORT { display: none; }
29
 
33
 
30
 ul.tabs { display: inline; list-style: none; }
34
 ul.tabs { display: inline; list-style: none; }
31
 ul.tabs li { display: inline; }
35
 ul.tabs li { display: inline; }
56
 
60
 
57
 fieldset { display: none; border: 1px solid #AAA; border-radius: 1em; }
61
 fieldset { display: none; border: 1px solid #AAA; border-radius: 1em; }
58
 fieldset legend { display: none; }
62
 fieldset legend { display: none; }
63
+
64
+.hilightable span {
65
+	display: block;
66
+	float: left;
67
+	width: 100%;
68
+	height: 1.3em;
69
+	background: rgba(225,255,0,1);
70
+	margin: 0 -100% -1em 0;
71
+	}
72
+
73
+#serial_stepper { padding-top: 0.75em; display: block; float: left; }
74
+#SERIAL_PORT { display: none; }

+ 6
- 3
Marlin/configurator/index.html 查看文件

14
   <body>
14
   <body>
15
     <section id="main">
15
     <section id="main">
16
       <h1>Marlin Configurator 0.1a</h1>
16
       <h1>Marlin Configurator 0.1a</h1>
17
-      <p>Enter values in the form, get a Marlin configuration. Will include a drop-down of known configurations.</p>
17
+
18
+      <div id="message">
19
+        <p class="info">Enter values in the form, get a Marlin configuration.<br/>Will include a drop-down of known configurations.</p>
20
+      </div>
18
 
21
 
19
       <div id="tabs"></div>
22
       <div id="tabs"></div>
20
 
23
 
64
         </fieldset>
67
         </fieldset>
65
 
68
 
66
         <h2>Marlin/Configuration.h</h2>
69
         <h2>Marlin/Configuration.h</h2>
67
-        <pre id="config_text" class="prettyprint linenums"></pre>
70
+        <pre id="config_text" class="hilightable"></pre>
68
         <h2>Marlin/Configuration_adv.h</h2>
71
         <h2>Marlin/Configuration_adv.h</h2>
69
-        <pre id="config_adv_text" class="prettyprint linenums"></pre>
72
+        <pre id="config_adv_text" class="hilightable"></pre>
70
 
73
 
71
         <br class="clear" />
74
         <br class="clear" />
72
       </form>
75
       </form>

+ 154
- 108
Marlin/configurator/js/configurator.js 查看文件

79
       self = this; // a 'this' for use when 'this' is something else
79
       self = this; // a 'this' for use when 'this' is something else
80
 
80
 
81
       // Set up the form
81
       // Set up the form
82
-      this.setupConfigForm();
82
+      this.initConfigForm();
83
 
83
 
84
       // Make tabs for the fieldsets
84
       // Make tabs for the fieldsets
85
       var $fset = $('#config_form fieldset');
85
       var $fset = $('#config_form fieldset');
105
       $('<br>',{class:'clear'}).appendTo('#tabs');
105
       $('<br>',{class:'clear'}).appendTo('#tabs');
106
       $tabs.find('a:first').trigger('click');
106
       $tabs.find('a:first').trigger('click');
107
 
107
 
108
-      // Make a droppable file uploader
108
+      // Make a droppable file uploader, if possible
109
       var $uploader = $('#file-upload');
109
       var $uploader = $('#file-upload');
110
       var fileUploader = new BinaryFileUploader({
110
       var fileUploader = new BinaryFileUploader({
111
         element:    $uploader[0],
111
         element:    $uploader[0],
112
         onFileLoad: function(file) { self.handleFileLoad(file, $uploader); }
112
         onFileLoad: function(file) { self.handleFileLoad(file, $uploader); }
113
       });
113
       });
114
-
115
-      if (!fileUploader.hasFileUploaderSupport()) alert('Your browser doesn\'t support the file reading API');
116
-
117
-      // Read boards.h
118
-      boards_list = {};
119
-
120
-      var errFunc = function(jqXHR, textStatus, errorThrown) {
121
-                      alert('Failed to load '+this.url+'. Try the file field.');
122
-                    };
123
-
124
-      $.ajax({
125
-        url: marlin_config+'/'+boards_file,
126
-        type: 'GET',
127
-        async: false,
128
-        cache: false,
129
-        success: function(txt) {
130
-          // Get all the boards and save them into an object
131
-          self.initBoardsFromText(txt);
132
-          has_boards = true;
133
-        },
134
-        error: errFunc
135
-      });
136
-
137
-      // Read Configuration.h
138
-      $.ajax({
139
-        url: marlin_config+'/'+config_file,
140
-        type: 'GET',
141
-        async: false,
142
-        cache: false,
143
-        success: function(txt) {
144
-          // File contents into the textarea
145
-          $config.text(txt);
146
-          // Get thermistor info too
147
-          self.initThermistorsFromText(txt);
148
-          has_config = true;
149
-        },
150
-        error: errFunc
151
-      });
152
-
153
-      // Read Configuration.h
154
-      $.ajax({
155
-        url: marlin_config+'/'+config_adv_file,
156
-        type: 'GET',
157
-        async: false,
158
-        cache: false,
159
-        success: function(txt) {
160
-          // File contents into the textarea
161
-          $config_adv.text(txt);
162
-          has_config_adv = true;
163
-          self.refreshConfigForm();
164
-        },
165
-        error: errFunc
114
+      if (!fileUploader.hasFileUploaderSupport())
115
+        this.setMessage("Your browser doesn't support the file reading API.", 'error');
116
+
117
+      // Read boards.h, Configuration.h, Configuration_adv.h
118
+      var ajax_count = 0, success_count = 0;
119
+      var loaded_items = {};
120
+      var config_files = [boards_file, config_file, config_adv_file];
121
+      $.each(config_files, function(i,fname){
122
+        self.log("Loading " + fname + "...", 3);
123
+        $.ajax({
124
+          url: marlin_config+'/'+fname,
125
+          type: 'GET',
126
+          async: true,
127
+          cache: false,
128
+          success: function(txt) {
129
+            self.log("Loaded " + fname + "...", 3);
130
+            loaded_items[fname] = function(){ self.fileLoaded(fname, txt); };
131
+            success_count++;
132
+          },
133
+          complete: function() {
134
+            ajax_count++;
135
+            if (ajax_count >= 3) {
136
+              $.each(config_files, function(i,fname){ if (typeof loaded_items[fname] != 'undefined') loaded_items[fname](); });
137
+              self.refreshConfigForm();
138
+              if (success_count < ajax_count)
139
+                self.setMessage('Unable to load configurations. Use the upload field instead.', 'error');
140
+            }
141
+          }
142
+        });
166
       });
143
       });
144
+    },
167
 
145
 
146
+    setMessage: function(msg,type) {
147
+      if (msg) {
148
+        if (typeof type == 'undefined') type = 'message';
149
+        var $err = $('<p class="'+type+'">'+msg+'</p>'), err = $err[0];
150
+        $('#message').prepend($err);
151
+        var baseColor = $err.css('color').replace(/rgba?\(([^),]+,[^),]+,[^),]+).*/, 'rgba($1,');
152
+        var d = new Date();
153
+        err.startTime = d.getTime();
154
+        err.pulser = setInterval(function(){
155
+            d = new Date();
156
+            var pulse_time = (d.getTime() - err.startTime);
157
+            $err.css({color:baseColor+(0.5+Math.sin(pulse_time/200)*0.4)+')'});
158
+            if (pulse_time > 5000) {
159
+              clearInterval(err.pulser);
160
+              $err.remove();
161
+            }
162
+          }, 50);
163
+      }
164
+      else {
165
+        $('#message p.error, #message p.warning').each(function() {
166
+          if (typeof this.pulser != 'undefined' && this.pulser)
167
+            clearInterval(this.pulser);
168
+          $(this).remove();
169
+        });
170
+      }
168
     },
171
     },
169
 
172
 
173
+    /**
174
+     * Init the boards array from a boards.h file
175
+     */
170
     initBoardsFromText: function(txt) {
176
     initBoardsFromText: function(txt) {
171
       boards_list = {};
177
       boards_list = {};
172
       var r, findDef = new RegExp('[ \\t]*#define[ \\t]+(BOARD_[\\w_]+)[ \\t]+(\\d+)[ \\t]*(//[ \\t]*)?(.+)?', 'gm');
178
       var r, findDef = new RegExp('[ \\t]*#define[ \\t]+(BOARD_[\\w_]+)[ \\t]+(\\d+)[ \\t]*(//[ \\t]*)?(.+)?', 'gm');
173
       while((r = findDef.exec(txt)) !== null) {
179
       while((r = findDef.exec(txt)) !== null) {
174
         boards_list[r[1]] = r[2].prePad(3, '  ') + " — " + r[4].replace(/\).*/, ')');
180
         boards_list[r[1]] = r[2].prePad(3, '  ') + " — " + r[4].replace(/\).*/, ')');
175
       }
181
       }
176
-      this.log("Loaded boards", 0);
177
-      this.log(boards_list, 0);
182
+      this.log("Loaded boards", 3); this.log(boards_list, 3);
183
+      has_boards = true;
178
     },
184
     },
179
 
185
 
186
+    /**
187
+     * Init the thermistors array from the Configuration.h file
188
+     */
180
     initThermistorsFromText: function(txt) {
189
     initThermistorsFromText: function(txt) {
181
       // Get all the thermistors and save them into an object
190
       // Get all the thermistors and save them into an object
182
       var r, s, findDef = new RegExp('(//.*\n)+\\s+(#define[ \\t]+TEMP_SENSOR_0)', 'g');
191
       var r, s, findDef = new RegExp('(//.*\n)+\\s+(#define[ \\t]+TEMP_SENSOR_0)', 'g');
187
       }
196
       }
188
     },
197
     },
189
 
198
 
190
-    handleFileLoad: function(file, $uploader) {
191
-      file += '';
199
+    /**
200
+     * Handle a file being dropped on the file field
201
+     */
202
+    handleFileLoad: function(txt, $uploader) {
203
+      txt += '';
192
       var filename = $uploader.val().replace(/.*[\/\\](.*)$/, '$1');
204
       var filename = $uploader.val().replace(/.*[\/\\](.*)$/, '$1');
193
       switch(filename) {
205
       switch(filename) {
194
         case boards_file:
206
         case boards_file:
195
-          this.initBoardsFromText(file);
196
-          has_boards = true;
207
+        case config_file:
208
+        case config_adv_file:
209
+          this.fileLoaded(filename, txt);
210
+          break;
211
+        default:
212
+          this.log("Can't parse "+filename, 1);
213
+          break;
214
+      }
215
+    },
216
+
217
+    fileLoaded: function(filename, txt) {
218
+      this.log("fileLoaded:"+filename,4);
219
+      switch(filename) {
220
+        case boards_file:
221
+          this.initBoardsFromText(txt);
197
           $('#MOTHERBOARD').html('').addOptions(boards_list);
222
           $('#MOTHERBOARD').html('').addOptions(boards_list);
198
           if (has_config) this.initField('MOTHERBOARD');
223
           if (has_config) this.initField('MOTHERBOARD');
224
+          this.setMessage(boards_file+' loaded successfully.');
199
           break;
225
           break;
200
         case config_file:
226
         case config_file:
201
           if (has_boards) {
227
           if (has_boards) {
202
-            $config.text(file);
203
-            has_config = true;
204
-            total_config_lines = file.replace(/[^\n]+/g, '').length;
205
-            this.initThermistorsFromText(file);
228
+            $config.text(txt);
229
+            total_config_lines = txt.split(/\r?\n|\r/).length;
230
+            this.initThermistorsFromText(txt);
206
             this.purgeDefineInfo(false);
231
             this.purgeDefineInfo(false);
207
             this.refreshConfigForm();
232
             this.refreshConfigForm();
233
+            this.setMessage(config_file+' loaded successfully.');
234
+            has_config = true;
208
           }
235
           }
209
           else {
236
           else {
210
-            alert("Upload a " + boards_file + " file first!");
237
+            this.setMessage("Upload a " + boards_file + " file first!", 'error');
211
           }
238
           }
212
           break;
239
           break;
213
         case config_adv_file:
240
         case config_adv_file:
214
           if (has_config) {
241
           if (has_config) {
215
-            $config_adv.text(file);
216
-            has_config_adv = true;
217
-            total_config_adv_lines = file.replace(/[^\n]+/g, '').length;
242
+            $config_adv.text(txt);
243
+            total_config_adv_lines = txt.split(/\r?\n|\r/).length;
218
             this.purgeDefineInfo(true);
244
             this.purgeDefineInfo(true);
219
             this.refreshConfigForm();
245
             this.refreshConfigForm();
246
+            this.setMessage(config_adv_file+' loaded successfully.');
247
+            has_config_adv = true;
220
           }
248
           }
221
           else {
249
           else {
222
-            alert("Upload a " + config_file + " file first!");
250
+            this.setMessage("Upload a " + config_file + " file first!", 'error');
223
           }
251
           }
224
           break;
252
           break;
225
-        default:
226
-          this.log("Can't parse "+filename, 1);
227
-          break;
228
       }
253
       }
229
     },
254
     },
230
 
255
 
231
-    setupConfigForm: function() {
256
+    /**
257
+     * Add enhancements to the form
258
+     */
259
+    initConfigForm: function() {
232
       // Modify form fields and make the form responsive.
260
       // Modify form fields and make the form responsive.
233
       // As values change on the form, we could update the
261
       // As values change on the form, we could update the
234
       // contents of text areas containing the configs, for
262
       // contents of text areas containing the configs, for
258
         );
286
         );
259
       });
287
       });
260
 
288
 
289
+      // Add options to the popup menus
261
       $('#SERIAL_PORT').addOptions([0,1,2,3,4,5,6,7]);
290
       $('#SERIAL_PORT').addOptions([0,1,2,3,4,5,6,7]);
262
       $('#BAUDRATE').addOptions([2400,9600,19200,38400,57600,115200,250000]);
291
       $('#BAUDRATE').addOptions([2400,9600,19200,38400,57600,115200,250000]);
263
       $('#EXTRUDERS').addOptions([1,2,3,4]);
292
       $('#EXTRUDERS').addOptions([1,2,3,4]);
264
       $('#POWER_SUPPLY').addOptions({'1':'ATX','2':'Xbox 360'});
293
       $('#POWER_SUPPLY').addOptions({'1':'ATX','2':'Xbox 360'});
265
 
294
 
295
+      // Replace the Serial popup menu with a stepper control
266
       $('#serial_stepper').jstepper({
296
       $('#serial_stepper').jstepper({
267
         min: 0,
297
         min: 0,
268
-        max: 7,
298
+        max: 3,
269
         val: $('#SERIAL_PORT').val(),
299
         val: $('#SERIAL_PORT').val(),
270
         arrowWidth: '18px',
300
         arrowWidth: '18px',
271
         arrowHeight: '15px',
301
         arrowHeight: '15px',
276
         textStyle: {width:'1.5em',fontSize:'120%',textAlign:'center'},
306
         textStyle: {width:'1.5em',fontSize:'120%',textAlign:'center'},
277
         onChange: function(v) { $('#SERIAL_PORT').val(v).trigger('change'); }
307
         onChange: function(v) { $('#SERIAL_PORT').val(v).trigger('change'); }
278
       });
308
       });
279
-
280
     },
309
     },
281
 
310
 
282
     refreshConfigForm: function() {
311
     refreshConfigForm: function() {
322
       this.initField('MAX_REDUNDANT_TEMP_SENSOR_DIFF');
351
       this.initField('MAX_REDUNDANT_TEMP_SENSOR_DIFF');
323
 
352
 
324
       this.initField('TEMP_RESIDENCY_TIME');
353
       this.initField('TEMP_RESIDENCY_TIME');
354
+    },
325
 
355
 
326
-      // prettyPrint();
356
+    setTextAndHighlight: function($field, txt, name) {
357
+      var $elm = $('#'+name), elm = $elm[0], inf = elm.defineInfo;
358
+      if (inf == null) return;
327
     },
359
     },
328
 
360
 
329
     /**
361
     /**
330
-     * initField - make a field responsive and get info
331
-     * about its configuration file define
362
+     * Make a field responsive and initialize its defineInfo
332
      */
363
      */
333
     initField: function(name, adv) {
364
     initField: function(name, adv) {
334
-      this.log("initField:"+name,3);
365
+      this.log("initField:"+name,4);
335
       var $elm = $('#'+name), elm = $elm[0];
366
       var $elm = $('#'+name), elm = $elm[0];
336
-      if (elm.defineInfo === undefined) {
367
+      if (elm.defineInfo == null) {
337
         elm.defineInfo = this.getDefineInfo(name, adv);
368
         elm.defineInfo = this.getDefineInfo(name, adv);
338
         $elm.on($elm.attr('type') == 'text' ? 'input' : 'change', this.handleChange);
369
         $elm.on($elm.attr('type') == 'text' ? 'input' : 'change', this.handleChange);
339
       }
370
       }
340
       this.setFieldFromDefine(name);
371
       this.setFieldFromDefine(name);
341
     },
372
     },
342
 
373
 
343
-    handleChange: function(e) {
344
-      self.updateDefineFromField(e.target.id);
345
-    },
374
+    /**
375
+     * Handle any value field being changed
376
+     */
377
+    handleChange: function() { self.updateDefineFromField(this.id); },
346
 
378
 
347
-    handleSwitch: function(e) {
348
-      var $elm = $(e.target), $prev = $elm.prev();
379
+    /**
380
+     * Handle a switch checkbox being changed
381
+     */
382
+    handleSwitch: function() {
383
+      var $elm = $(this), $prev = $elm.prev();
349
       var on = $elm.prop('checked') || false;
384
       var on = $elm.prop('checked') || false;
350
       $prev.attr('disabled', !on);
385
       $prev.attr('disabled', !on);
351
       self.setDefineEnabled($prev[0].id, on);
386
       self.setDefineEnabled($prev[0].id, on);
357
     defineValue: function(name) {
392
     defineValue: function(name) {
358
       this.log('defineValue:'+name,4);
393
       this.log('defineValue:'+name,4);
359
       var $elm = $('#'+name), elm = $elm[0], inf = elm.defineInfo;
394
       var $elm = $('#'+name), elm = $elm[0], inf = elm.defineInfo;
395
+      if (inf == null) return 'n/a';
360
       var result = inf.regex.exec($(inf.field).text());
396
       var result = inf.regex.exec($(inf.field).text());
361
 
397
 
362
       this.log(result,2);
398
       this.log(result,2);
370
     defineIsEnabled: function(name) {
406
     defineIsEnabled: function(name) {
371
       this.log('defineIsEnabled:'+name,4);
407
       this.log('defineIsEnabled:'+name,4);
372
       var $elm = $('#'+name), elm = $elm[0], inf = elm.defineInfo;
408
       var $elm = $('#'+name), elm = $elm[0], inf = elm.defineInfo;
409
+      if (inf == null) return false;
373
       var result = inf.regex.exec($(inf.field).text());
410
       var result = inf.regex.exec($(inf.field).text());
374
 
411
 
375
       this.log(result,2);
412
       this.log(result,2);
376
 
413
 
377
       var on = result !== null ? result[1].trim() != '//' : true;
414
       var on = result !== null ? result[1].trim() != '//' : true;
378
-      this.log(name + ' = ' + on, 4);
415
+      this.log(name + ' = ' + on, 2);
379
 
416
 
380
       return on;
417
       return on;
381
     },
418
     },
385
      */
422
      */
386
     setDefineEnabled: function(name, val) {
423
     setDefineEnabled: function(name, val) {
387
       this.log('setDefineEnabled:'+name,4);
424
       this.log('setDefineEnabled:'+name,4);
388
-
389
-      var $elm = $('#'+name), elm = $elm[0], inf = elm.defineInfo;
390
-      var $c = $(inf.field), txt = $c.text();
425
+      var $elm = $('#'+name), inf = $elm[0].defineInfo;
426
+      if (inf == null) return;
391
 
427
 
392
       var slash = val ? '' : '//';
428
       var slash = val ? '' : '//';
393
       var newline = inf.line
429
       var newline = inf.line
394
-        .replace(/^([ \t]*)(\/\/)([ \t]*)/, '$1$3') // remove slashes
395
-        .replace(inf.pre+inf.define, inf.pre+slash+inf.define);     // add them back
396
-
397
-      txt = txt.replace(inf.line, newline);
398
-      inf.line = newline;
399
-      this.log(newline, 2);
400
-
401
-      $c.text(txt);
430
+        .replace(/^([ \t]*)(\/\/)([ \t]*)/, '$1$3')              // remove slashes
431
+        .replace(inf.pre+inf.define, inf.pre+slash+inf.define);  // add them back
402
 
432
 
403
-      // Scroll to reveal the define
404
-      this.scrollToDefine(name);
433
+      this.setDefineLine(name, newline);
405
     },
434
     },
406
 
435
 
407
     /**
436
     /**
409
      */
438
      */
410
     updateDefineFromField: function(name) {
439
     updateDefineFromField: function(name) {
411
       this.log('updateDefineFromField:'+name,4);
440
       this.log('updateDefineFromField:'+name,4);
412
-      var $elm = $('#'+name), elm = $elm[0], inf = elm.defineInfo;
413
-      var $c = $(inf.field), txt = $c.text();
414
-
415
-      // var result = inf.repl.exec(txt);
416
-      // this.log(result, 2);
441
+      var $elm = $('#'+name), inf = $elm[0].defineInfo;
442
+      if (inf == null) return;
417
 
443
 
418
       var isCheck = $elm.attr('type') == 'checkbox',
444
       var isCheck = $elm.attr('type') == 'checkbox',
419
           val = isCheck ? $elm.prop('checked') : $elm.val();
445
           val = isCheck ? $elm.prop('checked') : $elm.val();
446
           break;
472
           break;
447
       }
473
       }
448
 
474
 
449
-      txt = txt.replace(inf.line, newline);
475
+      this.setDefineLine(name, newline);
476
+    },
477
+
478
+    /**
479
+     * Set the define's line in the text to a new line,
480
+     *   then update, highlight, and scroll to the line
481
+     */
482
+    setDefineLine: function(name, newline) {
483
+      var $elm = $('#'+name), elm = $elm[0], inf = elm.defineInfo;
484
+      var $c = $(inf.field), txt = $c.text();
485
+
486
+      var hilite_token = '[HIGHLIGHTER-TOKEN]';
487
+
488
+      txt = txt.replace(inf.line, hilite_token + newline);
450
       inf.line = newline;
489
       inf.line = newline;
490
+
451
       this.log(newline, 2);
491
       this.log(newline, 2);
452
 
492
 
453
-      $c.text(txt);
493
+      // Convert txt into HTML before storing
494
+      var html = $('<div/>').text(txt).html().replace(hilite_token, '<span></span>');
495
+
496
+      // Set the final text including the highlighter
497
+      $c.html(html);
454
 
498
 
455
       // Scroll to reveal the define
499
       // Scroll to reveal the define
456
       this.scrollToDefine(name);
500
       this.scrollToDefine(name);
457
     },
501
     },
458
 
502
 
459
     /**
503
     /**
460
-     * Scroll the field to show a define
504
+     * Scroll a pre box to reveal a #define
461
      */
505
      */
462
     scrollToDefine: function(name, always) {
506
     scrollToDefine: function(name, always) {
463
       this.log('scrollToDefine:'+name,4);
507
       this.log('scrollToDefine:'+name,4);
474
 
518
 
475
       if (always == true || Math.abs($c.prop('scrollTop') - textScrollY) > halfHeight)
519
       if (always == true || Math.abs($c.prop('scrollTop') - textScrollY) > halfHeight)
476
         $c.animate({ scrollTop: textScrollY < 0 ? 0 : textScrollY });
520
         $c.animate({ scrollTop: textScrollY < 0 ? 0 : textScrollY });
477
-
478
     },
521
     },
479
 
522
 
480
     /**
523
     /**
587
       return null;
630
       return null;
588
     },
631
     },
589
 
632
 
633
+    /**
634
+     * Count the number of lines before a match, return -1 on fail
635
+     */
590
     getLineInText: function(line, txt) {
636
     getLineInText: function(line, txt) {
591
       var pos = txt.indexOf(line);
637
       var pos = txt.indexOf(line);
592
       return (pos < 0) ? pos : txt.substr(0, pos).replace(/[^\n]+/g, '').length;
638
       return (pos < 0) ? pos : txt.substr(0, pos).replace(/[^\n]+/g, '').length;

正在加载...
取消
保存