1
0
Fork 0
mirror of https://github.com/iNavFlight/inav-configurator.git synced 2025-07-23 16:25:19 +03:00

crude port

This commit is contained in:
Pawel Spychalski (DzikuVx) 2016-11-22 23:14:04 +01:00
parent 706cb4c36f
commit 25c8a6f61c
15 changed files with 50864 additions and 4 deletions

BIN
images/osd-bg-1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 299 KiB

View file

@ -1,3 +1,4 @@
/*global $*/
'use strict'; 'use strict';
var TABS = {}; // filled by individual tab js file var TABS = {}; // filled by individual tab js file
@ -35,7 +36,8 @@ var GUI_control = function () {
'receiver', 'receiver',
'sensors', 'sensors',
'servos', 'servos',
'setup' 'setup',
'osd'
]; ];
this.allowedTabs = this.defaultAllowedTabsWhenDisconnected; this.allowedTabs = this.defaultAllowedTabsWhenDisconnected;
@ -239,6 +241,44 @@ GUI_control.prototype.tab_switch_cleanup = function (callback) {
} }
}; };
GUI_control.prototype.switchery = function() {
$('.togglesmall').each(function(index, elem) {
var switchery = new Switchery(elem, {
size: 'small',
color: '#ffbb00',
secondaryColor: '#c4c4c4'
});
$(elem).on("change", function (evt) {
switchery.setPosition();
});
$(elem).removeClass('togglesmall');
});
$('.toggle').each(function(index, elem) {
var switchery = new Switchery(elem, {
color: '#ffbb00',
secondaryColor: '#c4c4c4'
});
$(elem).on("change", function (evt) {
switchery.setPosition();
});
$(elem).removeClass('toggle');
});
$('.togglemedium').each(function(index, elem) {
var switchery = new Switchery(elem, {
className: 'switcherymid',
color: '#ffbb00',
secondaryColor: '#c4c4c4'
});
$(elem).on("change", function (evt) {
switchery.setPosition();
});
$(elem).removeClass('togglemedium');
});
};
GUI_control.prototype.content_ready = function (callback) { GUI_control.prototype.content_ready = function (callback) {
$('.togglesmall').each(function(index, elem) { $('.togglesmall').each(function(index, elem) {
@ -303,7 +343,7 @@ GUI_control.prototype.content_ready = function (callback) {
}); });
if (callback) callback(); if (callback) callback();
} };
// initialize object into GUI variable // initialize object into GUI variable
var GUI = new GUI_control(); var GUI = new GUI_control();

92
js/injected_methods.js Normal file
View file

@ -0,0 +1,92 @@
Number.prototype.clamp = function(min, max) {
return Math.min(Math.max(this, min), max);
};
/**
* String formatting now supports currying (partial application).
* For a format string with N replacement indices, you can call .format
* with M <= N arguments. The result is going to be a format string
* with N-M replacement indices, properly counting from 0 .. N-M.
* The following Example should explain the usage of partial applied format:
* "{0}:{1}:{2}".format("a","b","c") === "{0}:{1}:{2}".format("a","b").format("c")
* "{0}:{1}:{2}".format("a").format("b").format("c") === "{0}:{1}:{2}".format("a").format("b", "c")
**/
String.prototype.format = function () {
var args = arguments;
return this.replace(/\{(\d+)\}/g, function (t, i) {
return args[i] !== void 0 ? args[i] : "{"+(i-args.length)+"}";
});
};
Array.prototype.push8 = function(val) {
this.push(0xFF & val);
return this;
};
Array.prototype.push16 = function(val) {
// low byte
this.push(0x00FF & val);
// high byte
this.push(val >> 8);
// chainable
return this;
};
Array.prototype.push32 = function(val) {
this.push8(val)
.push8(val >> 8)
.push8(val >> 16)
.push8(val >> 24);
return this;
};
DataView.prototype.offset = 0;
DataView.prototype.readU8 = function() {
if (this.byteLength >= this.offset+1) {
return this.getUint8(this.offset++);
} else {
return null;
}
};
DataView.prototype.readU16 = function() {
if (this.byteLength >= this.offset+2) {
return this.readU8() + this.readU8()*256;
} else {
return null;
}
};
DataView.prototype.readU32 = function() {
if (this.byteLength >= this.offset+4) {
return this.readU16() + this.readU16()*65536;
} else {
return null;
}
};
DataView.prototype.read8 = function() {
if (this.byteLength >= this.offset+1) {
return this.getInt8(this.offset++, 1);
} else {
return null;
}
};
DataView.prototype.read16 = function() {
this.offset += 2;
if (this.byteLength >= this.offset) {
return this.getInt16(this.offset-2, 1);
} else {
return null;
}
};
DataView.prototype.read32 = function() {
this.offset += 4;
if (this.byteLength >= this.offset) {
return this.getInt32(this.offset-4, 1);
} else {
return null;
}
};

31
js/libraries/inflection.min.js vendored Normal file
View file

@ -0,0 +1,31 @@
/*!
* inflection
* Copyright(c) 2011 Ben Lin <ben@dreamerslab.com>
* MIT Licensed
*
* @fileoverview
* A port of inflection-js to node.js module.
*/
(function(a,b){if(typeof define==="function"&&define.amd){define([],b);
}else{if(typeof exports==="object"){module.exports=b();}else{a.inflection=b();}}}(this,function(){var d=["accommodation","adulthood","advertising","advice","aggression","aid","air","aircraft","alcohol","anger","applause","arithmetic","assistance","athletics","bacon","baggage","beef","biology","blood","botany","bread","butter","carbon","cardboard","cash","chalk","chaos","chess","crossroads","countryside","dancing","deer","dignity","dirt","dust","economics","education","electricity","engineering","enjoyment","envy","equipment","ethics","evidence","evolution","fame","fiction","flour","flu","food","fuel","fun","furniture","gallows","garbage","garlic","genetics","gold","golf","gossip","grammar","gratitude","grief","guilt","gymnastics","happiness","hardware","harm","hate","hatred","health","heat","help","homework","honesty","honey","hospitality","housework","humour","hunger","hydrogen","ice","importance","inflation","information","innocence","iron","irony","jam","jewelry","judo","karate","knowledge","lack","laughter","lava","leather","leisure","lightning","linguine","linguini","linguistics","literature","litter","livestock","logic","loneliness","luck","luggage","macaroni","machinery","magic","management","mankind","marble","mathematics","mayonnaise","measles","methane","milk","money","mud","music","mumps","nature","news","nitrogen","nonsense","nurture","nutrition","obedience","obesity","oxygen","pasta","patience","physics","poetry","pollution","poverty","pride","psychology","publicity","punctuation","quartz","racism","relaxation","reliability","research","respect","revenge","rice","rubbish","rum","safety","scenery","seafood","seaside","series","shame","sheep","shopping","sleep","smoke","smoking","snow","soap","software","soil","spaghetti","species","steam","stuff","stupidity","sunshine","symmetry","tennis","thirst","thunder","timber","traffic","transportation","trust","underwear","unemployment","unity","validity","veal","vegetation","vegetarianism","vengeance","violence","vitality","warmth","wealth","weather","welfare","wheat","wildlife","wisdom","yoga","zinc","zoology"];
var i={plural:{men:new RegExp("^(m|wom)en$","gi"),people:new RegExp("(pe)ople$","gi"),children:new RegExp("(child)ren$","gi"),tia:new RegExp("([ti])a$","gi"),analyses:new RegExp("((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$","gi"),hives:new RegExp("(hi|ti)ves$","gi"),curves:new RegExp("(curve)s$","gi"),lrves:new RegExp("([lr])ves$","gi"),foves:new RegExp("([^fo])ves$","gi"),movies:new RegExp("(m)ovies$","gi"),aeiouyies:new RegExp("([^aeiouy]|qu)ies$","gi"),series:new RegExp("(s)eries$","gi"),xes:new RegExp("(x|ch|ss|sh)es$","gi"),mice:new RegExp("([m|l])ice$","gi"),buses:new RegExp("(bus)es$","gi"),oes:new RegExp("(o)es$","gi"),shoes:new RegExp("(shoe)s$","gi"),crises:new RegExp("(cris|ax|test)es$","gi"),octopi:new RegExp("(octop|vir)i$","gi"),aliases:new RegExp("(alias|canvas|status|campus)es$","gi"),summonses:new RegExp("^(summons)es$","gi"),oxen:new RegExp("^(ox)en","gi"),matrices:new RegExp("(matr)ices$","gi"),vertices:new RegExp("(vert|ind)ices$","gi"),feet:new RegExp("^feet$","gi"),teeth:new RegExp("^teeth$","gi"),geese:new RegExp("^geese$","gi"),quizzes:new RegExp("(quiz)zes$","gi"),whereases:new RegExp("^(whereas)es$","gi"),criteria:new RegExp("^(criteri)a$","gi"),genera:new RegExp("^genera$","gi"),ss:new RegExp("ss$","gi"),s:new RegExp("s$","gi")},singular:{man:new RegExp("^(m|wom)an$","gi"),person:new RegExp("(pe)rson$","gi"),child:new RegExp("(child)$","gi"),ox:new RegExp("^(ox)$","gi"),axis:new RegExp("(ax|test)is$","gi"),octopus:new RegExp("(octop|vir)us$","gi"),alias:new RegExp("(alias|status|canvas|campus)$","gi"),summons:new RegExp("^(summons)$","gi"),bus:new RegExp("(bu)s$","gi"),buffalo:new RegExp("(buffal|tomat|potat)o$","gi"),tium:new RegExp("([ti])um$","gi"),sis:new RegExp("sis$","gi"),ffe:new RegExp("(?:([^f])fe|([lr])f)$","gi"),hive:new RegExp("(hi|ti)ve$","gi"),aeiouyy:new RegExp("([^aeiouy]|qu)y$","gi"),x:new RegExp("(x|ch|ss|sh)$","gi"),matrix:new RegExp("(matr)ix$","gi"),vertex:new RegExp("(vert|ind)ex$","gi"),mouse:new RegExp("([m|l])ouse$","gi"),foot:new RegExp("^foot$","gi"),tooth:new RegExp("^tooth$","gi"),goose:new RegExp("^goose$","gi"),quiz:new RegExp("(quiz)$","gi"),whereas:new RegExp("^(whereas)$","gi"),criterion:new RegExp("^(criteri)on$","gi"),genus:new RegExp("^genus$","gi"),s:new RegExp("s$","gi"),common:new RegExp("$","gi")}};
var g=[[i.plural.men],[i.plural.people],[i.plural.children],[i.plural.tia],[i.plural.analyses],[i.plural.hives],[i.plural.curves],[i.plural.lrves],[i.plural.foves],[i.plural.aeiouyies],[i.plural.series],[i.plural.movies],[i.plural.xes],[i.plural.mice],[i.plural.buses],[i.plural.oes],[i.plural.shoes],[i.plural.crises],[i.plural.octopi],[i.plural.aliases],[i.plural.summonses],[i.plural.oxen],[i.plural.matrices],[i.plural.feet],[i.plural.teeth],[i.plural.geese],[i.plural.quizzes],[i.plural.whereases],[i.plural.criteria],[i.plural.genera],[i.singular.man,"$1en"],[i.singular.person,"$1ople"],[i.singular.child,"$1ren"],[i.singular.ox,"$1en"],[i.singular.axis,"$1es"],[i.singular.octopus,"$1i"],[i.singular.alias,"$1es"],[i.singular.summons,"$1es"],[i.singular.bus,"$1ses"],[i.singular.buffalo,"$1oes"],[i.singular.tium,"$1a"],[i.singular.sis,"ses"],[i.singular.ffe,"$1$2ves"],[i.singular.hive,"$1ves"],[i.singular.aeiouyy,"$1ies"],[i.singular.matrix,"$1ices"],[i.singular.vertex,"$1ices"],[i.singular.x,"$1es"],[i.singular.mouse,"$1ice"],[i.singular.foot,"feet"],[i.singular.tooth,"teeth"],[i.singular.goose,"geese"],[i.singular.quiz,"$1zes"],[i.singular.whereas,"$1es"],[i.singular.criterion,"$1a"],[i.singular.genus,"genera"],[i.singular.s,"s"],[i.singular.common,"s"]];
var a=[[i.singular.man],[i.singular.person],[i.singular.child],[i.singular.ox],[i.singular.axis],[i.singular.octopus],[i.singular.alias],[i.singular.summons],[i.singular.bus],[i.singular.buffalo],[i.singular.tium],[i.singular.sis],[i.singular.ffe],[i.singular.hive],[i.singular.aeiouyy],[i.singular.x],[i.singular.matrix],[i.singular.mouse],[i.singular.foot],[i.singular.tooth],[i.singular.goose],[i.singular.quiz],[i.singular.whereas],[i.singular.criterion],[i.singular.genus],[i.plural.men,"$1an"],[i.plural.people,"$1rson"],[i.plural.children,"$1"],[i.plural.genera,"genus"],[i.plural.criteria,"$1on"],[i.plural.tia,"$1um"],[i.plural.analyses,"$1$2sis"],[i.plural.hives,"$1ve"],[i.plural.curves,"$1"],[i.plural.lrves,"$1f"],[i.plural.foves,"$1fe"],[i.plural.movies,"$1ovie"],[i.plural.aeiouyies,"$1y"],[i.plural.series,"$1eries"],[i.plural.xes,"$1"],[i.plural.mice,"$1ouse"],[i.plural.buses,"$1"],[i.plural.oes,"$1"],[i.plural.shoes,"$1"],[i.plural.crises,"$1is"],[i.plural.octopi,"$1us"],[i.plural.aliases,"$1"],[i.plural.summonses,"$1"],[i.plural.oxen,"$1"],[i.plural.matrices,"$1ix"],[i.plural.vertices,"$1ex"],[i.plural.feet,"foot"],[i.plural.teeth,"tooth"],[i.plural.geese,"goose"],[i.plural.quizzes,"$1"],[i.plural.whereases,"$1"],[i.plural.ss,"ss"],[i.plural.s,""]];
var c=["and","or","nor","a","an","the","so","but","to","of","at","by","from","into","on","onto","off","out","in","over","with","for"];var k=new RegExp("(_ids|_id)$","g");
var f=new RegExp("_","g");var j=new RegExp("[ _]","g");var e=new RegExp("([A-Z])","g");var h=new RegExp("^_");var b={_apply_rules:function(q,p,o,n){if(n){q=n;
}else{var r=(b.indexOf(o,q.toLowerCase())>-1);if(!r){var m=0;var l=p.length;for(;m<l;m++){if(q.match(p[m][0])){if(p[m][1]!==undefined){q=q.replace(p[m][0],p[m][1]);
}break;}}}}return q;},indexOf:function(l,r,q,m){if(!q){q=-1;}var o=-1;var p=q;var n=l.length;for(;p<n;p++){if(l[p]===r||m&&m(l[p],r)){o=p;break;}}return o;
},pluralize:function(m,l){return b._apply_rules(m,g,d,l);},singularize:function(m,l){return b._apply_rules(m,a,d,l);},inflect:function(o,n,m,l){n=parseInt(n,10);
if(isNaN(n)){return o;}if(n===0||n>1){return b._apply_rules(o,g,d,l);}else{return b._apply_rules(o,a,d,m);}},camelize:function(t,o){var v=t.split("/");
var r=0;var q=v.length;var u,m,p,n,s;for(;r<q;r++){u=v[r].split("_");p=0;n=u.length;for(;p<n;p++){if(p!==0){u[p]=u[p].toLowerCase();}s=u[p].charAt(0);s=o&&r===0&&p===0?s.toLowerCase():s.toUpperCase();
u[p]=s+u[p].substring(1);}v[r]=u.join("");}return v.join("::");},underscore:function(o,p){if(p&&o===o.toUpperCase()){return o;}var l=o.split("::");var n=0;
var m=l.length;for(;n<m;n++){l[n]=l[n].replace(e,"_$1");l[n]=l[n].replace(h,"");}return l.join("/").toLowerCase();},humanize:function(m,l){m=m.toLowerCase();
m=m.replace(k,"");m=m.replace(f," ");if(!l){m=b.capitalize(m);}return m;},capitalize:function(l){l=l.toLowerCase();return l.substring(0,1).toUpperCase()+l.substring(1);
},dasherize:function(l){return l.replace(j,"-");},titleize:function(s){s=s.toLowerCase().replace(f," ");var q=s.split(" ");var p=0;var o=q.length;var r,n,m;
for(;p<o;p++){r=q[p].split("-");n=0;m=r.length;for(;n<m;n++){if(b.indexOf(c,r[n].toLowerCase())<0){r[n]=b.capitalize(r[n]);}}q[p]=r.join("-");}s=q.join(" ");
s=s.substring(0,1).toUpperCase()+s.substring(1);return s;},demodulize:function(m){var l=m.split("::");return l[l.length-1];},tableize:function(l){l=b.underscore(l);
l=b.pluralize(l);return l;},classify:function(l){l=b.camelize(l);l=b.singularize(l);return l;},foreign_key:function(m,l){m=b.demodulize(m);m=b.underscore(m)+((l)?(""):("_"))+"id";
return m;},ordinalize:function(s){var q=s.split(" ");var o=0;var n=q.length;for(;o<n;o++){var m=parseInt(q[o],10);if(!isNaN(m)){var r=q[o].substring(q[o].length-2);
var p=q[o].substring(q[o].length-1);var l="th";if(r!="11"&&r!="12"&&r!="13"){if(p==="1"){l="st";}else{if(p==="2"){l="nd";}else{if(p==="3"){l="rd";}}}}q[o]+=l;
}}return q.join(" ");},transform:function(o,l){var n=0;var m=l.length;for(;n<m;n++){var p=l[n];if(this.hasOwnProperty(p)){o=this[p](o);}}return o;}};b.version="1.10.0";
return b;}));

View file

@ -0,0 +1,9 @@
/*
* jQuery throttle / debounce - v1.1 - 3/7/2010
* http://benalman.com/projects/jquery-throttle-debounce-plugin/
*
* Copyright (c) 2010 "Cowboy" Ben Alman
* Dual licensed under the MIT and GPL licenses.
* http://benalman.com/about/license/
*/
(function(b,c){var $=b.jQuery||b.Cowboy||(b.Cowboy={}),a;$.throttle=a=function(e,f,j,i){var h,d=0;if(typeof f!=="boolean"){i=j;j=f;f=c}function g(){var o=this,m=+new Date()-d,n=arguments;function l(){d=+new Date();j.apply(o,n)}function k(){h=c}if(i&&!h){l()}h&&clearTimeout(h);if(i===c&&m>e){l()}else{if(f!==true){h=setTimeout(i?k:l,i===c?e-m:e)}}}if($.guid){g.guid=j.guid=j.guid||$.guid++}return g};$.debounce=function(d,e,f){return f===c?a(d,e,false):a(d,f,e!==false)}})(this);

View file

@ -45,6 +45,7 @@ var MSP_codes = {
MSP_SET_BLACKBOX_CONFIG: 81, MSP_SET_BLACKBOX_CONFIG: 81,
MSP_TRANSPONDER_CONFIG: 82, MSP_TRANSPONDER_CONFIG: 82,
MSP_SET_TRANSPONDER_CONFIG: 83, MSP_SET_TRANSPONDER_CONFIG: 83,
MSP_OSD_CONFIG: 84,
MSP_ADVANCED_CONFIG: 90, MSP_ADVANCED_CONFIG: 90,
MSP_SET_ADVANCED_CONFIG: 91, MSP_SET_ADVANCED_CONFIG: 91,
@ -1180,6 +1181,8 @@ var MSP = {
case MSP_codes.MSP_SET_FAILSAFE_CONFIG: case MSP_codes.MSP_SET_FAILSAFE_CONFIG:
console.log('Failsafe config saved'); console.log('Failsafe config saved');
break; break;
case MSP_codes.MSP_OSD_CONFIG:
break;
default: default:
console.log('Unknown code detected: ' + code); console.log('Unknown code detected: ' + code);
} else { } else {
@ -1282,6 +1285,14 @@ var MSP = {
} }
return true; return true;
}, },
promise: function(code, data) {
var self = this;
return new Promise(function(resolve) {
self.send_message(code, data, false, function(data) {
resolve(data);
});
});
},
callbacks_cleanup: function () { callbacks_cleanup: function () {
for (var i = 0; i < this.callbacks.length; i++) { for (var i = 0; i < this.callbacks.length; i++) {
clearInterval(this.callbacks[i].timer); clearInterval(this.callbacks[i].timer);

View file

@ -217,8 +217,10 @@ function onOpen(openInfo) {
// continue as usually // continue as usually
CONFIGURATOR.connectionValid = true; CONFIGURATOR.connectionValid = true;
GUI.allowedTabs = GUI.defaultAllowedTabsWhenConnected.slice(); GUI.allowedTabs = GUI.defaultAllowedTabsWhenConnected.slice();
if (semver.lt(CONFIG.apiVersion, "1.4.0")) { //TODO here we can remove led_strip tab from NAZE and CC3D at least!
GUI.allowedTabs.splice(GUI.allowedTabs.indexOf('led_strip'), 1); //FIXME add real version here
if (false && semver.lt(CONFIG.flightControllerVersion, "1.4.0")) {
GUI.allowedTabs.splice(GUI.allowedTabs.indexOf('osd'), 1);
} }
onConnect(); onConnect();

View file

@ -27,6 +27,7 @@
<link type="text/css" rel="stylesheet" href="./tabs/auxiliary.css" media="all" /> <link type="text/css" rel="stylesheet" href="./tabs/auxiliary.css" media="all" />
<link type="text/css" rel="stylesheet" href="./tabs/failsafe.css" media="all" /> <link type="text/css" rel="stylesheet" href="./tabs/failsafe.css" media="all" />
<link type="text/css" rel="stylesheet" href="./tabs/transponder.css" media="all" /> <link type="text/css" rel="stylesheet" href="./tabs/transponder.css" media="all" />
<link type="text/css" rel="stylesheet" href="./tabs/osd.css" media="all" />
<link type="text/css" rel="stylesheet" href="./css/opensans_webfontkit/fonts.css" media="all" /> <link type="text/css" rel="stylesheet" href="./css/opensans_webfontkit/fonts.css" media="all" />
<link type="text/css" rel="stylesheet" href="./css/dropdown-lists/css/style_lists.css" media="all" /> <link type="text/css" rel="stylesheet" href="./css/dropdown-lists/css/style_lists.css" media="all" />
<link type="text/css" rel="stylesheet" href="./js/libraries/switchery/switchery.css" media="all" /> <link type="text/css" rel="stylesheet" href="./js/libraries/switchery/switchery.css" media="all" />
@ -44,6 +45,9 @@
<script type="text/javascript" src="./js/libraries/semver.js"></script> <script type="text/javascript" src="./js/libraries/semver.js"></script>
<script type="text/javascript" src="./js/libraries/jbox/jBox.min.js"></script> <script type="text/javascript" src="./js/libraries/jbox/jBox.min.js"></script>
<script type="text/javascript" src="./js/libraries/switchery/switchery.js"></script> <script type="text/javascript" src="./js/libraries/switchery/switchery.js"></script>
<script type="text/javascript" src="./js/libraries/jquery.ba-throttle-debounce.min.js"></script>
<script type="text/javascript" src="./js/libraries/inflection.min.js"></script>
<script type="text/javascript" src="./js/injected_methods.js"></script>
<script type="text/javascript" src="./js/port_handler.js"></script> <script type="text/javascript" src="./js/port_handler.js"></script>
<script type="text/javascript" src="./js/port_usage.js"></script> <script type="text/javascript" src="./js/port_usage.js"></script>
<script type="text/javascript" src="./js/serial.js"></script> <script type="text/javascript" src="./js/serial.js"></script>
@ -80,6 +84,7 @@
<script type="text/javascript" src="./tabs/firmware_flasher.js"></script> <script type="text/javascript" src="./tabs/firmware_flasher.js"></script>
<script type="text/javascript" src="./tabs/failsafe.js"></script> <script type="text/javascript" src="./tabs/failsafe.js"></script>
<script type="text/javascript" src="./tabs/transponder.js"></script> <script type="text/javascript" src="./tabs/transponder.js"></script>
<script type="text/javascript" src="./tabs/osd.js"></script>
<title></title> <title></title>
</head> </head>
<body> <body>
@ -217,6 +222,7 @@
<li class="tab_servos"><a href="#" i18n="tabServos" class="tabicon ic_servo" title="Servos"></a></li> <li class="tab_servos"><a href="#" i18n="tabServos" class="tabicon ic_servo" title="Servos"></a></li>
<li class="tab_gps"><a href="#" i18n="tabGPS" class="tabicon ic_gps" title="GPS"></a></li> <li class="tab_gps"><a href="#" i18n="tabGPS" class="tabicon ic_gps" title="GPS"></a></li>
<li class="tab_motors"><a href="#" i18n="tabMotorTesting" class="tabicon ic_motor" title="Motors"></a></li> <li class="tab_motors"><a href="#" i18n="tabMotorTesting" class="tabicon ic_motor" title="Motors"></a></li>
<li class="tab_osd"><a href="#" i18n="tabOSD" class="tabicon ic_osd" title="OSD"></a></li>
<li class="tab_transponder"><a href="#" i18n="tabTransponder" class="tabicon ic_transponder" title="Transponder"></a></li> <li class="tab_transponder"><a href="#" i18n="tabTransponder" class="tabicon ic_transponder" title="Transponder"></a></li>
<li class="tab_led_strip"><a href="#" i18n="tabLedStrip" class="tabicon ic_led" title="LED Strip"></a></li> <li class="tab_led_strip"><a href="#" i18n="tabLedStrip" class="tabicon ic_led" title="LED Strip"></a></li>
<li class="tab_sensors"><a href="#" i18n="tabRawSensorData" class="tabicon ic_sensors" title="Sensors"></a></li> <li class="tab_sensors"><a href="#" i18n="tabRawSensorData" class="tabicon ic_sensors" title="Sensors"></a></li>

View file

@ -1,3 +1,4 @@
/*global $, chrome*/
'use strict'; 'use strict';
// Google Analytics // Google Analytics
@ -163,6 +164,9 @@ $(document).ready(function () {
case 'motors': case 'motors':
TABS.motors.initialize(content_ready); TABS.motors.initialize(content_ready);
break; break;
case 'osd':
TABS.osd.initialize(content_ready);
break;
case 'sensors': case 'sensors':
TABS.sensors.initialize(content_ready); TABS.sensors.initialize(content_ready);
break; break;

16385
resources/osd/bold.mcm Normal file

File diff suppressed because it is too large Load diff

16385
resources/osd/default.mcm Normal file

File diff suppressed because it is too large Load diff

16385
resources/osd/large.mcm Normal file

File diff suppressed because it is too large Load diff

460
tabs/osd.css Normal file
View file

@ -0,0 +1,460 @@
.tab-osd .info {
margin: 10px 0 0 0;
position: relative;
}
.tab-osd .info .progressLabel {
position: absolute;
width: 100%;
height: 26px;
top: 0px;
left: 0;
text-align: center;
line-height: 24px;
color: white;
font-weight: bold;
/* text-shadow: 1px 0px 2px rgba(0, 0, 0, 0.9);*/
}
.darkgrey {
background-color: #575757;
}
.tab-osd .spacer_box_title {
float: none;
}
.tab-osd .info {
float: left;
width: 100%;
}
.info .progressLabel a {
color: white;
}
.info .progressLabel a:hover {
text-decoration: underline;
}
.info .progress {
width: 100%;
height: 26px;
border-radius: 5px;
border: 1px solid silver;
}
.info .progress {
-webkit-appearance: none;
}
.info .progress::-webkit-progress-bar {
background-color: #4f4f4f;
border-radius: 4px;
box-shadow: inset 0px 0px 5px #2f2f2f;
}
.info .progress::-webkit-progress-value {
background-color: #F86008;
border-radius: 4px;
}
.info .progress.valid::-webkit-progress-bar {
background-color: #56ac1d;
border-radius: 4px;
}
.info .progress.valid::-webkit-progress-value {
background-color: #56ac1d;
border-radius: 4px;
}
.info .progress.invalid::-webkit-progress-bar {
background-color: #A62E32;
border-radius: 4px;
}
.info .progress.invalid::-webkit-progress-value {
background-color: #A62E32;
border-radius: 4px;
}
.tab-osd ul li {
list-style: initial;
list-style-type: circle;
margin-left: 30px;
}
.tab-osd .options {
position: relative;
margin-bottom: 10px;
line-height: 18px;
text-align: left;
}
.tab-osd td {
text-align: left;
}
.tab-osd .options label input {
float: left;
margin-top: 2px;
}
.tab-osd .options label span {
font-weight: bold;
margin-left: 6px;
}
.tab-osd .options select {
width: 300px;
height: 20px;
border: 1px solid silver;
}
.tab-osd .options .releases select {
width: 280px;
}
.tab-osd .option.releases {
margin: 0 0 2px 0;
line-height: 20px;
}
.tab-osd .options .description {
position: relative;
left: 0px;
font-style: italic;
color: #818181;
}
.tab-osd .cf_table td:last-child {
text-align: left;
}
.tab-osd .options .flash_on_connect_wrapper {
display: none;
}
.tab-osd .options .manual_baud_rate select {
width: 75px;
margin-left: 19px;
}
.tab-osd .release_info {
display: none;
}
.tab-osd .release_info .title {
line-height: 20px;
text-align: center;
font-weight: bold;
color: white;
border-bottom: 1px solid silver;
background-color: #3f4241;
}
.tab-osd .release_info .target {
color: blue;
}
.tab-osd .release_info p {
padding: 5px;
}
.tab-osd .release_info p a {
font-weight: bold;
}
.tab-osd .release_info p a:hover {
text-decoration: underline;
}
.tab-osd .release_info .notes {
padding: 5px;
}
.tab-osd .git_info {
display: none;
margin-bottom: 10px;
border: 1px solid silver;
}
.tab-osd .git_info .title {
line-height: 20px;
text-align: center;
font-weight: bold;
color: white;
border-bottom: 1px solid silver;
background-color: #3f4241;
}
.tab-osd .git_info p {
padding: 5px;
}
.tab-osd .git_info p a {
font-weight: bold;
}
.tab-osd .git_info p a:hover {
text-decoration: underline;
}
.tab-osd .buttons {
width: calc(100% - 20px);
margin-top: 10px;
bottom: 10px;
}
.tab-osd .buttons a {
display: block;
float: left;
margin: 0 10px 0 0;
padding: 0 15px 0 15px;
height: 28px;
line-height: 28px;
text-align: center;
font-weight: bold;
border: 1px solid silver;
background-color: #ececec;
}
.tab-osd .buttons a:hover {
background-color: #dedcdc;
}
.tab-osd .buttons a.flash_font.locked {
background-color: #b8b8b8;
}
.tab-osd .buttons a.flash_font.locked:hover {
cursor: default;
background-color: #b8b8b8;
}
.tab-osd .buttons a.load_remote_file.locked {
background-color: #b8b8b8;
}
.tab-osd .buttons a.load_remote_file.locked:hover {
cursor: default;
background-color: #b8b8b8;
}
.tab-osd .buttons .back {
float: right;
margin: 0;
}
.tab-osd .btn .disabled {
cursor: default;
color: #fff;
background-color: #AFAFAF;
border: none;
pointer-events: none;
text-shadow: none;
opacity: 0.5;
}
.tab-osd .display-layout label {
margin: .25em .1em;
display: inline-block;
}
.tab-osd .display-layout input {
margin: .1em 1em;
}
.tab-osd .display-layout input.position{
width: 5em;
border-bottom: 1px solid #ccc
}
.tab-osd .hide {
display: none;
}
.tab-osd .note {
padding: 1em;
}
.tab-osd .col {
display: inline-block;
}
.tab-osd .left {
float: left;
}
.tab-osd .right {
float: right;
margin-top: -7px;
}
.tab-osd .preview .gui_box_titlebar {
position: relative;
}
.tab-osd .preview .char {
display: inline-block;
padding: 0;
margin: 0;
}
.tab-osd .char.mouseover {
background: rgba(255,255,255,0.4);
}
.tab-osd .char.dragging {
background: rgba(255,255,255,0.4);
}
.tab-osd .char-label.mouseover {
background: rgba(255,255,255,0.4);
}
.tab-osd .preview .char[draggable="true"] {
cursor: move;
}
.tab-osd .preview-logo {
position: absolute;
right: 0;
}
.tab-osd .preview .row {
height: 18px;
}
.tab-osd .content_wrapper {
height: calc(100% - 41px);
}
.tab-osd .content_toolbar {
text-align: right;
}
.tab-osd .content_toolbar button {
margin-right: 1em;
}
button {
padding: 4px 10px !important;
font-family: 'open_sanssemibold', Arial;
font-size: 9pt !important;
cursor: pointer;
}
.fontbuttons {
display: inline-block;
position: absolute;
right: 1.2em;
top: .8em;
}
.tab-osd .display-field {
padding: 3px;
border: 1px solid transparent;
border-bottom: 1px solid #ddd;
}
.tab-osd .display-field.mouseover {
background: #fff;
border: 1px solid #ccc;
font-weight: 800;
}
.tab-osd .display-field input {
float: right;
width: 50px;
border-radius: 3px;
border: 1px solid #ddd;
padding:2px;
margin-top: -2px;
display: none;
}
.tab-osd .display-field label {
margin-left: 5px;
}
.tab-osd .display-fields {
float: left;
margin-top: 5px;
margin-bottom: 8px;
width: 100%;
}
.spacer_box_title span {
font-size: 11px;
font-weight: normal;
font-family: 'open_sansregular', 'Segoe UI', Tahoma, sans-serif;
}
.spacer_box div input {
margin-right: 5px;
}
.spacer_box div label {
margin-right: 10px;
}
.tab-osd .display-field:last-child {
border-bottom: 0px;
}
.tab-osd .preview {
width: 360px;
}
.tab-osd .preview {
/* please don't copy the generic background image from another project
* and replace the one that @nathantsoi took :)
*/
background: url(/images/osd-bg-1.jpg);
background-size: cover;
border-bottom-left-radius: 3px;
border-bottom-right-radius: 3px;
}
.tab-osd .alarms label {
display: block;
width:100%;
border-bottom: 1px solid #ddd;
margin-top:5px;
padding-bottom: 5px
}
.tab-osd .alarms label:last-child {
border-bottom: none;
padding-bottom: 0px
}
.tab-osd .alarms input {
width: 55px;
padding-left: 3px;
height: 18px;
line-height: 20px;
text-align: left;
border: 1px solid silver;
border-radius: 3px;
margin-right: 11px;
font-size: 11px;
font-weight: normal;
}
@media only screen and (max-width: 1055px) , only screen and (max-device-width: 1055px) {
.tab-osd .content_wrapper {
height: calc(100% - 30px);
}
}

127
tabs/osd.html Normal file
View file

@ -0,0 +1,127 @@
<div class="tab-osd toolbar_fixed_bottom">
<div class="content_wrapper">
<h1 class="tab_title">
OSD
</h1>
<div class="cf_doc_version_bt">
<a id="button-documentation" href="" target="_blank"></a>
</div>
<div class="unsupported hide">
<p class="note">Your flight controller isn't responding to OSD commands. This probably means that it does not have an integrated BetaFlight OSD.</p>
<p class="note">Note that some flight controllers have an onboard <a href="https://www.youtube.com/watch?v=ikKH_6SQ-Tk" target="_blank">MinimOSD</a> that can be flashed and configured with <a href="https://github.com/ShikOfTheRa/scarab-osd/releases/latest" target="_blank">scarab-osd</a>, however the MinimOSD cannot be configured through this interface.</p>
</div>
<div class="supported hide">
<div class="cf_column third_left elements">
<div class="spacer_right">
<div class="gui_box grey">
<div
class="gui_box_titlebar"
style="margin-bottom: 0px;">
<div class="spacer_box_title">
Elements
</div>
</div>
<div class="spacer_box">
<div class="display-fields"></div>
</div>
</div>
</div>
</div>
<div class="cf_column twothird">
<div class="gui_box grey preview" style="float: left;">
<div class="gui_box_titlebar image">
<div class="spacer_box_title">
Preview <span>(drag to change position)</span><span class="preview-logo cf_tip" title="Show or hide the logo in the preview window. This will not change any settings on the flight controller."></span>
</div>
</div>
<div class="display-layout">
<div class="col right">
<div class="preview">
</div>
</div>
</div>
</div>
<div class="cf_column third_right" style="width: calc(100% - 377px);">
<div class="gui_box grey">
<div class="gui_box_titlebar">
<div class="spacer_box_title">Video
Format
</div>
</div>
<div class="spacer_box">
<div class="video-types"></div>
</div>
</div>
<div class="gui_box grey units-container" style="display:none;">
<div class="gui_box_titlebar">
<div class="spacer_box_title">Units
</div>
</div>
<div class="spacer_box">
<div class="units"></div>
</div>
</div>
<div class="gui_box grey alarms-container" style="display:none;">
<div class="gui_box_titlebar">
<div class="spacer_box_title">
Alarms
</div>
</div>
<div class="spacer_box">
<div class="alarms"></div>
</div>
</div>
<div class="gui_box grey" style="display:none;">
<div class="gui_box_titlebar">
<div class="spacer_box_title">VTX
Settings
</div>
</div>
<div class="spacer_box">
<div class="vtx-settings"></div>
</div>
</div>
<div class="gui_box grey" style="display:none;">
<div class="gui_box_titlebar">
<div class="spacer_box_title">Craft Name
</div>
</div>
<div class="spacer_box">
<div class="callsign"></div>
</div>
</div>
</div>
</div>
<div id="fontmanagercontent" style="display:none; width:600px;">
<div class="font-picker" style="margin-bottom: 10px;">
<h1 class="tab_title">Font presets:</h1>
<div class="content_wrapper font-preview"></div>
<div class="fontbuttons">
<button data-font-file="default">Default</button>
<button data-font-file="bold">Bold</button>
<button data-font-file="large">Large</button>
<button class="load_font_file">Open Font File</button>
</div>
<div class="info">
<a name="progressbar"></a>
<progress class="progress" value="0" min="0" max="100"></progress>
<div class="progressLabel" style="margin-top: -21px; width: 95%; text-align: center; color: white; position: absolute;"></div>
</div>
</div>
<div class="default_btn green" style="width:100%; float:left; margin-bottom: 0px;
">
<a class="flash_font active">Upload Font</a>
</div>
</div>
<div class="clear-both"></div>
<div class="content_toolbar supported hide" style="left:0;">
<div class="btn">
<a class="active save" href="#" >Save</a>
</div>
<div class="btn">
<a class="fonts" id="fontmanager" href="#" >Font Manager</a>
</div>
</div>
</div>
</div>
</div>

923
tabs/osd.js Normal file
View file

@ -0,0 +1,923 @@
/*global $*/
'use strict';
var SYM = SYM || {};
SYM.VOLT = 0x06;
SYM.RSSI = 0x01;
SYM.AH_RIGHT = 0x02;
SYM.AH_LEFT = 0x03;
SYM.THR = 0x04;
SYM.THR1 = 0x05;
SYM.FLY_M = 0x9C;
SYM.ON_M = 0x9B;
SYM.AH_CENTER_LINE = 0x26;
SYM.AH_CENTER_LINE_RIGHT = 0x27;
SYM.AH_CENTER = 0x7E;
SYM.AH_BAR9_0 = 0x80;
SYM.AH_DECORATION = 0x13;
SYM.LOGO = 0xA0;
SYM.AMP = 0x9A;
SYM.MAH = 0x07;
SYM.METRE = 0xC;
SYM.FEET = 0xF;
SYM.GPS_SAT = 0x1F;
var FONT = FONT || {};
//FIXME This is hack!
var MSPCodes = MSP_codes;
FONT.initData = function () {
if (FONT.data) {
return;
}
FONT.data = {
// default font file name
loaded_font_file: 'default',
// array of arry of image bytes ready to upload to fc
characters_bytes: [],
// array of array of image bits by character
characters: [],
// an array of base64 encoded image strings by character
character_image_urls: []
}
};
FONT.constants = {
SIZES: {
/** NVM ram size for one font char, actual character bytes **/
MAX_NVM_FONT_CHAR_SIZE: 54,
/** NVM ram field size for one font char, last 10 bytes dont matter **/
MAX_NVM_FONT_CHAR_FIELD_SIZE: 64,
CHAR_HEIGHT: 18,
CHAR_WIDTH: 12,
LINE: 30
},
COLORS: {
// black
0: 'rgba(0, 0, 0, 1)',
// also the value 3, could yield transparent according to
// https://www.sparkfun.com/datasheets/BreakoutBoards/MAX7456.pdf
1: 'rgba(255, 255, 255, 0)',
// white
2: 'rgba(255,255,255, 1)'
}
};
/**
* Each line is composed of 8 asci 1 or 0, representing 1 bit each for a total of 1 byte per line
*/
FONT.parseMCMFontFile = function (data) {
var data = data.split("\n");
// clear local data
FONT.data.characters.length = 0;
FONT.data.characters_bytes.length = 0;
FONT.data.character_image_urls.length = 0;
// make sure the font file is valid
if (data.shift().trim() != 'MAX7456') {
var msg = 'that font file doesnt have the MAX7456 header, giving up';
console.debug(msg);
Promise.reject(msg);
}
var character_bits = [];
var character_bytes = [];
// hexstring is for debugging
FONT.data.hexstring = [];
var pushChar = function () {
FONT.data.characters_bytes.push(character_bytes);
FONT.data.characters.push(character_bits);
FONT.draw(FONT.data.characters.length - 1);
//$log.debug('parsed char ', i, ' as ', character);
character_bits = [];
character_bytes = [];
};
for (var i = 0; i < data.length; i++) {
var line = data[i];
// hexstring is for debugging
FONT.data.hexstring.push('0x' + parseInt(line, 2).toString(16));
// every 64 bytes (line) is a char, we're counting chars though, which are 2 bits
if (character_bits.length == FONT.constants.SIZES.MAX_NVM_FONT_CHAR_FIELD_SIZE * (8 / 2)) {
pushChar()
}
for (var y = 0; y < 8; y = y + 2) {
var v = parseInt(line.slice(y, y + 2), 2);
character_bits.push(v);
}
character_bytes.push(parseInt(line, 2));
}
// push the last char
pushChar();
return FONT.data.characters;
};
FONT.openFontFile = function ($preview) {
return new Promise(function (resolve) {
chrome.fileSystem.chooseEntry({type: 'openFile', accepts: [
{extensions: ['mcm']}
]}, function (fileEntry) {
FONT.data.loaded_font_file = fileEntry.name;
if (chrome.runtime.lastError) {
console.error(chrome.runtime.lastError.message);
return;
}
fileEntry.file(function (file) {
var reader = new FileReader();
reader.onloadend = function (e) {
if (e.total != 0 && e.total == e.loaded) {
FONT.parseMCMFontFile(e.target.result);
resolve();
}
else {
console.error('could not load whole font file');
}
};
reader.readAsText(file);
});
});
});
};
/**
* returns a canvas image with the character on it
*/
var drawCanvas = function (charAddress) {
var canvas = document.createElement('canvas');
var ctx = canvas.getContext("2d");
// TODO: do we want to be able to set pixel size? going to try letting the consumer scale the image.
var pixelSize = pixelSize || 1;
var width = pixelSize * FONT.constants.SIZES.CHAR_WIDTH;
var height = pixelSize * FONT.constants.SIZES.CHAR_HEIGHT;
canvas.width = width;
canvas.height = height;
for (var y = 0; y < height; y++) {
for (var x = 0; x < width; x++) {
if (!(charAddress in FONT.data.characters)) {
console.log('charAddress', charAddress, ' is not in ', FONT.data.characters.length);
}
var v = FONT.data.characters[charAddress][(y * width) + x];
ctx.fillStyle = FONT.constants.COLORS[v];
ctx.fillRect(x, y, pixelSize, pixelSize);
}
}
return canvas;
};
FONT.draw = function (charAddress) {
var cached = FONT.data.character_image_urls[charAddress];
if (!cached) {
cached = FONT.data.character_image_urls[charAddress] = drawCanvas(charAddress).toDataURL('image/png');
}
return cached;
};
FONT.msp = {
encode: function (charAddress) {
return [charAddress].concat(FONT.data.characters_bytes[charAddress].slice(0, FONT.constants.SIZES.MAX_NVM_FONT_CHAR_SIZE));
}
};
FONT.upload = function ($progress) {
return Promise.mapSeries(FONT.data.characters, function (data, i) {
$progress.val((i / FONT.data.characters.length) * 100);
return MSP.promise(MSPCodes.MSP_OSD_CHAR_WRITE, FONT.msp.encode(i));
})
.then(function () {
OSD.GUI.jbox.close();
return MSP.promise(MSPCodes.MSP_SET_REBOOT);
});
};
FONT.preview = function ($el) {
$el.empty()
for (var i = 0; i < SYM.LOGO; i++) {
var url = FONT.data.character_image_urls[i];
$el.append('<img src="' + url + '" title="0x' + i.toString(16) + '"></img>');
}
};
FONT.symbol = function (hexVal) {
return String.fromCharCode(hexVal);
};
var OSD = OSD || {};
// parsed fc output and output to fc, used by to OSD.msp.encode
OSD.initData = function () {
OSD.data = {
video_system: null,
unit_mode: null,
alarms: [],
display_items: [],
last_positions: {},
preview_logo: false,
preview: []
};
};
OSD.initData();
OSD.constants = {
VISIBLE: 0x0800,
VIDEO_TYPES: [
'AUTO',
'PAL',
'NTSC'
],
VIDEO_LINES: {
PAL: 16,
NTSC: 13
},
VIDEO_BUFFER_CHARS: {
PAL: 480,
NTSC: 390
},
UNIT_TYPES: [
'IMPERIAL',
'METRIC'
],
AHISIDEBARWIDTHPOSITION: 7,
AHISIDEBARHEIGHTPOSITION: 3,
// All display fields, from every version, do not remove elements, only add!
ALL_DISPLAY_FIELDS: {
MAIN_BATT_VOLTAGE: {
name: 'MAIN_BATT_VOLTAGE',
default_position: -29,
positionable: true,
preview: FONT.symbol(SYM.VOLT) + '16.8'
},
RSSI_VALUE: {
name: 'RSSI_VALUE',
default_position: -59,
positionable: true,
preview: FONT.symbol(SYM.RSSI) + '99'
},
TIMER: {
name: 'TIMER',
default_position: -39,
positionable: true,
preview: FONT.symbol(SYM.ON_M) + ' 11:11'
},
THROTTLE_POSITION: {
name: 'THROTTLE_POSITION',
default_position: -9,
positionable: true,
preview: FONT.symbol(SYM.THR) + FONT.symbol(SYM.THR1) + ' 69'
},
CPU_LOAD: {
name: 'CPU_LOAD',
default_position: 26,
positionable: true,
preview: '15'
},
VTX_CHANNEL: {
name: 'VTX_CHANNEL',
default_position: 1,
positionable: true,
preview: 'CH:1'
},
VOLTAGE_WARNING: {
name: 'VOLTAGE_WARNING',
default_position: -80,
positionable: true,
preview: 'LOW VOLTAGE'
},
ARMED: {
name: 'ARMED',
default_position: -107,
positionable: true,
preview: 'ARMED'
},
DISARMED: {
name: 'DISARMED',
default_position: -109,
positionable: true,
preview: 'DISARMED'
},
CROSSHAIRS: {
name: 'CROSSHAIRS',
default_position: -1,
positionable: false
},
ARTIFICIAL_HORIZON: {
name: 'ARTIFICIAL_HORIZON',
default_position: -1,
positionable: false
},
HORIZON_SIDEBARS: {
name: 'HORIZON_SIDEBARS',
default_position: -1,
positionable: false
},
CURRENT_DRAW: {
name: 'CURRENT_DRAW',
default_position: -23,
positionable: true,
preview: FONT.symbol(SYM.AMP) + '42.0'
},
MAH_DRAWN: {
name: 'MAH_DRAWN',
default_position: -18,
positionable: true,
preview: FONT.symbol(SYM.MAH) + '690'
},
CRAFT_NAME: {
name: 'CRAFT_NAME',
default_position: -77,
positionable: true,
preview: '[CRAFT_NAME]'
},
ALTITUDE: {
name: 'ALTITUDE',
default_position: 62,
positionable: true,
preview: function (osd_data) {
return '399.7' + FONT.symbol(osd_data.unit_mode === 0 ? SYM.FEET : SYM.METRE)
}
},
ONTIME: {
name: 'ONTIME',
default_position: -1,
positionable: true,
preview: FONT.symbol(SYM.ON_M) + ' 4:11'
},
FLYTIME: {
name: 'FLYTIME',
default_position: -1,
positionable: true,
preview: FONT.symbol(SYM.FLY_M) + ' 4:11'
},
FLYMODE: {
name: 'FLYMODE',
default_position: -1,
positionable: true,
preview: 'STAB'
},
GPS_SPEED: {
name: 'GPS_SPEED',
default_position: -1,
positionable: true,
preview: '40'
},
GPS_SATS: {
name: 'GPS_SATS',
default_position: -1,
positionable: true,
preview: FONT.symbol(SYM.GPS_SAT) + '14'
}
}
};
// Pick display fields by version, order matters, so these are going in an array... pry could iterate the example map instead
OSD.chooseFields = function () {
var F = OSD.constants.ALL_DISPLAY_FIELDS;
OSD.constants.DISPLAY_FIELDS = [
F.RSSI_VALUE,
F.MAIN_BATT_VOLTAGE,
F.CROSSHAIRS,
F.ARTIFICIAL_HORIZON,
F.HORIZON_SIDEBARS,
F.ONTIME,
F.FLYTIME,
F.FLYMODE,
F.CRAFT_NAME,
F.THROTTLE_POSITION,
F.VTX_CHANNEL,
F.CURRENT_DRAW,
F.MAH_DRAWN,
F.GPS_SPEED,
F.GPS_SATS,
F.ALTITUDE
]
};
OSD.updateDisplaySize = function () {
var video_type = OSD.constants.VIDEO_TYPES[OSD.data.video_system];
if (video_type == 'AUTO') {
video_type = 'PAL';
}
// compute the size
OSD.data.display_size = {
x: FONT.constants.SIZES.LINE,
y: OSD.constants.VIDEO_LINES[video_type],
total: null
};
};
OSD.msp = {
/**
* Note, unsigned 16 bit int for position ispacked:
* 0: unused
* v: visible flag
* b: blink flag
* y: y coordinate
* x: x coordinate
* 0000 vbyy yyyx xxxx
*/
helpers: {
unpack: {
position: function (bits, c) {
var display_item = {};
// size * y + x
display_item.position = FONT.constants.SIZES.LINE * ((bits >> 5) & 0x001F) + (bits & 0x001F);
display_item.isVisible = (bits & OSD.constants.VISIBLE) != 0;
return display_item;
}
},
pack: {
position: function (display_item) {
var isVisible = display_item.isVisible;
var position = display_item.position;
return (isVisible ? 0x0800 : 0) | (((position / FONT.constants.SIZES.LINE) & 0x001F) << 5) | (position % FONT.constants.SIZES.LINE);
}
}
},
encodeOther: function () {
var result = [-1, OSD.data.video_system];
result.push8(OSD.data.unit_mode);
// watch out, order matters! match the firmware
result.push8(OSD.data.alarms.rssi.value);
result.push16(OSD.data.alarms.cap.value);
result.push16(OSD.data.alarms.time.value);
result.push16(OSD.data.alarms.alt.value);
return result;
},
encode: function (display_item) {
var buffer = [];
buffer.push8(display_item.index);
buffer.push16(this.helpers.pack.position(display_item));
return buffer;
},
// Currently only parses MSP_MAX_OSD responses, add a switch on payload.code if more codes are handled
decode: function (payload) {
var view = payload.data;
var d = OSD.data;
d.compiled_in = view.readU8();
d.video_system = view.readU8();
d.unit_mode = view.readU8();
d.alarms = {};
d.alarms['rssi'] = { display_name: 'Rssi', value: view.readU8() };
d.alarms['cap'] = { display_name: 'Capacity', value: view.readU16() };
d.alarms['time'] = { display_name: 'Minutes', value: view.readU16() };
d.alarms['alt'] = { display_name: 'Altitude', value: view.readU16() };
d.display_items = [];
// start at the offset from the other fields
while (view.offset < view.byteLength) {
var v = null;
v = view.readU16();
var j = d.display_items.length;
var c = OSD.constants.DISPLAY_FIELDS[j];
if (c) {
d.display_items.push($.extend({
name: c.name,
index: j,
positionable: c.positionable,
preview: typeof(c.preview) === 'function' ? c.preview(d) : c.preview
}, this.helpers.unpack.position(v, c)));
}
}
OSD.updateDisplaySize();
}
};
OSD.GUI = {};
OSD.GUI.preview = {
onMouseEnter: function () {
if (!$(this).data('field')) {
return;
}
$('.field-' + $(this).data('field').index).addClass('mouseover')
},
onMouseLeave: function () {
if (!$(this).data('field')) {
return;
}
$('.field-' + $(this).data('field').index).removeClass('mouseover')
},
onDragStart: function (e) {
var ev = e.originalEvent;
ev.dataTransfer.setData("text/plain", $(ev.target).data('field').index);
ev.dataTransfer.setDragImage($(this).data('field').preview_img, 6, 9);
},
onDragOver: function (e) {
var ev = e.originalEvent;
ev.preventDefault();
ev.dataTransfer.dropEffect = "move";
$(this).css({
background: 'rgba(0,0,0,.5)'
});
},
onDragLeave: function (e) {
// brute force unstyling on drag leave
$(this).removeAttr('style');
},
onDrop: function (e) {
var ev = e.originalEvent;
var position = $(this).removeAttr('style').data('position');
var field_id = parseInt(ev.dataTransfer.getData('text'))
var display_item = OSD.data.display_items[field_id];
var overflows_line = FONT.constants.SIZES.LINE - ((position % FONT.constants.SIZES.LINE) + display_item.preview.length);
if (overflows_line < 0) {
position += overflows_line;
}
$('input.' + field_id + '.position').val(position).change();
}
};
TABS.osd = {};
TABS.osd.initialize = function (callback) {
var self = this;
if (GUI.active_tab != 'osd') {
GUI.active_tab = 'osd';
}
$('#content').load("./tabs/osd.html", function () {
// translate to user-selected language
localize();
// Open modal window
OSD.GUI.jbox = new jBox('Modal', {
width: 600,
height: 240,
closeButton: 'title',
animation: false,
attach: $('#fontmanager'),
title: 'OSD Font Manager',
content: $('#fontmanagercontent')
});
// 2 way binding... sorta
function updateOsdView() {
// ask for the OSD config data
MSP.promise(MSPCodes.MSP_OSD_CONFIG)
.then(function (info) {
console.log(info);
OSD.chooseFields();
if (!info.length) {
$('.unsupported').fadeIn();
return;
}
$('.supported').fadeIn();
OSD.msp.decode(info);
// show Betaflight logo in preview
var $previewLogo = $('.preview-logo').empty();
$previewLogo.append(
$('<label for="preview-logo">Logo: </label><input type="checkbox" name="preview-logo" class="togglesmall"></input>')
.attr('checked', OSD.data.preview_logo)
.change(function (e) {
OSD.data.preview_logo = $(this).attr('checked') == undefined;
updateOsdView();
})
);
// video mode
var $videoTypes = $('.video-types').empty();
for (var i = 0; i < OSD.constants.VIDEO_TYPES.length; i++) {
var type = OSD.constants.VIDEO_TYPES[i];
var $checkbox = $('<label/>').append($('<input name="video_system" type="radio"/>' + type + '</label>')
.prop('checked', i === OSD.data.video_system)
.data('type', type)
.data('type', i)
);
$videoTypes.append($checkbox);
}
$videoTypes.find(':radio').click(function (e) {
OSD.data.video_system = $(this).data('type');
MSP.promise(MSPCodes.MSP_SET_OSD_CONFIG, OSD.msp.encodeOther())
.then(function () {
updateOsdView();
});
});
// units
$('.units-container').show();
var $unitMode = $('.units').empty();
for (var i = 0; i < OSD.constants.UNIT_TYPES.length; i++) {
var type = OSD.constants.UNIT_TYPES[i];
var $checkbox = $('<label/>').append($('<input name="unit_mode" type="radio"/>' + type + '</label>')
.prop('checked', i === OSD.data.unit_mode)
.data('type', type)
.data('type', i)
);
$unitMode.append($checkbox);
}
$unitMode.find(':radio').click(function (e) {
OSD.data.unit_mode = $(this).data('type');
MSP.promise(MSPCodes.MSP_SET_OSD_CONFIG, OSD.msp.encodeOther())
.then(function () {
updateOsdView();
});
});
// alarms
$('.alarms-container').show();
var $alarms = $('.alarms').empty();
for (let k in OSD.data.alarms) {
var alarm = OSD.data.alarms[k];
var alarmInput = $('<input name="alarm" type="number" id="' + k + '"/>' + alarm.display_name + '</label>');
alarmInput.val(alarm.value);
alarmInput.blur(function (e) {
OSD.data.alarms[$(this)[0].id].value = $(this)[0].value;
MSP.promise(MSPCodes.MSP_SET_OSD_CONFIG, OSD.msp.encodeOther())
.then(function () {
updateOsdView();
});
});
var $input = $('<label/>').append(alarmInput);
$alarms.append($input);
}
// display fields on/off and position
var $displayFields = $('.display-fields').empty();
for (let field of
OSD.data.display_items
)
{
// versioning related, if the field doesn't exist at the current flight controller version, just skip it
if (!field.name) {
continue;
}
var checked = field.isVisible ? 'checked' : '';
var $field = $('<div class="display-field field-' + field.index + '"/>');
$field.append(
$('<input type="checkbox" name="' + field.name + '" class="togglesmall"></input>')
.data('field', field)
.attr('checked', field.isVisible)
.change(function (e) {
var field = $(this).data('field');
var $position = $(this).parent().find('.position.' + field.name);
field.isVisible = !field.isVisible;
if (field.isVisible) {
$position.show();
} else {
$position.hide();
}
MSP.promise(MSPCodes.MSP_SET_OSD_CONFIG, OSD.msp.encode(field))
.then(function () {
updateOsdView();
});
})
);
$field.append('<label for="' + field.name + '" class="char-label">' + inflection.titleize(field.name) + '</label>');
if (field.positionable && field.isVisible) {
$field.append(
$('<input type="number" class="' + field.index + ' position"></input>')
.data('field', field)
.val(field.position)
.change($.debounce(250, function (e) {
var field = $(this).data('field');
field.position = parseInt($(this).val());
MSP.promise(MSPCodes.MSP_SET_OSD_CONFIG, OSD.msp.encode(field))
.then(function () {
updateOsdView();
});
}))
);
}
$displayFields.append($field);
}
GUI.switchery();
// buffer the preview
OSD.data.preview = [];
OSD.data.display_size.total = OSD.data.display_size.x * OSD.data.display_size.y;
for (let field of
OSD.data.display_items
)
{
// reset fields that somehow end up off the screen
if (field.position > OSD.data.display_size.total) {
field.position = 0;
}
}
// clear the buffer
for (var i = 0; i < OSD.data.display_size.total; i++) {
OSD.data.preview.push([null, ' '.charCodeAt(0)]);
}
// logo first, so it gets overwritten by subsequent elements
if (OSD.data.preview_logo) {
var x = 160;
for (var i = 1; i < 5; i++) {
for (var j = 3; j < 27; j++)
OSD.data.preview[i * 30 + j] = [
{name: 'LOGO', positionable: false},
x++
];
}
}
// draw all the displayed items and the drag and drop preview images
for (let field of
OSD.data.display_items
)
{
if (!field.preview || !field.isVisible) {
continue;
}
var j = (field.position >= 0) ? field.position : field.position + OSD.data.display_size.total;
// create the preview image
field.preview_img = new Image();
var canvas = document.createElement('canvas');
var ctx = canvas.getContext("2d");
// fill the screen buffer
for (var i = 0; i < field.preview.length; i++) {
var charCode = field.preview.charCodeAt(i);
OSD.data.preview[j++] = [field, charCode];
// draw the preview
var img = new Image();
img.src = FONT.draw(charCode);
ctx.drawImage(img, i * 12, 0);
}
field.preview_img.src = canvas.toDataURL('image/png');
}
var centerishPosition = 194;
// artificial horizon
if ($('input[name="ARTIFICIAL_HORIZON"]').prop('checked')) {
for (var i = 0; i < 9; i++) {
OSD.data.preview[centerishPosition - 4 + i] = SYM.AH_BAR9_0 + 4;
}
}
// crosshairs
if ($('input[name="CROSSHAIRS"]').prop('checked')) {
OSD.data.preview[centerishPosition - 1] = SYM.AH_CENTER_LINE;
OSD.data.preview[centerishPosition + 1] = SYM.AH_CENTER_LINE_RIGHT;
OSD.data.preview[centerishPosition] = SYM.AH_CENTER;
}
// sidebars
if ($('input[name="HORIZON_SIDEBARS"]').prop('checked')) {
var hudwidth = OSD.constants.AHISIDEBARWIDTHPOSITION;
var hudheight = OSD.constants.AHISIDEBARHEIGHTPOSITION;
for (var i = -hudheight; i <= hudheight; i++) {
OSD.data.preview[centerishPosition - hudwidth + (i * FONT.constants.SIZES.LINE)] = SYM.AH_DECORATION;
OSD.data.preview[centerishPosition + hudwidth + (i * FONT.constants.SIZES.LINE)] = SYM.AH_DECORATION;
}
// AH level indicators
OSD.data.preview[centerishPosition - hudwidth + 1] = SYM.AH_LEFT;
OSD.data.preview[centerishPosition + hudwidth - 1] = SYM.AH_RIGHT;
}
// render
var $preview = $('.display-layout .preview').empty();
var $row = $('<div class="row"/>');
for (var i = 0; i < OSD.data.display_size.total;) {
var charCode = OSD.data.preview[i];
if (typeof charCode === 'object') {
var field = OSD.data.preview[i][0];
var charCode = OSD.data.preview[i][1];
}
var $img = $('<div class="char"><img src=' + FONT.draw(charCode) + '></img></div>')
.on('mouseenter', OSD.GUI.preview.onMouseEnter)
.on('mouseleave', OSD.GUI.preview.onMouseLeave)
.on('dragover', OSD.GUI.preview.onDragOver)
.on('dragleave', OSD.GUI.preview.onDragLeave)
.on('drop', OSD.GUI.preview.onDrop)
.data('field', field)
.data('position', i);
if (field && field.positionable) {
$img
.addClass('field-' + field.index)
.data('field', field)
.prop('draggable', true)
.on('dragstart', OSD.GUI.preview.onDragStart);
}
else {
}
$row.append($img);
if (++i % OSD.data.display_size.x == 0) {
$preview.append($row);
$row = $('<div class="row"/>');
}
}
});
};
$('a.save').click(function () {
var self = this;
MSP.promise(MSPCodes.MSP_EEPROM_WRITE);
GUI.log('OSD settings saved');
var oldText = $(this).text();
$(this).html("Saved");
setTimeout(function () {
$(self).html(oldText);
}, 2000);
});
// font preview window
var $preview = $('.font-preview');
// init structs once, also clears current font
FONT.initData();
var $fontPicker = $('.fontbuttons button');
$fontPicker.click(function (e) {
if (!$(this).data('font-file')) {
return;
}
$fontPicker.removeClass('active');
$(this).addClass('active');
$.get('/resources/osd/' + $(this).data('font-file') + '.mcm', function (data) {
FONT.parseMCMFontFile(data);
FONT.preview($preview);
updateOsdView();
});
});
// load the first font when we change tabs
$fontPicker.first().click();
$('button.load_font_file').click(function () {
$fontPicker.removeClass('active');
FONT.openFontFile().then(function () {
FONT.preview($preview);
updateOsdView();
});
});
// font upload
$('a.flash_font').click(function () {
if (!GUI.connect_lock) { // button disabled while flashing is in progress
$('.progressLabel').text('Uploading...');
FONT.upload($('.progress').val(0)).then(function () {
var msg = 'Uploaded all ' + FONT.data.characters.length + ' characters';
console.log(msg);
$('.progressLabel').text(msg);
});
}
});
$(document).on('click', 'span.progressLabel a.save_font', function () {
chrome.fileSystem.chooseEntry({type: 'saveFile', suggestedName: 'baseflight', accepts: [
{extensions: ['mcm']}
]}, function (fileEntry) {
if (chrome.runtime.lastError) {
console.error(chrome.runtime.lastError.message);
return;
}
chrome.fileSystem.getDisplayPath(fileEntry, function (path) {
console.log('Saving firmware to: ' + path);
// check if file is writable
chrome.fileSystem.isWritableEntry(fileEntry, function (isWritable) {
if (isWritable) {
var blob = new Blob([intel_hex], {type: 'text/plain'});
fileEntry.createWriter(function (writer) {
var truncated = false;
writer.onerror = function (e) {
console.error(e);
};
writer.onwriteend = function () {
if (!truncated) {
// onwriteend will be fired again when truncation is finished
truncated = true;
writer.truncate(blob.size);
return;
}
};
writer.write(blob);
}, function (e) {
console.error(e);
});
} else {
console.log('You don\'t have write permissions for this file, sorry.');
GUI.log('You don\'t have <span style="color: red">write permissions</span> for this file');
}
});
});
});
});
$(document).keypress(function (e) {
if (e.which == 13) { // enter
// Trigger regular Flashing sequence
$('a.flash_font').click();
}
});
GUI.content_ready(callback);
});
};
TABS.osd.cleanup = function (callback) {
PortHandler.flush_callbacks();
// unbind "global" events
$(document).unbind('keypress');
$(document).off('click', 'span.progressLabel a');
if (callback) callback();
};