Use api.github to get Configurations

- Make api.github the default source for configurations
- Remove configurations from the configurator
- Continuation and enhancements
This commit is contained in:
Scott Lahteine 2015-02-09 04:34:57 -08:00
parent 4228758f1d
commit ba24a09f24
3 changed files with 273 additions and 158 deletions

View File

@ -2,7 +2,7 @@
/* Styles for Marlin Configurator */ /* Styles for Marlin Configurator */
body { margin: 0; padding: 0; background: #56A; color: #FFC; font-family: sans-serif; } body { margin: 0; padding: 0; background: #56A; color: #FFC; font-family: sans-serif; }
fieldset { height: 16.1em; overflow: auto; margin-top: 10px; }
#main { max-width: 1000px; margin: 0 auto; } #main { max-width: 1000px; margin: 0 auto; }
#main { padding: 0 4%; width: 92%; } #main { padding: 0 4%; width: 92%; }
#main { font-family: monospace; } #main { font-family: monospace; }
@ -19,10 +19,23 @@ h1, #message { text-align: center; }
img { display: none; } img { display: none; }
label, input, select, textarea { display: block; float: left; margin: 1px 0; } label, input, select, textarea { display: block; float: left; margin: 1px 0; }
label.newline, textarea, fieldset { clear: both; } label.newline, textarea, fieldset { clear: both; }
label { width: 130px; height: 1em; padding: 10px 480px 10px 1em; margin-right: -470px; text-align: right; } label {
width: 140px; /* label area */
height: 1em;
padding: 10px 460px 10px 1em;
margin-right: -450px;
text-align: right;
}
input[type="text"], select { margin: 0.75em 0 0; } input[type="text"], select { margin: 0.75em 0 0; }
input[type="checkbox"], input[type="radio"], input[type="file"] { margin: 1em 0 0; } input[type="checkbox"], input[type="radio"], input[type="file"] { margin: 1em 0 0; }
#config_form { display: block; background: #DDD; padding: 20px; color: #000; position: relative; } #config_form {
display: block;
background: #EEE;
padding: 6px 20px 20px;
color: #000;
position: relative;
}
/*#config_text, #config_adv_text { font-family: "Andale mono", monospace; clear: both; }*/ /*#config_text, #config_adv_text { font-family: "Andale mono", monospace; clear: both; }*/
#config_text, #config_adv_text { #config_text, #config_adv_text {
height: 25em; height: 25em;
@ -32,6 +45,8 @@ input[type="checkbox"], input[type="radio"], input[type="file"] { margin: 1em 0
overflow: auto; overflow: auto;
background-color: #FFF; background-color: #FFF;
color: #000; color: #000;
font-family: "Fira Mono";
font-size: small;
} }
input[type="checkbox"], input[type="radio"].enabler { margin-left: 1em; } input[type="checkbox"], input[type="radio"].enabler { margin-left: 1em; }
input:disabled { color: #BBB; } input:disabled { color: #BBB; }
@ -39,31 +54,35 @@ input:disabled { color: #BBB; }
h1, h2, h3, h4, h5, h6 { clear: both; } h1, h2, h3, h4, h5, h6 { clear: both; }
h2 { margin: 0; padding: 1em 0 0; } h2 { margin: 0; padding: 1em 0 0; }
ul.tabs { display: inline; list-style: none; } ul.tabs { padding: 0; list-style: none; }
ul.tabs li { display: inline; } ul.tabs li { display: inline; }
ul.tabs li a, ul.tabs li a,
ul.tabs li a.active:hover, ul.tabs li a.active:hover,
ul.tabs li a.active:active { ul.tabs li a.active:active {
display: block; display: block;
float: left; float: left;
background: #666; background: #1E4059;
color: #CCC; color: #CCC;
font-size: 150%; font-size: 110%;
border-radius: 0.25em 0.25em 0 0; border-radius: 0.25em 0.25em 0 0;
margin: 0 4px 0 0; margin: 0 4px 0 0;
padding: 2px 4px; padding: 2px 8px;
text-decoration: none; text-decoration: none;
font-family: georgia,"times new roman",times;
} }
ul.tabs li a.active:link, ul.tabs li a.active:link,
ul.tabs li a.active:visited { ul.tabs li a.active:visited {
background: #DDD; background: #DDD;
color: #900; color: #06F;
cursor: default; cursor: default;
margin-top: -4px;
padding-bottom: 4px;
padding-top: 4px;
} }
ul.tabs li a:hover, ul.tabs li a:hover,
ul.tabs li a:active { ul.tabs li a:active {
background: #777; background: #000;
color: #DDD; color: #FFF;
} }
fieldset { display: none; border: 1px solid #AAA; border-radius: 1em; } fieldset { display: none; border: 1px solid #AAA; border-radius: 1em; }
@ -118,3 +137,32 @@ fieldset legend { display: none; }
left: 20px; left: 20px;
} }
#tooltip>strong { color: #00B; } #tooltip>strong { color: #00B; }
span.disclose {
float: right;
margin-top: -10px;
width: 0;
height: 0;
cursor: pointer;
border-left: 8px solid transparent;
border-right: 8px solid transparent;
border-top: 10px solid #000;
}
span.disclose.closed {
margin: -14px 4px 0 0;
border-top: 8px solid transparent;
border-bottom: 8px solid transparent;
border-right: 10px solid #000;
}
span.disclose.almost {
-ms-transform: rotate(45deg); /* IE 9 */
-webkit-transform: rotate(45deg); /* Chrome, Safari, Opera */
transform: rotate(45deg);
}
span.disclose.closed.almost {
-ms-transform: rotate(315deg); /* IE 9 */
-webkit-transform: rotate(315deg); /* Chrome, Safari, Opera */
transform: rotate(315deg);
}
#tipson { float: right; font-weight: bold; font-size: 100%; font-family: helvetica; }
#tipson input { float: none; display: inline; }

View File

@ -3,6 +3,7 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>Marlin Configurator</title> <title>Marlin Configurator</title>
<link href='http://fonts.googleapis.com/css?family=Fira+Mono&amp;subset=latin,latin-ext' rel='stylesheet' type='text/css' />
<script src="js/jquery-2.1.3.min.js"></script> <script src="js/jquery-2.1.3.min.js"></script>
<script src="js/binarystring.js"></script> <script src="js/binarystring.js"></script>
<script src="js/binaryfileuploader.js"></script> <script src="js/binaryfileuploader.js"></script>
@ -26,6 +27,8 @@
<div id="tooltip"></div> <div id="tooltip"></div>
<label>Drop Files Here:</label><input type="file" id="file-upload" /> <label>Drop Files Here:</label><input type="file" id="file-upload" />
<div id="tipson"><input type="checkbox" checked /> ?</div>
<br class="clear" />
<fieldset id="machine"> <fieldset id="machine">
<legend>Machine</legend> <legend>Machine</legend>
@ -64,13 +67,25 @@
<label>Max Diff:</label> <label>Max Diff:</label>
<input name="MAX_REDUNDANT_TEMP_SENSOR_DIFF" type="text" size="3" maxlength="2" /> <input name="MAX_REDUNDANT_TEMP_SENSOR_DIFF" type="text" size="3" maxlength="2" />
<label class="newline">Temp Residency Time (s):</label> <label>Temp Residency Time (s):</label>
<input name="TEMP_RESIDENCY_TIME" type="text" size="3" maxlength="2" /> <input name="TEMP_RESIDENCY_TIME" type="text" size="3" maxlength="2" />
</fieldset> </fieldset>
<h2>Marlin/Configuration.h</h2> <fieldset id="hotends">
<legend>Hot Ends</legend>
</fieldset>
<fieldset id="heatbed">
<legend>Heated Bed</legend>
</fieldset>
<fieldset id="more">
<legend>More…</legend>
</fieldset>
<h2>Marlin/Configuration.h</h2><span class="disclose"></span>
<pre id="config_text" class="hilightable"></pre> <pre id="config_text" class="hilightable"></pre>
<h2>Marlin/Configuration_adv.h</h2> <h2>Marlin/Configuration_adv.h</h2><span class="disclose"></span>
<pre id="config_adv_text" class="hilightable"></pre> <pre id="config_adv_text" class="hilightable"></pre>
<br class="clear" /> <br class="clear" />

View File

@ -16,9 +16,9 @@
$(function(){ $(function(){
var marlin_config = 'config'; var marlin_config = 'https://api.github.com/repos/MarlinFirmware/Marlin/contents/Marlin';
// Extend String // Extend builtins
String.prototype.lpad = function(len, chr) { String.prototype.lpad = function(len, chr) {
if (chr === undefined) { chr = '&nbsp;'; } if (chr === undefined) { chr = '&nbsp;'; }
var s = this+'', need = len - s.length; var s = this+'', need = len - s.length;
@ -29,7 +29,13 @@ String.prototype.prePad = function(len, chr) { return len ? this.lpad(len, chr)
String.prototype.zeroPad = function(len) { return this.prePad(len, '0'); }; String.prototype.zeroPad = function(len) { return this.prePad(len, '0'); };
String.prototype.toHTML = function() { return jQuery('<div>').text(this).html(); }; String.prototype.toHTML = function() { return jQuery('<div>').text(this).html(); };
String.prototype.regEsc = function() { return this.replace(/[.?*+^$[\]\\(){}|-]/g, "\\$&"); } String.prototype.regEsc = function() { return this.replace(/[.?*+^$[\]\\(){}|-]/g, "\\$&"); }
String.prototype.lineCount = function() { var len = this.split(/\r?\n|\r/).length; return len > 0 ? len - 1 : len; }; String.prototype.lineCount = function() { var len = this.split(/\r?\n|\r/).length; return len > 0 ? len - 1 : 0; };
String.prototype.toLabel = function() { return this.replace(/_/g, ' ').toTitleCase(); }
String.prototype.toTitleCase = function() { return this.replace(/([A-Z])(\w+)/gi, function(m,p1,p2) { return p1.toUpperCase() + p2.toLowerCase(); }); }
Number.prototype.limit = function(m1, m2) {
if (m2 == null) return this > m1 ? m1 : this;
return this < m1 ? m1 : this > m2 ? m2 : this;
};
/** /**
* selectField.addOptions takes an array or keyed object * selectField.addOptions takes an array or keyed object
@ -56,9 +62,11 @@ var configuratorApp = (function(){
boards_file = 'boards.h', boards_file = 'boards.h',
config_file = 'Configuration.h', config_file = 'Configuration.h',
config_adv_file = 'Configuration_adv.h', config_adv_file = 'Configuration_adv.h',
$form = $('#config_form'),
$tooltip = $('#tooltip'), $tooltip = $('#tooltip'),
$config = $('#config_text'), $config = $('#config_text'),
$config_adv = $('#config_adv_text'), $config_adv = $('#config_adv_text'),
define_list = [[],[]],
boards_list = {}, boards_list = {},
therms_list = {}, therms_list = {},
total_config_lines, total_config_lines,
@ -74,32 +82,11 @@ var configuratorApp = (function(){
init: function() { init: function() {
self = this; // a 'this' for use when 'this' is something else self = this; // a 'this' for use when 'this' is something else
// Set up the form // Set up the form, creating fields and fieldsets as-needed
this.initConfigForm(); this.initConfigForm();
// Make tabs for the fieldsets // Make tabs for all the fieldsets
var $fset = $('#config_form fieldset'); this.makeTabsForFieldsets();
var $tabs = $('<ul>',{class:'tabs'}), ind = 1;
$('#config_form fieldset').each(function(){
var tabID = 'TAB'+ind;
$(this).addClass(tabID);
var $leg = $(this).find('legend');
var $link = $('<a>',{href:'#'+ind,id:tabID}).text($leg.text());
$tabs.append($('<li>').append($link));
$link.click(function(e){
e.preventDefault;
var ind = this.id;
$tabs.find('.active').removeClass('active');
$(this).addClass('active');
$fset.hide();
$fset.filter('.'+this.id).show();
return false;
});
ind++;
});
$tabs.appendTo('#tabs');
$('<br>',{class:'clear'}).appendTo('#tabs');
$tabs.find('a:first').trigger('click');
// Make a droppable file uploader, if possible // Make a droppable file uploader, if possible
var $uploader = $('#file-upload'); var $uploader = $('#file-upload');
@ -110,25 +97,35 @@ var configuratorApp = (function(){
if (!fileUploader.hasFileUploaderSupport()) if (!fileUploader.hasFileUploaderSupport())
this.setMessage("Your browser doesn't support the file reading API.", 'error'); this.setMessage("Your browser doesn't support the file reading API.", 'error');
// Make the disclosure items work
$('.disclose').click(function(){
var $dis = $(this), $pre = $dis.next('pre');
var didAnim = function() {$dis.toggleClass('closed almost');};
$dis.addClass('almost').hasClass('closed')
? $pre.slideDown(500, didAnim)
: $pre.slideUp(500, didAnim);
});
// Read boards.h, Configuration.h, Configuration_adv.h // Read boards.h, Configuration.h, Configuration_adv.h
var ajax_count = 0, success_count = 0; var ajax_count = 0, success_count = 0;
var loaded_items = {}; var loaded_items = {};
var config_files = [boards_file, config_file, config_adv_file]; var config_files = [boards_file, config_file, config_adv_file];
var isGithub = marlin_config.match('api.github');
$.each(config_files, function(i,fname){ $.each(config_files, function(i,fname){
$.ajax({ $.ajax({
url: marlin_config+'/'+fname, url: marlin_config+'/'+fname,
type: 'GET', type: 'GET',
dataType: isGithub ? 'jsonp' : 'script',
async: true, async: true,
cache: false, cache: false,
success: function(txt) { success: function(txt) {
loaded_items[fname] = function(){ self.fileLoaded(fname, txt); }; loaded_items[fname] = function(){ self.fileLoaded(fname, isGithub ? atob(txt.data.content) : txt); };
success_count++; success_count++;
}, },
complete: function() { complete: function() {
ajax_count++; ajax_count++;
if (ajax_count >= 3) { if (ajax_count >= 3) {
$.each(config_files, function(i,fname){ if (loaded_items[fname] !== undefined) loaded_items[fname](); }); $.each(config_files, function(){ if (loaded_items[this]) loaded_items[this](); });
self.refreshConfigForm();
if (success_count < ajax_count) if (success_count < ajax_count)
self.setMessage('Unable to load configurations. Use the upload field instead.', 'error'); self.setMessage('Unable to load configurations. Use the upload field instead.', 'error');
} }
@ -137,34 +134,6 @@ var configuratorApp = (function(){
}); });
}, },
setMessage: function(msg,type) {
if (msg) {
if (type === undefined) type = 'message';
var $err = $('<p class="'+type+'">'+msg+'</p>'), err = $err[0];
$('#message').prepend($err);
var baseColor = $err.css('color').replace(/rgba?\(([^),]+,[^),]+,[^),]+).*/, 'rgba($1,');
var d = new Date();
err.pulse_offset = (pulse_offset += 200);
err.startTime = d.getTime() + pulse_offset;
err.pulser = setInterval(function(){
d = new Date();
var pulse_time = d.getTime() + err.pulse_offset;
$err.css({color:baseColor+(0.5+Math.sin(pulse_time/200)*0.4)+')'});
if (pulse_time - err.startTime > 5000) {
clearInterval(err.pulser);
$err.remove();
}
}, 50);
}
else {
$('#message p.error, #message p.warning').each(function() {
if (this.pulser !== undefined && this.pulser)
clearInterval(this.pulser);
$(this).remove();
});
}
},
/** /**
* Init the boards array from a boards.h file * Init the boards array from a boards.h file
*/ */
@ -174,7 +143,7 @@ var configuratorApp = (function(){
while((r = findDef.exec(txt)) !== null) { while((r = findDef.exec(txt)) !== null) {
boards_list[r[1]] = r[2].prePad(3, '  ') + " — " + r[4].replace(/\).*/, ')'); boards_list[r[1]] = r[2].prePad(3, '  ') + " — " + r[4].replace(/\).*/, ')');
} }
this.log("Loaded boards", 3); this.log(boards_list, 3); this.log("Loaded boards " + boards_list.join(' '), 3);
has_boards = true; has_boards = true;
}, },
@ -191,6 +160,45 @@ var configuratorApp = (function(){
} }
}, },
/**
* Get all the unique define names
*/
getDefinesFromText: function(txt) {
// Get all the unique #define's and save them in an array
var r, define_obj = {}, findDef = new RegExp('#define[ \\t]+(\\w+)', 'gm');
var cnt = 0;
while((r = findDef.exec(txt)) !== null) {
if (cnt++ && !(r[1] in define_obj)) define_obj[r[1]] = null;
}
this.log(Object.keys(define_obj), 2);
return Object.keys(define_obj);
},
/**
* Create placeholder fields for defines, as needed
*/
createFieldsForDefines: function(adv) {
var e = adv ? 1 : 0, n = 0;
var fail_list = [];
$.each(define_list[e], function(i,name) {
if (!$('#'+name).length) {
var $ff = $('#more');
var inf = self.getDefineInfo(name, adv);
if (inf) {
var $newlabel = $('<label>',{for:name}).text(name.toLabel());
// if (!(++n % 3))
$newlabel.addClass('newline');
var $newfield = inf.type == 'switch' ? $('<input>',{type:'checkbox'}) : $('<input>',{type:'text',size:10,maxlength:40});
$newfield.attr({id:name,name:name}).prop({defineInfo:inf});
$ff.append($newlabel, $newfield);
}
else
fail_list.push(name);
}
});
if (fail_list) this.log('Unable to parse:\n' + fail_list.join('\n'), 2);
},
/** /**
* Handle a file being dropped on the file field * Handle a file being dropped on the file field
*/ */
@ -204,7 +212,7 @@ var configuratorApp = (function(){
this.fileLoaded(filename, txt); this.fileLoaded(filename, txt);
break; break;
default: default:
this.log("Can't parse "+filename, 1); this.setMessage("Can't parse '"+filename+"'!");
break; break;
} }
}, },
@ -214,12 +222,12 @@ var configuratorApp = (function(){
*/ */
fileLoaded: function(filename, txt) { fileLoaded: function(filename, txt) {
this.log("fileLoaded:"+filename,4); this.log("fileLoaded:"+filename,4);
var err;
switch(filename) { switch(filename) {
case boards_file: case boards_file:
this.initBoardsFromText(txt); this.initBoardsFromText(txt);
$('#MOTHERBOARD').html('').addOptions(boards_list); $('#MOTHERBOARD').html('').addOptions(boards_list);
if (has_config) this.initField('MOTHERBOARD'); if (has_config) this.initField('MOTHERBOARD');
this.setMessage(boards_file+' loaded successfully.');
break; break;
case config_file: case config_file:
if (has_boards) { if (has_boards) {
@ -227,12 +235,14 @@ var configuratorApp = (function(){
total_config_lines = txt.lineCount(); total_config_lines = txt.lineCount();
this.initThermistorsFromText(txt); this.initThermistorsFromText(txt);
this.purgeDefineInfo(false); this.purgeDefineInfo(false);
define_list[0] = this.getDefinesFromText(txt);
this.log(define_list[0], 2);
this.createFieldsForDefines(0);
this.refreshConfigForm(); this.refreshConfigForm();
this.setMessage(config_file+' loaded successfully.');
has_config = true; has_config = true;
} }
else { else {
this.setMessage("Upload a " + boards_file + " file first!", 'error'); err = boards_file;
} }
break; break;
case config_adv_file: case config_adv_file:
@ -240,15 +250,20 @@ var configuratorApp = (function(){
$config_adv.text(txt); $config_adv.text(txt);
total_config_adv_lines = txt.lineCount(); total_config_adv_lines = txt.lineCount();
this.purgeDefineInfo(true); this.purgeDefineInfo(true);
define_list[1] = this.getDefinesFromText(txt);
this.log(define_list[1], 2);
this.refreshConfigForm(); this.refreshConfigForm();
this.setMessage(config_adv_file+' loaded successfully.');
has_config_adv = true; has_config_adv = true;
} }
else { else {
this.setMessage("Upload a " + config_file + " file first!", 'error'); err = config_file;
} }
break; break;
} }
this.setMessage(err
? 'Please upload a "' + boards_file + '" file first!'
: '"' + filename + '" loaded successfully.', err ? 'error' : 'message'
);
}, },
/** /**
@ -264,7 +279,7 @@ var configuratorApp = (function(){
// while(!$config.text() == null) {} // while(!$config.text() == null) {}
// Go through all form items with names // Go through all form items with names
$('#config_form').find('[name]').each(function() { $form.find('[name]').each(function() {
// Set its id to its name // Set its id to its name
var name = $(this).attr('name'); var name = $(this).attr('name');
$(this).attr({id: name}); $(this).attr({id: name});
@ -304,52 +319,59 @@ var configuratorApp = (function(){
}); });
}, },
/**
* Make tabs to switch between fieldsets
*/
makeTabsForFieldsets: function() {
// Make tabs for the fieldsets
var $fset = $form.find('fieldset');
var $tabs = $('<ul>',{class:'tabs'}), ind = 1;
$fset.each(function(){
var tabID = 'TAB'+ind;
$(this).addClass(tabID);
var $leg = $(this).find('legend');
var $link = $('<a>',{href:'#'+ind,id:tabID}).text($leg.text());
$tabs.append($('<li>').append($link));
$link.click(function(e){
e.preventDefault;
var ind = this.id;
$tabs.find('.active').removeClass('active');
$(this).addClass('active');
$fset.hide();
$fset.filter('.'+this.id).show();
return false;
});
ind++;
});
$('#tabs').html('').append($tabs);
$('<br>',{class:'clear'}).appendTo('#tabs');
$tabs.find('a:first').trigger('click');
},
/** /**
* Update all fields on the form after loading a configuration * Update all fields on the form after loading a configuration
*/ */
refreshConfigForm: function() { refreshConfigForm: function() {
/** /**
* For now I'm manually creating these references * Any manually-created form elements will remain
* but I should be able to parse Configuration.h * where they are. Unknown defines (currently most)
* and iterate the #defines. * are added to the "More..." tab for now.
* *
* For any #ifdef blocks I can create field groups * Specific exceptions can be managed by applying
* which can be dimmed together when the option * classes to the associated form fields.
* is disabled. * Sorting and arrangement can come from an included
* js file that describes the configuration in JSON.
* *
* Then we only need to specify exceptions to * For now I'm trying to derive information
* standard behavior, (which is to add a text field) * about options directly from the config file.
*/ */
this.initField('SERIAL_PORT');
this.initField('BAUDRATE');
this.initField('BTENABLED');
$('#MOTHERBOARD').html('').addOptions(boards_list); $('#MOTHERBOARD').html('').addOptions(boards_list);
this.initField('MOTHERBOARD');
this.initField('CUSTOM_MENDEL_NAME');
this.initField('MACHINE_UUID');
this.initField('EXTRUDERS');
this.initField('POWER_SUPPLY');
this.initField('PS_DEFAULT_OFF');
$('#TEMP_SENSOR_0, #TEMP_SENSOR_1, #TEMP_SENSOR_2, #TEMP_SENSOR_BED').html('').addOptions(therms_list); $('#TEMP_SENSOR_0, #TEMP_SENSOR_1, #TEMP_SENSOR_2, #TEMP_SENSOR_BED').html('').addOptions(therms_list);
this.initField('TEMP_SENSOR_0');
this.initField('TEMP_SENSOR_1');
this.initField('TEMP_SENSOR_2');
this.initField('TEMP_SENSOR_BED');
this.initField('TEMP_SENSOR_1_AS_REDUNDANT'); $.each(define_list, function() { $.each(this, function() { if ($('#'+this).length) self.initField(this); }); });
this.initField('MAX_REDUNDANT_TEMP_SENSOR_DIFF');
this.initField('TEMP_RESIDENCY_TIME');
}, },
/** /**
@ -362,13 +384,14 @@ var configuratorApp = (function(){
var inf = elm.defineInfo = this.getDefineInfo(name, adv); var inf = elm.defineInfo = this.getDefineInfo(name, adv);
$elm.on($elm.attr('type') == 'text' ? 'input' : 'change', this.handleChange); $elm.on($elm.attr('type') == 'text' ? 'input' : 'change', this.handleChange);
if (inf.comment) { if (inf.tooltip) {
var $tipme = $elm.prev('label'); var $tipme = $elm.prev('label');
if ($tipme.length) { if ($tipme.length) {
$tipme.hover( $tipme.hover(
function() { function() {
if ($('#tipson input').prop('checked')) {
var pos = $tipme.position(); var pos = $tipme.position();
$tooltip.html(inf.comment) $tooltip.html(inf.tooltip)
.append('<span>') .append('<span>')
.css({bottom:($tooltip.parent().outerHeight()-pos.top)+'px',left:(pos.left+70)+'px'}) .css({bottom:($tooltip.parent().outerHeight()-pos.top)+'px',left:(pos.left+70)+'px'})
.show(); .show();
@ -376,6 +399,7 @@ var configuratorApp = (function(){
clearTimeout(hover_timer); clearTimeout(hover_timer);
hover_timer = null; hover_timer = null;
} }
}
}, },
function() { function() {
hover_timer = setTimeout(function(){ hover_timer = setTimeout(function(){
@ -410,7 +434,7 @@ var configuratorApp = (function(){
*/ */
defineValue: function(name) { defineValue: function(name) {
this.log('defineValue:'+name,4); this.log('defineValue:'+name,4);
var $elm = $('#'+name), elm = $elm[0], inf = elm.defineInfo; var inf = $('#'+name)[0].defineInfo;
if (inf == null) return 'n/a'; if (inf == null) return 'n/a';
var result = inf.regex.exec($(inf.field).text()); var result = inf.regex.exec($(inf.field).text());
@ -424,7 +448,7 @@ var configuratorApp = (function(){
*/ */
defineIsEnabled: function(name) { defineIsEnabled: function(name) {
this.log('defineIsEnabled:'+name,4); this.log('defineIsEnabled:'+name,4);
var $elm = $('#'+name), elm = $elm[0], inf = elm.defineInfo; var inf = $('#'+name)[0].defineInfo;
if (inf == null) return false; if (inf == null) return false;
var result = inf.regex.exec($(inf.field).text()); var result = inf.regex.exec($(inf.field).text());
@ -441,15 +465,14 @@ var configuratorApp = (function(){
*/ */
setDefineEnabled: function(name, val) { setDefineEnabled: function(name, val) {
this.log('setDefineEnabled:'+name,4); this.log('setDefineEnabled:'+name,4);
var $elm = $('#'+name), inf = $elm[0].defineInfo; var inf = $('#'+name)[0].defineInfo;
if (inf == null) return; if (inf) {
var slash = val ? '' : '//'; var slash = val ? '' : '//';
var newline = inf.line var newline = inf.line
.replace(/^([ \t]*)(\/\/)([ \t]*)/, '$1$3') // remove slashes .replace(/^([ \t]*)(\/\/)([ \t]*)/, '$1$3') // remove slashes
.replace(inf.pre+inf.define, inf.pre+slash+inf.define); // add them back .replace(inf.pre+inf.define, inf.pre+slash+inf.define); // add them back
this.setDefineLine(name, newline); this.setDefineLine(name, newline);
}
}, },
/** /**
@ -486,7 +509,7 @@ var configuratorApp = (function(){
*/ */
setDefineLine: function(name, newline) { setDefineLine: function(name, newline) {
this.log('setDefineLine:'+name+'\n'+newline,4); this.log('setDefineLine:'+name+'\n'+newline,4);
var $elm = $('#'+name), elm = $elm[0], inf = elm.defineInfo; var inf = $('#'+name)[0].defineInfo;
var $c = $(inf.field), txt = $c.text(); var $c = $(inf.field), txt = $c.text();
var hilite_token = '[HIGHLIGHTER-TOKEN]'; var hilite_token = '[HIGHLIGHTER-TOKEN]';
@ -501,7 +524,7 @@ var configuratorApp = (function(){
$c.html(html); $c.html(html);
// Scroll to reveal the define // Scroll to reveal the define
this.scrollToDefine(name); if ($c.is(':visible')) this.scrollToDefine(name);
}, },
/** /**
@ -509,19 +532,17 @@ var configuratorApp = (function(){
*/ */
scrollToDefine: function(name, always) { scrollToDefine: function(name, always) {
this.log('scrollToDefine:'+name,4); this.log('scrollToDefine:'+name,4);
var $elm = $('#'+name), inf = $elm[0].defineInfo, $c = $(inf.field); var inf = $('#'+name)[0].defineInfo, $c = $(inf.field);
// Scroll to the altered text if it isn't visible // Scroll to the altered text if it isn't visible
var halfHeight = $c.height()/2, scrollHeight = $c.prop('scrollHeight'), var halfHeight = $c.height()/2, scrollHeight = $c.prop('scrollHeight'),
textScrollY = inf.lineNum * scrollHeight/(inf.adv ? total_config_adv_lines : total_config_lines) - halfHeight; lineHeight = scrollHeight/(inf.adv ? total_config_adv_lines : total_config_lines),
textScrollY = (inf.lineNum * lineHeight - halfHeight).limit(0, scrollHeight - 1);
if (textScrollY < 0) if (always || Math.abs($c.prop('scrollTop') - textScrollY) > halfHeight) {
textScrollY = 0; $c.find('span').height(lineHeight);
else if (textScrollY > scrollHeight) $c.animate({ scrollTop: textScrollY });
textScrollY = scrollHeight - 1; }
if (always == true || Math.abs($c.prop('scrollTop') - textScrollY) > halfHeight)
$c.animate({ scrollTop: textScrollY < 0 ? 0 : textScrollY });
}, },
/** /**
@ -530,7 +551,7 @@ var configuratorApp = (function(){
setFieldFromDefine: function(name) { setFieldFromDefine: function(name) {
var $elm = $('#'+name), val = this.defineValue(name); var $elm = $('#'+name), val = this.defineValue(name);
this.log('setFieldFromDefine:' + name + ' to ' + val, 4); this.log('setFieldFromDefine:' + name + ' to ' + val, 2);
// Set the field value // Set the field value
$elm.attr('type') == 'checkbox' ? $elm.prop('checked', val) : $elm.val(''+val); $elm.attr('type') == 'checkbox' ? $elm.prop('checked', val) : $elm.val(''+val);
@ -569,20 +590,20 @@ var configuratorApp = (function(){
/** /**
* Get information about a #define from configuration file text: * Get information about a #define from configuration file text:
* *
* Pre-examine the #define for its prefix, value position, suffix, etc. * - Pre-examine the #define for its prefix, value position, suffix, etc.
* Construct a regex for the #define to quickly find (and replace) values. * - Construct RegExp's for the #define to quickly find (and replace) values.
* Store the existing #define line as the key to finding it later. * - Store the existing #define line as a fast key to finding it later.
* Determine the line number of the #define so it can be scrolled to. * - Determine the line number of the #define so it can be scrolled to.
* - Gather nearby comments to be used as tooltips.
*/ */
getDefineInfo: function(name, adv) { getDefineInfo: function(name, adv) {
if (adv === undefined) adv = false; if (adv === undefined) adv = false;
this.log('getDefineInfo:'+name,4); this.log('getDefineInfo:'+name,4);
var $elm = $('#'+name), elm = $elm[0], var $c = adv ? $config_adv : $config,
$c = adv ? $config_adv : $config,
txt = $c.text(); txt = $c.text();
// a switch line with no value // a switch line with no value
var findDef = new RegExp('^([ \\t]*//)?([ \\t]*#define[ \\t]+' + elm.id + ')([ \\t]*/[*/].*)?$', 'm'), var findDef = new RegExp('^([ \\t]*//)?([ \\t]*#define[ \\t]+' + name + ')([ \\t]*/[*/].*)?$', 'm'),
result = findDef.exec(txt), result = findDef.exec(txt),
info = { type:0, adv:adv, field:$c[0], val_i: 2 }; info = { type:0, adv:adv, field:$c[0], val_i: 2 };
if (result !== null) { if (result !== null) {
@ -599,7 +620,7 @@ var configuratorApp = (function(){
} }
else { else {
// a define with quotes // a define with quotes
findDef = new RegExp('^(.*//)?(.*#define[ \\t]+' + elm.id + '[ \\t]+)("[^"]*")([ \\t]*/[*/].*)?$', 'm'); findDef = new RegExp('^(.*//)?(.*#define[ \\t]+' + name + '[ \\t]+)("[^"]*")([ \\t]*/[*/].*)?$', 'm');
result = findDef.exec(txt); result = findDef.exec(txt);
if (result !== null) { if (result !== null) {
$.extend(info, { $.extend(info, {
@ -614,7 +635,7 @@ var configuratorApp = (function(){
} }
else { else {
// a define with no quotes // a define with no quotes
findDef = new RegExp('^([ \\t]*//)?([ \\t]*#define[ \\t]+' + elm.id + '[ \\t]+)(\\S*)([ \\t]*/[*/].*)?$', 'm'); findDef = new RegExp('^([ \\t]*//)?([ \\t]*#define[ \\t]+' + name + '[ \\t]+)(\\S*)([ \\t]*/[*/].*)?$', 'm');
result = findDef.exec(txt); result = findDef.exec(txt);
if (result !== null) { if (result !== null) {
$.extend(info, { $.extend(info, {
@ -632,10 +653,10 @@ var configuratorApp = (function(){
if (info.type) { if (info.type) {
// Get the end-of-line comment, if there is one // Get the end-of-line comment, if there is one
var comment = ''; var tooltip = '';
findDef = new RegExp('.*#define[ \\t].*/[/*]+[ \\t]*(.*)'); findDef = new RegExp('.*#define[ \\t].*/[/*]+[ \\t]*(.*)');
if (info.line.search(findDef) >= 0) if (info.line.search(findDef) >= 0)
comment = info.line.replace(findDef, '$1'); tooltip = info.line.replace(findDef, '$1');
// Get all the comments immediately before the item // Get all the comments immediately before the item
var r, s; var r, s;
@ -644,16 +665,16 @@ var configuratorApp = (function(){
findDef = new RegExp('^[ \\t]*//+[ \\t]*(.*)[ \\t]*$', 'gm'); findDef = new RegExp('^[ \\t]*//+[ \\t]*(.*)[ \\t]*$', 'gm');
while((s = findDef.exec(r[1])) !== null) { while((s = findDef.exec(r[1])) !== null) {
if (s[1].match(/^#define[ \\t]/) != null) { if (s[1].match(/^#define[ \\t]/) != null) {
comment = ''; tooltip = '';
break; break;
} }
comment += ' ' + s[1] + "\n"; tooltip += ' ' + s[1] + "\n";
} }
} }
findDef = new RegExp('^[ \\t]*'+name+'[ \\t]*', 'm'); findDef = new RegExp('^[ \\t]*'+name+'[ \\t]*', 'm');
$.extend(info, { $.extend(info, {
comment: '<strong>'+name+'</strong> '+comment.replace(findDef,'').trim().toHTML(), tooltip: '<strong>'+name+'</strong> '+tooltip.replace(findDef,'').trim().toHTML(),
lineNum: this.getLineNumberOfText(info.line, txt) lineNum: this.getLineNumberOfText(info.line, txt)
}); });
} }
@ -673,6 +694,37 @@ var configuratorApp = (function(){
return (pos < 0) ? pos : txt.substr(0, pos).lineCount(); return (pos < 0) ? pos : txt.substr(0, pos).lineCount();
}, },
/**
* Add a temporary message to the page
*/
setMessage: function(msg,type) {
if (msg) {
if (type === undefined) type = 'message';
var $err = $('<p class="'+type+'">'+msg+'</p>'), err = $err[0];
$('#message').prepend($err);
var baseColor = $err.css('color').replace(/rgba?\(([^),]+,[^),]+,[^),]+).*/, 'rgba($1,');
var d = new Date();
err.pulse_offset = (pulse_offset += 200);
err.startTime = d.getTime() + pulse_offset;
err.pulser = setInterval(function(){
d = new Date();
var pulse_time = d.getTime() + err.pulse_offset;
$err.css({color:baseColor+(0.5+Math.sin(pulse_time/200)*0.4)+')'});
if (pulse_time - err.startTime > 5000) {
clearInterval(err.pulser);
$err.remove();
}
}, 50);
}
else {
$('#message p.error, #message p.warning').each(function() {
if (this.pulser !== undefined && this.pulser)
clearInterval(this.pulser);
$(this).remove();
});
}
},
log: function(o,l) { log: function(o,l) {
if (l === undefined) l = 0; if (l === undefined) l = 0;
if (this.logging>=l*1) console.log(o); if (this.logging>=l*1) console.log(o);