1
0
Fork 0
mirror of https://github.com/betaflight/betaflight-configurator.git synced 2025-07-25 09:15:49 +03:00

Changes as per review comment

This commit is contained in:
Bas Delfos 2018-01-25 00:37:07 +01:00
parent 24c81375a4
commit fd8e47706d
141 changed files with 248 additions and 263 deletions

122
src/js/eventPage.js Executable file
View file

@ -0,0 +1,122 @@
/*
If an id is also specified and a window with a matching id has been shown before, the remembered bounds of the window will be used instead.
*/
'use strict';
function startApplication() {
var applicationStartTime = new Date().getTime();
chrome.app.window.create('main.html', {
id: 'main-window',
frame: 'chrome',
innerBounds: {
minWidth: 1024,
minHeight: 550
},
icon: 'images/bf_icon_128.png'
}, function (createdWindow) {
createdWindow.onClosed.addListener(function () {
// automatically close the port when application closes
// save connectionId in separate variable before createdWindow.contentWindow is destroyed
var connectionId = createdWindow.contentWindow.serial.connectionId,
valid_connection = createdWindow.contentWindow.CONFIGURATOR.connectionValid,
mincommand = createdWindow.contentWindow.MOTOR_CONFIG.mincommand;
if (connectionId && valid_connection) {
// code below is handmade MSP message (without pretty JS wrapper), it behaves exactly like MSP.send_message
// sending exit command just in case the cli tab was open.
// reset motors to default (mincommand)
var bufferOut = new ArrayBuffer(5),
bufView = new Uint8Array(bufferOut);
bufView[0] = 0x65; // e
bufView[1] = 0x78; // x
bufView[2] = 0x69; // i
bufView[3] = 0x74; // t
bufView[4] = 0x0D; // enter
chrome.serial.send(connectionId, bufferOut, function () { console.log('Send exit') });
setTimeout(function() {
bufferOut = new ArrayBuffer(22);
bufView = new Uint8Array(bufferOut);
var checksum = 0;
bufView[0] = 36; // $
bufView[1] = 77; // M
bufView[2] = 60; // <
bufView[3] = 16; // data length
bufView[4] = 214; // MSP_SET_MOTOR
checksum = bufView[3] ^ bufView[4];
for (var i = 0; i < 16; i += 2) {
bufView[i + 5] = mincommand & 0x00FF;
bufView[i + 6] = mincommand >> 8;
checksum ^= bufView[i + 5];
checksum ^= bufView[i + 6];
}
bufView[5 + 16] = checksum;
chrome.serial.send(connectionId, bufferOut, function (sendInfo) {
chrome.serial.disconnect(connectionId, function (result) {
console.log('SERIAL: Connection closed - ' + result);
});
});
}, 100);
} else if (connectionId) {
chrome.serial.disconnect(connectionId, function (result) {
console.log('SERIAL: Connection closed - ' + result);
});
}
});
});
}
chrome.app.runtime.onLaunched.addListener(startApplication);
chrome.runtime.onInstalled.addListener(function (details) {
if (details.reason == 'update') {
var previousVersionArr = details.previousVersion.split('.'),
currentVersionArr = getManifestVersion().split('.');
// only fire up notification sequence when one of the major version numbers changed
if (currentVersionArr[0] > previousVersionArr[0] || currentVersionArr[1] > previousVersionArr[1]) {
var manifest = chrome.runtime.getManifest();
var options = {
priority: 0,
type: 'basic',
title: manifest.name,
message: chrome.i18n.getMessage('notifications_app_just_updated_to_version', [getManifestVersion(manifest)]),
iconUrl: '/images/icon_128.png',
buttons: [{'title': chrome.i18n.getMessage('notifications_click_here_to_start_app')}]
};
chrome.notifications.create('baseflight_update', options, function (notificationId) {
// empty
});
}
}
});
chrome.notifications.onButtonClicked.addListener(function (notificationId, buttonIndex) {
if (notificationId == 'baseflight_update') {
startApplication();
}
});
function getManifestVersion(manifest) {
if (!manifest) {
manifest = chrome.runtime.getManifest();
}
var version = manifest.version_name;
if (!version) {
version = manifest.version;
}
return version;
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,52 +0,0 @@
/*
* jQuery Flight Indicators plugin
* By Sébastien Matton (seb_matton@hotmail.com)
* Published under GPLv3 License.
*
* https://github.com/sebmatton/jQuery-Flight-Indicators
*/
/* Global block of an indicator*/
div.instrument {
width: 250px;
height: 250px;
position: relative;
display: inline-block;
overflow: hidden;
}
/* The box containing any element of an indicator */
div.instrument .box {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
/* Default transformations */
div.instrument.attitude div.roll {
transform: rotate(0deg);
}
div.instrument.attitude div.roll div.pitch {
top: 0%;
}
div.instrument.heading div.yaw {
transform: rotate(0deg);
}
div.instrument.vario div.vario {
transform: rotate(0deg);
}
div.instrument.speed div.airspeed {
transform: rotate(90deg);
}
div.instrument.altimeter div.pressure {
transform: rotate(40deg);
}
div.instrument.altimeter div.needle {
transform: rotate(90deg);
}
div.instrument.altimeter div.needleSmall {
transform: rotate(90deg);
}

View file

@ -1,31 +0,0 @@
/*!
* 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

@ -1,571 +0,0 @@
/* Global */
.jBox-wrapper {
text-align: left;
-webkit-box-sizing: border-box;
box-sizing: border-box;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.jBox-title,
.jBox-content,
.jBox-container {
position: relative;
word-break: break-word;
}
.jBox-container {
background: #fff;
border:1px solid #ffbb00;
font-size:11px;
line-height:13px;
color:#525352;
}
.jBox-content {
padding: 4px 5px;
overflow: auto;
-webkit-transition: opacity .15s;
transition: opacity .15s;
}
/* jBox Tooltip */
.jBox-Tooltip .jBox-container,
.jBox-Mouse .jBox-container {
border-radius: 3px;
box-shadow: 0 0 5px rgba(0, 0, 0, .3);
}
.jBox-Tooltip .jBox-title,
.jBox-Mouse .jBox-title {
padding: 8px 10px 0;
font-weight: bold;
}
.jBox-hasTitle.jBox-Tooltip .jBox-content,
.jBox-hasTitle.jBox-Mouse .jBox-content {
padding-top: 5px;
}
/* Pointer */
.jBox-pointer {
position: absolute;
overflow: hidden;
}
.jBox-pointer-top { top: 0; }
.jBox-pointer-bottom { bottom: 0; }
.jBox-pointer-left { left: 0; }
.jBox-pointer-right { right: 0; }
.jBox-pointer-top,
.jBox-pointer-bottom {
width: 22px;
height: 10px;
}
.jBox-pointer-left,
.jBox-pointer-right {
width: 10px;
height: 20px;
}
.jBox-pointer:after {
content: '';
width: 10px;
height: 9px;
position: absolute;
background: #fff;
-webkit-transform: rotate(45deg);
transform: rotate(45deg);
border:1px solid #ffbb00;
}
.jBox-pointer-top:after {
left: 5px;
top: 6px;
box-shadow: -1px -1px 4px rgba(0, 0, 0, .2);
}
.jBox-pointer-right:after {
top: 5px;
right: 6px;
box-shadow: 1px -1px 4px rgba(0, 0, 0, .2);
}
.jBox-pointer-bottom:after {
left: 5px;
bottom: 6px;
box-shadow: 1px 1px 4px rgba(0, 0, 0, .2);
}
.jBox-pointer-left:after {
top: 5px;
left: 6px;
box-shadow: -1px 1px 4px rgba(0, 0, 0, .2);
}
/* jBox Modal & jBox Confirm */
.jBox-Modal .jBox-container,
.jBox-Confirm .jBox-container {
border-radius: 3px;
box-shadow: 0 3px 15px rgba(0, 0, 0, .4), 0 0 5px rgba(0, 0, 0, .4);
}
.jBox-Modal .jBox-title,
.jBox-Confirm .jBox-title {
border-radius: 3px 3px 0 0;
padding: 10px 15px;
background: #f4f5f6;
border-bottom: 1px solid #ddd;
text-shadow: 0 1px 1px #fff;
}
.jBox-Modal.jBox-closeButton-title .jBox-title,
.jBox-Confirm.jBox-closeButton-title .jBox-title {
padding-right: 55px;
}
.jBox-Modal.jBox-closeButton-box:before,
.jBox-Confirm.jBox-closeButton-box:before {
box-shadow: 0 3px 15px rgba(0, 0, 0, .4), 0 0 5px rgba(0, 0, 0, .4);
}
/* jBox Modal */
.jBox-Modal .jBox-content {
padding: 12px 15px;
}
/* jBox Confirm */
.jBox-Confirm .jBox-content {
text-align: center;
padding: 45px 35px;
}
.jBox-Confirm-footer {
border-top: 1px solid #e2e2e2;
background: #fafafa;
border-radius: 0 0 3px 3px;
text-align: center;
padding: 10px 0;
}
.jBox-Confirm-button {
display: inline-block;
cursor: pointer;
font-size: 15px;
line-height: 30px;
height: 30px;
border-radius: 3px;
padding: 0 20px;
-webkit-transition: color .2s, background-color .2s;
transition: color .2s, background-color .2s;
}
.jBox-Confirm-button-cancel {
text-shadow: 0 1px 1px rgba(255, 255, 255, .6);
background: #ddd;
color: #999;
margin-right: 25px;
}
.jBox-Confirm-button-cancel:hover {
background: #ccc;
color: #666;
}
.jBox-Confirm-button-submit {
text-shadow: 0 -1px 1px rgba(0, 0, 0, .2);
background: #5fc04c;
color: #fff;
}
.jBox-Confirm-button-submit:hover {
background: #53a642;
}
.jBox-Confirm-button-cancel:active,
.jBox-Confirm-button-submit:active {
box-shadow: inset 0 1px 3px rgba(0, 0, 0, .26);
}
/* jBox Notice */
.jBox-Notice {
-webkit-transition: margin .2s;
transition: margin .2s;
}
.jBox-Notice .jBox-container {
border-radius: 3px;
box-shadow: 0 0 3px rgba(0, 0, 0, .2);
color: #fff;
text-shadow: 0 -1px 1px #000;
background: #333;
background-image: linear-gradient(to bottom, #444, #222);
}
.jBox-Notice .jBox-content {
border-radius: 3px;
padding: 12px 20px;
}
.jBox-Notice .jBox-title {
padding: 8px 20px 0;
font-weight: bold;
}
.jBox-hasTitle.jBox-Notice .jBox-content {
padding-top: 5px;
}
.jBox-Notice-color .jBox-container {
text-shadow: 0 -1px 1px rgba(0, 0, 0, .3);
}
.jBox-Notice-gray .jBox-container {
color: #666;
text-shadow: 0 1px 1px #fff;
background: #f4f4f4;
background-image: linear-gradient(to bottom, #fafafa, #f0f0f0);
}
.jBox-Notice-red .jBox-container {
background: #b02222;
background-image: linear-gradient(to bottom, #ee2222, #b02222);
}
.jBox-Notice-green .jBox-container {
background: #70a800;
background-image: linear-gradient(to bottom, #95cc2a, #70a800);
}
.jBox-Notice-blue .jBox-container {
background: #2b91d9;
background-image: linear-gradient(to bottom, #5abaff, #2b91d9);
}
.jBox-Notice-yellow .jBox-container {
color: #744700;
text-shadow: 0 1px 1px rgba(255, 255, 255, .6);
background: #ffb11f;
background-image: linear-gradient(to bottom, #ffd665, #ffb11f);
}
/* jBox Image */
.jBox-Image {
background: #fff;
padding: 8px 8px 45px;
border-radius: 5px;
}
.jBox-Image .jBox-content {
padding: 0;
width: 100%;
height: 100%;
}
.jBox-image-container {
border-radius: 5px;
background: #fff center center no-repeat;
position: absolute;
width: 100%;
height: 100%;
opacity: 0;
}
.jBox-image-label {
box-sizing: border-box;
position: absolute;
background: #fff;
top: 100%;
left: 0;
width: 100%;
color: #333;
margin-top: -35px;
padding: 0 90px 5px 10px;
border-radius: 0 0 5px 5px;
-webkit-transition: opacity .3s;
transition: opacity .3s;
opacity: 0;
}
.jBox-image-label.active {
opacity: 1;
}
.jBox-image-pointer-next,
.jBox-image-pointer-prev {
position: absolute;
bottom: 0px;
width: 22px;
height: 45px;
background: no-repeat center center url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9Ijc0LjcgMjI0IDE4LjcgMzIiPg0KPHBhdGggZmlsbD0iIzAwMDAwMCIgZD0iTTkzLDIyNy40TDgwLjQsMjQwTDkzLDI1Mi42YzAuNCwwLjQsMC40LDEuMSwwLDEuNWwtMS42LDEuNmMtMC40LDAuNC0xLDAuNS0xLjUsMEw3NSwyNDAuN2MtMC40LTAuNC0wLjUtMSwwLTEuNWwxNC45LTE0LjljMC40LTAuNCwxLTAuNCwxLjUsMGwxLjYsMS42QzkzLjUsMjI2LjQsOTMuNCwyMjcsOTMsMjI3LjR6Ii8+DQo8L3N2Zz4=);
background-size: 11px auto;
cursor: pointer;
opacity: .6;
-webkit-touch-callout: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-transition: opacity .2s;
transition: opacity .2s;
}
.jBox-image-pointer-next:hover,
.jBox-image-pointer-prev:hover {
opacity: 1;
}
.jBox-image-pointer-next {
right: 8px;
-webkit-transform: scaleX(-1);
transform: scaleX(-1);
}
.jBox-image-pointer-prev {
right: 30px;
}
.jBox-image-open #jBox-overlay {
background-color: rgba(0, 0, 0, .86);
}
.jBox-Image.jBox-loading .jBox-container:before {
left: auto;
top: auto;
bottom: -33px;
right: 55px;
margin-top: -9px;
margin-left: -9px;
}
/* Close button */
.jBox-closeButton {
cursor: pointer;
position: absolute;
}
.jBox-closeButton svg {
position: absolute;
top: 50%;
right: 50%;
}
.jBox-closeButton path {
-webkit-transition: fill .2s;
transition: fill .2s;
}
.jBox-closeButton path {
fill: #aaa;
}
.jBox-closeButton:hover path {
fill: #888;
}
.jBox-closeButton:active path {
fill: #666;
}
/* Close button in overlay */
#jBox-overlay .jBox-closeButton {
top: 0;
right: 0;
width: 40px;
height: 40px;
}
#jBox-overlay .jBox-closeButton svg {
width: 20px;
height: 20px;
margin-top: -10px;
margin-right: -10px;
}
#jBox-overlay .jBox-closeButton path {
fill: #d2d4d6;
}
#jBox-overlay .jBox-closeButton:hover path {
fill: #fff;
}
#jBox-overlay .jBox-closeButton:active path {
fill: #b2b4b6;
}
/* Close button in title */
.jBox-closeButton-title .jBox-closeButton {
top: 0;
right: 0;
bottom: 0;
width: 40px;
}
.jBox-closeButton-title .jBox-closeButton svg {
width: 12px;
height: 12px;
margin-top: -6px;
margin-right: -6px;
}
/* Close button in box */
.jBox-closeButton-box .jBox-closeButton {
top: -8px;
right: -10px;
width: 24px;
height: 24px;
background: #fff;
border-radius: 50%;
}
.jBox-closeButton-box .jBox-closeButton svg {
width: 10px;
height: 10px;
margin-top: -5px;
margin-right: -5px;
}
.jBox-hasTitle.jBox-Modal.jBox-closeButton-box .jBox-closeButton {
background: #f4f5f6;
}
.jBox-closeButton-box:before {
content: '';
position: absolute;
top: -8px;
right: -10px;
width: 24px;
height: 24px;
border-radius: 50%;
box-shadow: 0 0 5px rgba(0, 0, 0, .3);
}
.jBox-pointerPosition-top.jBox-closeButton-box:before {
top: 4px;
}
.jBox-pointerPosition-right.jBox-closeButton-box:before {
right: 2px;
}
/* Overlay */
#jBox-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: #000;
background-color: rgba(0, 0, 0, .6);
}
/* Block scrolling */
body[class^="jBox-blockScroll-"],
body[class*=" jBox-blockScroll-"] {
overflow: hidden;
}
/* Draggable */
.jBox-draggable {
cursor: move;
}
/* Spinner */
@keyframes jBoxLoading {
to {transform: rotate(360deg);}
}
@-webkit-keyframes jBoxLoading {
to {-webkit-transform: rotate(360deg);}
}
.jBox-loading .jBox-content {
min-height: 32px;
min-width: 38px;
opacity: 0;
}
.jBox-spinner {
position: absolute;
top: 50%;
left: 50%;
width: 20px;
height: 20px;
margin-top: -10px;
margin-left: -10px;
}
.jBox-spinner:before {
content: 'Loading…';
display: block;
width: 20px;
height: 20px;
text-align: center;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
.jBox-spinner:not(:required):before {
content: '';
border-radius: 50%;
border: 2px solid rgba(0, 0, 0, .3);
border-top-color: rgba(0, 0, 0, .6);
animation: jBoxLoading .6s linear infinite;
-webkit-animation: jBoxLoading .6s linear infinite;
}
/* IE8 fixes */
.jBox-IE8.jBox-Tooltip .jBox-container,
.jBox-IE8.jBox-Mouse .jBox-container {
border: 1px solid #aaa;
}
.jBox-IE8 .jBox-pointer:after {
display: none;
}
.jBox-IE8 .jBox-pointer {
border: 0;
background: no-repeat url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAYAAACN1PRVAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAPJJREFUeNq01l0OwyAIAGAlvY+n8ZJ6Gk/EqqkNtf7ApCQ+LM34iuCmRUQzihjj6FH+kjWL8N4/Ph9GHpiTnC9SwDbhLGyvspSScc71KkOa/HpuuRhIK+psE2pjONouCQg7kBSEXUgC2tHo52mTTBpnaEATWlaYK6MrhIAaceWpOcsCrYp6FV4H/90zTWjUQ/gSevVQq0ecHqoOxWpYoO7p5O9ku2fnVtp7QAik2rsK3fnpWfjynJWpbw+1BkghurrYDjiCptg/4AxaYhJwBbEwDsiB2NgM5EIirAdKIDFGQSmU1+NaIPjJYt2I25vxT4ABAMhWvtle2YvmAAAAAElFTkSuQmCC);
}
.jBox-IE8 .jBox-pointer-top { background-position: center top; }
.jBox-IE8 .jBox-pointer-bottom { background-position: center bottom; }
.jBox-IE8 .jBox-pointer-left { background-position: left center; }
.jBox-IE8 .jBox-pointer-right { background-position: right center; }
.jBox-IE8.jBox-Modal .jBox-container {
border: 3px solid #aaa;
}
/* No SVG support fixes */
.jBox-nosvg .jBox-closeButton:before {
font-family: Verdana, sans-serif;
content: 'x';
text-align: center;
font-size: 18px;
color: #888;
}

File diff suppressed because one or more lines are too long

View file

@ -1,46 +0,0 @@
/* Wrapper */
.jBox-ModalBorder {
border-radius: 8px;
background: rgba(0, 0, 0, .4);
padding: 8px;
box-shadow: 0 0 6px rgba(0, 0, 0, .2);
}
/* Container */
.jBox-ModalBorder .jBox-container {
border-radius: 5px;
box-shadow: 0 0 5px rgba(0, 0, 0, 0.2);
}
/* Close button */
.jBox-ModalBorder.jBox-closeButton-box {
border-top-right-radius: 0;
}
.jBox-ModalBorder.jBox-closeButton-box:before {
display: none;
}
.jBox-ModalBorder.jBox-hasTitle.jBox-closeButton-box .jBox-closeButton,
.jBox-ModalBorder.jBox-closeButton-box .jBox-closeButton {
background: rgba(0, 0, 0, .4);
border-radius: 0 50% 50% 0;
right: -32px;
top: -8px;
}
.jBox-ModalBorder.jBox-closeButton-box .jBox-closeButton path {
fill: #d2d4d6;
}
.jBox-ModalBorder.jBox-closeButton-box .jBox-closeButton:hover path {
fill: #fff;
}
.jBox-ModalBorder.jBox-closeButton-box .jBox-closeButton:active path {
fill: #b2b4b6;
}

View file

@ -1,45 +0,0 @@
/* jBox: Notice */
.jBox-NoticeBorder .jBox-container {
border-radius: 6px;
}
.jBox-NoticeBorder .jBox-content,
.jBox-NoticeBorder .jBox-title {
padding-left: 26px;
}
.jBox-NoticeBorder.jBox-Notice-color .jBox-container {
color: #fff;
text-shadow: 0 -1px 0 #000;
background: rgba(0, 0, 0, .92);
}
.jBox-NoticeBorder.jBox-Notice-color .jBox-container:after {
content: '';
position: absolute;
top: 0;
left: 0;
bottom: 0;
width: 8px;
border-radius: 5px 0 0 5px;
background-image: linear-gradient(45deg, rgba(255, 255, 255, .5) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .5) 50%, rgba(255, 255, 255, .5) 75%, transparent 75%, transparent);
background-size: 18px 18px;
}
.jBox-NoticeBorder.jBox-Notice-red .jBox-container:after {
background-color: #ee0000;
}
.jBox-NoticeBorder.jBox-Notice-green .jBox-container:after {
background-color: #95cc2a;
}
.jBox-NoticeBorder.jBox-Notice-blue .jBox-container:after {
background-color: #4cb4ff;
}
.jBox-NoticeBorder.jBox-Notice-yellow .jBox-container:after {
background-color: #ffba00;
}

View file

@ -1,33 +0,0 @@
/* Container */
.jBox-TooltipBorder .jBox-container {
border-radius: 5px;
border: 2px solid #52a2cb;
}
/* Pointer */
.jBox-TooltipBorder .jBox-pointer:after {
border: 2px solid #52a2cb;
}
.jBox-TooltipBorder .jBox-pointer-top,
.jBox-TooltipBorder .jBox-pointer-bottom {
width: 34px;
height: 12px;
}
.jBox-TooltipBorder .jBox-pointer-left,
.jBox-TooltipBorder .jBox-pointer-right {
width: 12px;
height: 34px;
}
/* Close button */
.jBox-TooltipBorder.jBox-closeButton-box:before {
width: 28px;
height: 28px;
background: #52a2cb;
}

View file

@ -1,37 +0,0 @@
/* Container */
.jBox-TooltipDark .jBox-container {
border-radius: 3px;
background: #222;
color: #fff;
box-shadow: 0 0 6px rgba(0, 0, 0, .4);
}
/* Pointer */
.jBox-TooltipDark .jBox-pointer:after {
background: #222;
}
/* Close button */
.jBox-TooltipDark .jBox-closeButton {
background: #222;
}
.jBox-TooltipDark.jBox-closeButton-box:before {
box-shadow: 0 0 6px rgba(0, 0, 0, .4);
}
.jBox-TooltipDark.jBox-closeButton-box .jBox-closeButton path {
fill: #d2d4d6;
}
.jBox-TooltipDark.jBox-closeButton-box .jBox-closeButton:hover path {
fill: #fff;
}
.jBox-TooltipDark.jBox-closeButton-box .jBox-closeButton:active path {
fill: #b2b4b6;
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,9 +0,0 @@
/*
* 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

@ -1,174 +0,0 @@
/*
* jQuery Flight Indicators plugin
* By Sébastien Matton (seb_matton@hotmail.com)
* Published under GPLv3 License.
*
* https://github.com/sebmatton/jQuery-Flight-Indicators
*/
(function($) {
function FlightIndicator( placeholder, type, options ) {
// Initial configuration
var attitude = this;
var settings = $.extend({
size : 200,
roll : 0,
pitch : 0,
turn : 0,
heading: 0,
vario: 0,
airspeed: 0,
altitude: 0,
pressure: 1000,
showBox : true,
img_directory : 'img/'
}, options );
var constants = {
pitch_bound:30,
vario_bound : 1.95,
airspeed_bound_l : 0,
airspeed_bound_h : 160
}
// Creation of the instrument
placeholder.each(function(){
switch(type){
case 'heading':
$(this).html('<div class="instrument heading"><img src="' + settings.img_directory + 'fi_box.svg" class="background box" alt="" /><div class="heading box"><img src="' + settings.img_directory + 'heading_yaw.svg" class="box" alt="" /></div><div class="mechanics box"><img src="' + settings.img_directory + 'heading_mechanics.svg" class="box" alt="" /><img src="' + settings.img_directory + 'fi_circle.svg" class="box" alt="" /></div></div>');
_setHeading(settings.heading);
break;
case 'variometer':
$(this).html('<div class="instrument vario"><img src="' + settings.img_directory + 'fi_box.svg" class="background box" alt="" /><img src="' + settings.img_directory + 'vertical_mechanics.svg" class="box" alt="" /><div class="vario box"><img src="' + settings.img_directory + 'fi_needle.svg" class="box" alt="" /></div><div class="mechanics box"><img src="' + settings.img_directory + 'fi_circle.svg" class="box" alt="" /></div></div>');
_setVario(settings.vario);
break;
case 'turn_coordinator':
$(this).html('<div class="instrument turn_coordinator"><img src="' + settings.img_directory + 'fi_box.svg" class="background box" alt="" /><img src="' + settings.img_directory + 'turn_coordinator.svg" class="box" alt="" /><div class="turn box"><img src="' + settings.img_directory + 'fi_tc_airplane.svg" class="box" alt="" /></div><div class="mechanics box"><img src="' + settings.img_directory + 'fi_circle.svg" class="box" alt="" /></div></div>');
_setTurn(settings.turn);
break;
case 'airspeed':
$(this).html('<div class="instrument airspeed"><img src="' + settings.img_directory + 'fi_box.svg" class="background box" alt="" /><img src="' + settings.img_directory + 'speed_mechanics.svg" class="box" alt="" /><div class="speed box"><img src="' + settings.img_directory + 'fi_needle.svg" class="box" alt="" /></div><div class="mechanics box"><img src="' + settings.img_directory + 'fi_circle.svg" class="box" alt="" /></div></div>');
_setAirSpeed(settings.airspeed);
break
case 'altimeter':
$(this).html('<div class="instrument altimeter"><img src="' + settings.img_directory + 'fi_box.svg" class="background box" alt="" /><div class="pressure box"><img src="' + settings.img_directory + 'altitude_pressure.svg" class="box" alt="" /></div><img src="' + settings.img_directory + 'altitude_ticks.svg" class="box" alt="" /><div class="needleSmall box"><img src="' + settings.img_directory + 'fi_needle_small.svg" class="box" alt="" /></div><div class="needle box"><img src="' + settings.img_directory + 'fi_needle.svg" class="box" alt="" /></div><div class="mechanics box"><img src="' + settings.img_directory + 'fi_circle.svg" class="box" alt="" /></div></div>');
_setAltitude(settings.altitude);
_setPressure(settings.pressure);
break;
default:
$(this).html('<div class="instrument attitude"><img src="' + settings.img_directory + 'fi_box.svg" class="background box" alt="" /><div class="roll box"><img src="' + settings.img_directory + 'horizon_back.svg" class="box" alt="" /><div class="pitch box"><img src="' + settings.img_directory + 'horizon_ball.svg" class="box" alt="" /></div><img src="' + settings.img_directory + 'horizon_circle.svg" class="box" alt="" /></div><div class="mechanics box"><img src="' + settings.img_directory + 'horizon_mechanics.svg" class="box" alt="" /><img src="' + settings.img_directory + 'fi_circle.svg" class="box" alt="" /></div></div>');
_setRoll(settings.roll);
_setPitch(settings.pitch);
}
$(this).find('div.instrument').css({height : settings.size, width : settings.size});
$(this).find('div.instrument img.box.background').toggle(settings.showBox);
});
// Private methods
function _setRoll(roll){
roll *= -1;
placeholder.each(function(){
$(this).find('div.instrument.attitude div.roll').css('transform', 'rotate('+roll+'deg)');
});
}
function _setPitch(pitch){
// alert(pitch);
if(pitch>constants.pitch_bound){pitch = constants.pitch_bound;}
else if(pitch<-constants.pitch_bound){pitch = -constants.pitch_bound;}
placeholder.each(function(){
$(this).find('div.instrument.attitude div.roll div.pitch').css('top', -pitch*0.7 + '%');
});
}
function _setHeading(heading){
placeholder.each(function(){
$(this).find('div.instrument.heading div.heading').css('transform', 'rotate(' + -heading + 'deg)');
});
}
function _setTurn(turn){
placeholder.each(function(){
$(this).find('div.instrument.turn_coordinator div.turn').css('transform', 'rotate('+turn+'deg)');
});
}
function _setVario(vario){
if(vario > constants.vario_bound){vario = constants.vario_bound;}
else if(vario < -constants.vario_bound){vario = -constants.vario_bound;}
vario = vario*90;
placeholder.each(function(){
$(this).find('div.instrument.vario div.vario').css('transform', 'rotate(' + vario + 'deg)');
});
}
function _setAirSpeed(speed){
if(speed > constants.airspeed_bound_h){speed = constants.airspeed_bound_h;}
else if(speed < constants.airspeed_bound_l){speed = constants.airspeed_bound_l;}
speed = 90+speed*2;
placeholder.each(function(){
$(this).find('div.instrument.airspeed div.speed').css('transform', 'rotate(' + speed + 'deg)');
});
}
function _setAltitude(altitude){
var needle = 90 + altitude%1000 * 360 / 1000;
var needleSmall = altitude / 10000 * 360;
placeholder.each(function(){
$(this).find('div.instrument.altimeter div.needle').css('transform', 'rotate(' + needle + 'deg)');
$(this).find('div.instrument.altimeter div.needleSmall').css('transform', 'rotate(' + needleSmall + 'deg)');
});
}
function _setPressure(pressure){
pressure = 2*pressure - 1980;
placeholder.each(function(){
$(this).find('div.instrument.altimeter div.pressure').css('transform', 'rotate(' + pressure + 'deg)');
});
}
function _resize(size){
placeholder.each(function(){
$(this).find('div.instrument').css({height : size, width : size});
});
}
function _showBox(){
placeholder.each(function(){
$(this).find('img.box.background').show();
});
}
function _hideBox(){
placeholder.each(function(){
$(this).find('img.box.background').hide();
});
}
// Public methods
this.setRoll = function(roll){_setRoll(roll);}
this.setPitch = function(pitch){_setPitch(pitch);}
this.setHeading = function(heading){_setHeading(heading);}
this.setTurn = function(turn){_setTurn(turn);}
this.setVario = function(vario){_setVario(vario);}
this.setAirSpeed = function(speed){_setAirSpeed(speed);}
this.setAltitude = function(altitude){_setAltitude(altitude);}
this.setPressure = function(pressure){_setPressure(pressure);}
this.resize = function(size){_resize(size);}
this.showBox = function(){_showBox();}
this.hideBox = function(){_hideBox();}
return attitude;
};
// Extension to jQuery
$.flightIndicator = function(placeholder, type, options){
var flightIndicator = new FlightIndicator($(placeholder), type, options)
return flightIndicator;
}
$.fn.flightIndicator = function(data, type, options){
return this.each(function(){
$.flightIndicator(this, type, options);
});
}
}( jQuery ));

View file

@ -1,311 +0,0 @@
/*jslint browser: true */
/*jslint white: true */
(function( $ ){
'use strict';
// Helpers
// Test in an object is an instance of jQuery or Zepto.
function isInstance ( a ) {
return a instanceof $ || ( $.zepto && $.zepto.isZ(a) );
}
// Link types
function fromPrefix ( target, method ) {
// If target is a string, a new hidden input will be created.
if ( typeof target === 'string' && target.indexOf('-inline-') === 0 ) {
// By default, use the 'html' method.
this.method = method || 'html';
// Use jQuery to create the element
this.target = this.el = $( target.replace('-inline-', '') || '<div/>' );
return true;
}
}
function fromString ( target ) {
// If the string doesn't begin with '-', which is reserved, add a new hidden input.
if ( typeof target === 'string' && target.indexOf('-') !== 0 ) {
this.method = 'val';
var element = document.createElement('input');
element.name = target;
element.type = 'hidden';
this.target = this.el = $(element);
return true;
}
}
function fromFunction ( target ) {
// The target can also be a function, which will be called.
if ( typeof target === 'function' ) {
this.target = false;
this.method = target;
return true;
}
}
function fromInstance ( target, method ) {
if ( isInstance( target ) && !method ) {
// If a jQuery/Zepto input element is provided, but no method is set,
// the element can assume it needs to respond to 'change'...
if ( target.is('input, select, textarea') ) {
// Default to .val if this is an input element.
this.method = 'val';
// Fire the API changehandler when the target changes.
this.target = target.on('change.liblink', this.changeHandler);
} else {
this.target = target;
// If no method is set, and we are not auto-binding an input, default to 'html'.
this.method = 'html';
}
return true;
}
}
function fromInstanceMethod ( target, method ) {
// The method must exist on the element.
if ( isInstance( target ) &&
(typeof method === 'function' ||
(typeof method === 'string' && target[method]))
) {
this.method = method;
this.target = target;
return true;
}
}
var
/** @const */
creationFunctions = [fromPrefix, fromString, fromFunction, fromInstance, fromInstanceMethod];
// Link Instance
/** @constructor */
function Link ( target, method, format ) {
var that = this, valid = false;
// Forward calls within scope.
this.changeHandler = function ( changeEvent ) {
var decodedValue = that.formatInstance.from( $(this).val() );
// If the value is invalid, stop this event, as well as it's propagation.
if ( decodedValue === false || isNaN(decodedValue) ) {
// Reset the value.
$(this).val(that.lastSetValue);
return false;
}
that.changeHandlerMethod.call( '', changeEvent, decodedValue );
};
// See if this Link needs individual targets based on its usage.
// If so, return the element that needs to be copied by the
// implementing interface.
// Default the element to false.
this.el = false;
// Store the formatter, or use the default.
this.formatInstance = format;
// Try all Link types.
/*jslint unparam: true*/
$.each(creationFunctions, function(i, fn){
valid = fn.call(that, target, method);
return !valid;
});
/*jslint unparam: false*/
// Nothing matched, throw error.
if ( !valid ) {
throw new RangeError("(Link) Invalid Link.");
}
}
// Provides external items with the object value.
Link.prototype.set = function ( value ) {
// Ignore the value, so only the passed-on arguments remain.
var args = Array.prototype.slice.call( arguments ),
additionalArgs = args.slice(1);
// Store some values. The actual, numerical value,
// the formatted value and the parameters for use in 'resetValue'.
// Slice additionalArgs to break the relation.
this.lastSetValue = this.formatInstance.to( value );
// Prepend the value to the function arguments.
additionalArgs.unshift(
this.lastSetValue
);
// When target is undefined, the target was a function.
// In that case, provided the object as the calling scope.
// Branch between writing to a function or an object.
( typeof this.method === 'function' ?
this.method :
this.target[ this.method ] ).apply( this.target, additionalArgs );
};
// Developer API
/** @constructor */
function LinkAPI ( origin ) {
this.items = [];
this.elements = [];
this.origin = origin;
}
LinkAPI.prototype.push = function( item, element ) {
this.items.push(item);
// Prevent 'false' elements
if ( element ) {
this.elements.push(element);
}
};
LinkAPI.prototype.reconfirm = function ( flag ) {
var i;
for ( i = 0; i < this.elements.length; i += 1 ) {
this.origin.LinkConfirm(flag, this.elements[i]);
}
};
LinkAPI.prototype.remove = function ( flag ) {
var i;
for ( i = 0; i < this.items.length; i += 1 ) {
this.items[i].target.off('.liblink');
}
for ( i = 0; i < this.elements.length; i += 1 ) {
this.elements[i].remove();
}
};
LinkAPI.prototype.change = function ( value ) {
if ( this.origin.LinkIsEmitting ) {
return false;
}
this.origin.LinkIsEmitting = true;
var args = Array.prototype.slice.call( arguments, 1 ), i;
args.unshift( value );
// Write values to serialization Links.
// Convert the value to the correct relative representation.
for ( i = 0; i < this.items.length; i += 1 ) {
this.items[i].set.apply(this.items[i], args);
}
this.origin.LinkIsEmitting = false;
};
// jQuery plugin
function binder ( flag, target, method, format ){
if ( flag === 0 ) {
flag = this.LinkDefaultFlag;
}
// Create a list of API's (if it didn't exist yet);
if ( !this.linkAPI ) {
this.linkAPI = {};
}
// Add an API point.
if ( !this.linkAPI[flag] ) {
this.linkAPI[flag] = new LinkAPI(this);
}
var linkInstance = new Link ( target, method, format || this.LinkDefaultFormatter );
// Default the calling scope to the linked object.
if ( !linkInstance.target ) {
linkInstance.target = $(this);
}
// If the Link requires creation of a new element,
// Pass the element and request confirmation to get the changehandler.
// Set the method to be called when a Link changes.
linkInstance.changeHandlerMethod = this.LinkConfirm( flag, linkInstance.el );
// Store the linkInstance in the flagged list.
this.linkAPI[flag].push( linkInstance, linkInstance.el );
// Now that Link have been connected, request an update.
this.LinkUpdate( flag );
}
/** @export */
$.fn.Link = function( flag ){
var that = this;
// Delete all linkAPI
if ( flag === false ) {
return that.each(function(){
// .Link(false) can be called on elements without Links.
// When that happens, the objects can't be looped.
if ( !this.linkAPI ) {
return;
}
$.map(this.linkAPI, function(api){
api.remove();
});
delete this.linkAPI;
});
}
if ( flag === undefined ) {
flag = 0;
} else if ( typeof flag !== 'string') {
throw new Error("Flag must be string.");
}
return {
to: function( a, b, c ){
return that.each(function(){
binder.call(this, flag, a, b, c);
});
}
};
};
}( window.jQuery || window.Zepto ));

File diff suppressed because one or more lines are too long

View file

@ -1,4 +0,0 @@
/*! noUiSlider - 7.0.9 - 2014-10-08 16:49:45 */
.noUi-target,.noUi-target *{-webkit-touch-callout:none;-webkit-user-select:none;-ms-touch-action:none;-ms-user-select:none;-moz-user-select:none;-moz-box-sizing:border-box;box-sizing:border-box}.noUi-target{position:relative}.noUi-base{width:100%;height:100%;position:relative}.noUi-origin{position:absolute;right:0;top:0;left:0;bottom:0}.noUi-handle{position:relative;z-index:1}.noUi-stacking .noUi-handle{z-index:10}.noUi-state-tap .noUi-origin{-webkit-transition:left .3s,top .3s;transition:left .3s,top .3s}.noUi-state-drag *{cursor:inherit!important}.noUi-base{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}.noUi-horizontal{height:18px}.noUi-horizontal .noUi-handle{width:34px;height:28px;left:-17px;top:-6px}.noUi-vertical{width:18px}.noUi-vertical .noUi-handle{width:28px;height:34px;left:-6px;top:-17px}.noUi-background{background:#FAFAFA;box-shadow:inset 0 1px 1px #f0f0f0}.noUi-connect{background:#3FB8AF;box-shadow:inset 0 0 3px rgba(51,51,51,.45);-webkit-transition:background 450ms;transition:background 450ms}.noUi-origin{border-radius:2px}.noUi-target{border-radius:4px;border:1px solid #D3D3D3;box-shadow:inset 0 1px 1px #F0F0F0,0 3px 6px -5px #BBB}.noUi-target.noUi-connect{box-shadow:inset 0 0 3px rgba(51,51,51,.45),0 3px 6px -5px #BBB}.noUi-dragable{cursor:w-resize}.noUi-vertical .noUi-dragable{cursor:n-resize}.noUi-handle{border:1px solid #D9D9D9;border-radius:3px;background:#FFF;cursor:default;box-shadow:inset 0 0 1px #FFF,inset 0 1px 7px #EBEBEB,0 3px 6px -3px #BBB}.noUi-active{box-shadow:inset 0 0 1px #FFF,inset 0 1px 7px #DDD,0 3px 6px -3px #BBB}.noUi-handle:after,.noUi-handle:before{content:"";display:block;position:absolute;height:14px;width:1px;background:#E8E7E6;left:14px;top:6px}.noUi-handle:after{left:17px}.noUi-vertical .noUi-handle:after,.noUi-vertical .noUi-handle:before{width:14px;height:1px;left:6px;top:14px}.noUi-vertical .noUi-handle:after{top:17px}[disabled] .noUi-connect,[disabled].noUi-connect{background:#B8B8B8}[disabled] .noUi-handle{cursor:not-allowed}

View file

@ -1,4 +0,0 @@
/*! noUiSlider - 7.0.9 - 2014-10-08 16:49:45 */
.noUi-pips,.noUi-pips *{-moz-box-sizing:border-box;box-sizing:border-box}.noUi-pips{position:absolute;font:400 12px Arial;color:#999}.noUi-value{width:40px;position:absolute;text-align:center}.noUi-value-sub{color:#ccc;font-size:10px}.noUi-marker{position:absolute;background:#CCC}.noUi-marker-large,.noUi-marker-sub{background:#AAA}.noUi-pips-horizontal{padding:10px 0;height:50px;top:100%;left:0;width:100%}.noUi-value-horizontal{margin-left:-20px;padding-top:20px}.noUi-value-horizontal.noUi-value-sub{padding-top:15px}.noUi-marker-horizontal.noUi-marker{margin-left:-1px;width:2px;height:5px}.noUi-marker-horizontal.noUi-marker-sub{height:10px}.noUi-marker-horizontal.noUi-marker-large{height:15px}.noUi-pips-vertical{padding:0 10px;height:100%;top:0;left:100%}.noUi-value-vertical{width:15px;margin-left:20px;margin-top:-5px}.noUi-marker-vertical.noUi-marker{width:5px;height:2px;margin-top:-1px}.noUi-marker-vertical.noUi-marker-sub{width:10px}.noUi-marker-vertical.noUi-marker-large{width:15px}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,98 +0,0 @@
/*
*
* Main stylesheet for Switchery.
* http://abpetkov.github.io/switchery/
*
*/
/* Switchery defaults. */
.switchery {
background-color: #fff;
border: 1px solid #fff;
border-radius: 20px;
cursor: pointer;
display: inline-block;
height: 14px;
position: relative;
vertical-align: middle;
width: 45px;
z-index:1000;
opacity:1 !important;
-moz-user-select: none;
-khtml-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
user-select: none;
box-sizing: content-box;
background-clip: content-box;
}
.switchery > small {
background: #fff;
border-radius: 100%;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
height: 14px;
position: absolute;
top: 0;
width: 14px;
}
.switcherymid {
background-color: #fff;
border: 1px solid #fff;
border-radius: 20px;
cursor: pointer;
display: inline-block;
height: 14px;
position: relative;
vertical-align: middle;
width: 35px;
-moz-user-select: none;
-khtml-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
user-select: none;
box-sizing: content-box;
background-clip: content-box;
}
.switcherymid > small {
background: #fff;
border-radius: 100%;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
height: 14px;
position: absolute;
top: 0;
width: 14px;
}
/* Switchery sizes. */
.switchery-small {
border: none !important;
border-radius: 20px;
height: 10px;
width: 20px;
margin-top:-3px;
margin-right:3px;
}
.switchery-small > small {
height: 10px;
width: 10px;
}
.switchery-large {
border-radius: 40px;
height: 40px;
width: 66px;
}
.switchery-large > small {
height: 40px;
width: 40px;
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,921 +0,0 @@
/**
* @author mrdoob / http://mrdoob.com/
* @author supereggbert / http://www.paulbrunt.co.uk/
* @author julianwa / https://github.com/julianwa
*/
THREE.RenderableObject = function () {
this.id = 0;
this.object = null;
this.z = 0;
this.renderOrder = 0;
};
//
THREE.RenderableFace = function () {
this.id = 0;
this.v1 = new THREE.RenderableVertex();
this.v2 = new THREE.RenderableVertex();
this.v3 = new THREE.RenderableVertex();
this.normalModel = new THREE.Vector3();
this.vertexNormalsModel = [ new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3() ];
this.vertexNormalsLength = 0;
this.color = new THREE.Color();
this.material = null;
this.uvs = [ new THREE.Vector2(), new THREE.Vector2(), new THREE.Vector2() ];
this.z = 0;
this.renderOrder = 0;
};
//
THREE.RenderableVertex = function () {
this.position = new THREE.Vector3();
this.positionWorld = new THREE.Vector3();
this.positionScreen = new THREE.Vector4();
this.visible = true;
};
THREE.RenderableVertex.prototype.copy = function ( vertex ) {
this.positionWorld.copy( vertex.positionWorld );
this.positionScreen.copy( vertex.positionScreen );
};
//
THREE.RenderableLine = function () {
this.id = 0;
this.v1 = new THREE.RenderableVertex();
this.v2 = new THREE.RenderableVertex();
this.vertexColors = [ new THREE.Color(), new THREE.Color() ];
this.material = null;
this.z = 0;
this.renderOrder = 0;
};
//
THREE.RenderableSprite = function () {
this.id = 0;
this.object = null;
this.x = 0;
this.y = 0;
this.z = 0;
this.rotation = 0;
this.scale = new THREE.Vector2();
this.material = null;
this.renderOrder = 0;
};
//
THREE.Projector = function () {
var _object, _objectCount, _objectPool = [], _objectPoolLength = 0,
_vertex, _vertexCount, _vertexPool = [], _vertexPoolLength = 0,
_face, _faceCount, _facePool = [], _facePoolLength = 0,
_line, _lineCount, _linePool = [], _linePoolLength = 0,
_sprite, _spriteCount, _spritePool = [], _spritePoolLength = 0,
_renderData = { objects: [], lights: [], elements: [] },
_vector3 = new THREE.Vector3(),
_vector4 = new THREE.Vector4(),
_clipBox = new THREE.Box3( new THREE.Vector3( - 1, - 1, - 1 ), new THREE.Vector3( 1, 1, 1 ) ),
_boundingBox = new THREE.Box3(),
_points3 = new Array( 3 ),
_points4 = new Array( 4 ),
_viewMatrix = new THREE.Matrix4(),
_viewProjectionMatrix = new THREE.Matrix4(),
_modelMatrix,
_modelViewProjectionMatrix = new THREE.Matrix4(),
_normalMatrix = new THREE.Matrix3(),
_frustum = new THREE.Frustum(),
_clippedVertex1PositionScreen = new THREE.Vector4(),
_clippedVertex2PositionScreen = new THREE.Vector4();
//
this.projectVector = function ( vector, camera ) {
console.warn( 'THREE.Projector: .projectVector() is now vector.project().' );
vector.project( camera );
};
this.unprojectVector = function ( vector, camera ) {
console.warn( 'THREE.Projector: .unprojectVector() is now vector.unproject().' );
vector.unproject( camera );
};
this.pickingRay = function ( vector, camera ) {
console.error( 'THREE.Projector: .pickingRay() is now raycaster.setFromCamera().' );
};
//
var RenderList = function () {
var normals = [];
var uvs = [];
var object = null;
var material = null;
var normalMatrix = new THREE.Matrix3();
var setObject = function ( value ) {
object = value;
material = object.material;
normalMatrix.getNormalMatrix( object.matrixWorld );
normals.length = 0;
uvs.length = 0;
};
var projectVertex = function ( vertex ) {
var position = vertex.position;
var positionWorld = vertex.positionWorld;
var positionScreen = vertex.positionScreen;
positionWorld.copy( position ).applyMatrix4( _modelMatrix );
positionScreen.copy( positionWorld ).applyMatrix4( _viewProjectionMatrix );
var invW = 1 / positionScreen.w;
positionScreen.x *= invW;
positionScreen.y *= invW;
positionScreen.z *= invW;
vertex.visible = positionScreen.x >= - 1 && positionScreen.x <= 1 &&
positionScreen.y >= - 1 && positionScreen.y <= 1 &&
positionScreen.z >= - 1 && positionScreen.z <= 1;
};
var pushVertex = function ( x, y, z ) {
_vertex = getNextVertexInPool();
_vertex.position.set( x, y, z );
projectVertex( _vertex );
};
var pushNormal = function ( x, y, z ) {
normals.push( x, y, z );
};
var pushUv = function ( x, y ) {
uvs.push( x, y );
};
var checkTriangleVisibility = function ( v1, v2, v3 ) {
if ( v1.visible === true || v2.visible === true || v3.visible === true ) return true;
_points3[ 0 ] = v1.positionScreen;
_points3[ 1 ] = v2.positionScreen;
_points3[ 2 ] = v3.positionScreen;
return _clipBox.isIntersectionBox( _boundingBox.setFromPoints( _points3 ) );
};
var checkBackfaceCulling = function ( v1, v2, v3 ) {
return ( ( v3.positionScreen.x - v1.positionScreen.x ) *
( v2.positionScreen.y - v1.positionScreen.y ) -
( v3.positionScreen.y - v1.positionScreen.y ) *
( v2.positionScreen.x - v1.positionScreen.x ) ) < 0;
};
var pushLine = function ( a, b ) {
var v1 = _vertexPool[ a ];
var v2 = _vertexPool[ b ];
_line = getNextLineInPool();
_line.id = object.id;
_line.v1.copy( v1 );
_line.v2.copy( v2 );
_line.z = ( v1.positionScreen.z + v2.positionScreen.z ) / 2;
_line.renderOrder = object.renderOrder;
_line.material = object.material;
_renderData.elements.push( _line );
};
var pushTriangle = function ( a, b, c ) {
var v1 = _vertexPool[ a ];
var v2 = _vertexPool[ b ];
var v3 = _vertexPool[ c ];
if ( checkTriangleVisibility( v1, v2, v3 ) === false ) return;
if ( material.side === THREE.DoubleSide || checkBackfaceCulling( v1, v2, v3 ) === true ) {
_face = getNextFaceInPool();
_face.id = object.id;
_face.v1.copy( v1 );
_face.v2.copy( v2 );
_face.v3.copy( v3 );
_face.z = ( v1.positionScreen.z + v2.positionScreen.z + v3.positionScreen.z ) / 3;
_face.renderOrder = object.renderOrder;
// use first vertex normal as face normal
_face.normalModel.fromArray( normals, a * 3 );
_face.normalModel.applyMatrix3( normalMatrix ).normalize();
for ( var i = 0; i < 3; i ++ ) {
var normal = _face.vertexNormalsModel[ i ];
normal.fromArray( normals, arguments[ i ] * 3 );
normal.applyMatrix3( normalMatrix ).normalize();
var uv = _face.uvs[ i ];
uv.fromArray( uvs, arguments[ i ] * 2 );
}
_face.vertexNormalsLength = 3;
_face.material = object.material;
_renderData.elements.push( _face );
}
};
return {
setObject: setObject,
projectVertex: projectVertex,
checkTriangleVisibility: checkTriangleVisibility,
checkBackfaceCulling: checkBackfaceCulling,
pushVertex: pushVertex,
pushNormal: pushNormal,
pushUv: pushUv,
pushLine: pushLine,
pushTriangle: pushTriangle
}
};
var renderList = new RenderList();
this.projectScene = function ( scene, camera, sortObjects, sortElements ) {
_faceCount = 0;
_lineCount = 0;
_spriteCount = 0;
_renderData.elements.length = 0;
if ( scene.autoUpdate === true ) scene.updateMatrixWorld();
if ( camera.parent === null ) camera.updateMatrixWorld();
_viewMatrix.copy( camera.matrixWorldInverse.getInverse( camera.matrixWorld ) );
_viewProjectionMatrix.multiplyMatrices( camera.projectionMatrix, _viewMatrix );
_frustum.setFromMatrix( _viewProjectionMatrix );
//
_objectCount = 0;
_renderData.objects.length = 0;
_renderData.lights.length = 0;
scene.traverseVisible( function ( object ) {
if ( object instanceof THREE.Light ) {
_renderData.lights.push( object );
} else if ( object instanceof THREE.Mesh || object instanceof THREE.Line || object instanceof THREE.Sprite ) {
var material = object.material;
if ( material.visible === false ) return;
if ( object.frustumCulled === false || _frustum.intersectsObject( object ) === true ) {
_object = getNextObjectInPool();
_object.id = object.id;
_object.object = object;
_vector3.setFromMatrixPosition( object.matrixWorld );
_vector3.applyProjection( _viewProjectionMatrix );
_object.z = _vector3.z;
_object.renderOrder = object.renderOrder;
_renderData.objects.push( _object );
}
}
} );
if ( sortObjects === true ) {
_renderData.objects.sort( painterSort );
}
//
for ( var o = 0, ol = _renderData.objects.length; o < ol; o ++ ) {
var object = _renderData.objects[ o ].object;
var geometry = object.geometry;
renderList.setObject( object );
_modelMatrix = object.matrixWorld;
_vertexCount = 0;
if ( object instanceof THREE.Mesh ) {
if ( geometry instanceof THREE.BufferGeometry ) {
var attributes = geometry.attributes;
var groups = geometry.groups;
if ( attributes.position === undefined ) continue;
var positions = attributes.position.array;
for ( var i = 0, l = positions.length; i < l; i += 3 ) {
renderList.pushVertex( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] );
}
if ( attributes.normal !== undefined ) {
var normals = attributes.normal.array;
for ( var i = 0, l = normals.length; i < l; i += 3 ) {
renderList.pushNormal( normals[ i ], normals[ i + 1 ], normals[ i + 2 ] );
}
}
if ( attributes.uv !== undefined ) {
var uvs = attributes.uv.array;
for ( var i = 0, l = uvs.length; i < l; i += 2 ) {
renderList.pushUv( uvs[ i ], uvs[ i + 1 ] );
}
}
if ( geometry.index !== null ) {
var indices = geometry.index.array;
if ( groups.length > 0 ) {
for ( var o = 0; o < groups.length; o ++ ) {
var group = groups[ o ];
for ( var i = group.start, l = group.start + group.count; i < l; i += 3 ) {
renderList.pushTriangle( indices[ i ], indices[ i + 1 ], indices[ i + 2 ] );
}
}
} else {
for ( var i = 0, l = indices.length; i < l; i += 3 ) {
renderList.pushTriangle( indices[ i ], indices[ i + 1 ], indices[ i + 2 ] );
}
}
} else {
for ( var i = 0, l = positions.length / 3; i < l; i += 3 ) {
renderList.pushTriangle( i, i + 1, i + 2 );
}
}
} else if ( geometry instanceof THREE.Geometry ) {
var vertices = geometry.vertices;
var faces = geometry.faces;
var faceVertexUvs = geometry.faceVertexUvs[ 0 ];
_normalMatrix.getNormalMatrix( _modelMatrix );
var material = object.material;
var isFaceMaterial = material instanceof THREE.MeshFaceMaterial;
var objectMaterials = isFaceMaterial === true ? object.material : null;
for ( var v = 0, vl = vertices.length; v < vl; v ++ ) {
var vertex = vertices[ v ];
_vector3.copy( vertex );
if ( material.morphTargets === true ) {
var morphTargets = geometry.morphTargets;
var morphInfluences = object.morphTargetInfluences;
for ( var t = 0, tl = morphTargets.length; t < tl; t ++ ) {
var influence = morphInfluences[ t ];
if ( influence === 0 ) continue;
var target = morphTargets[ t ];
var targetVertex = target.vertices[ v ];
_vector3.x += ( targetVertex.x - vertex.x ) * influence;
_vector3.y += ( targetVertex.y - vertex.y ) * influence;
_vector3.z += ( targetVertex.z - vertex.z ) * influence;
}
}
renderList.pushVertex( _vector3.x, _vector3.y, _vector3.z );
}
for ( var f = 0, fl = faces.length; f < fl; f ++ ) {
var face = faces[ f ];
material = isFaceMaterial === true
? objectMaterials.materials[ face.materialIndex ]
: object.material;
if ( material === undefined ) continue;
var side = material.side;
var v1 = _vertexPool[ face.a ];
var v2 = _vertexPool[ face.b ];
var v3 = _vertexPool[ face.c ];
if ( renderList.checkTriangleVisibility( v1, v2, v3 ) === false ) continue;
var visible = renderList.checkBackfaceCulling( v1, v2, v3 );
if ( side !== THREE.DoubleSide ) {
if ( side === THREE.FrontSide && visible === false ) continue;
if ( side === THREE.BackSide && visible === true ) continue;
}
_face = getNextFaceInPool();
_face.id = object.id;
_face.v1.copy( v1 );
_face.v2.copy( v2 );
_face.v3.copy( v3 );
_face.normalModel.copy( face.normal );
if ( visible === false && ( side === THREE.BackSide || side === THREE.DoubleSide ) ) {
_face.normalModel.negate();
}
_face.normalModel.applyMatrix3( _normalMatrix ).normalize();
var faceVertexNormals = face.vertexNormals;
for ( var n = 0, nl = Math.min( faceVertexNormals.length, 3 ); n < nl; n ++ ) {
var normalModel = _face.vertexNormalsModel[ n ];
normalModel.copy( faceVertexNormals[ n ] );
if ( visible === false && ( side === THREE.BackSide || side === THREE.DoubleSide ) ) {
normalModel.negate();
}
normalModel.applyMatrix3( _normalMatrix ).normalize();
}
_face.vertexNormalsLength = faceVertexNormals.length;
var vertexUvs = faceVertexUvs[ f ];
if ( vertexUvs !== undefined ) {
for ( var u = 0; u < 3; u ++ ) {
_face.uvs[ u ].copy( vertexUvs[ u ] );
}
}
_face.color = face.color;
_face.material = material;
_face.z = ( v1.positionScreen.z + v2.positionScreen.z + v3.positionScreen.z ) / 3;
_face.renderOrder = object.renderOrder;
_renderData.elements.push( _face );
}
}
} else if ( object instanceof THREE.Line ) {
if ( geometry instanceof THREE.BufferGeometry ) {
var attributes = geometry.attributes;
if ( attributes.position !== undefined ) {
var positions = attributes.position.array;
for ( var i = 0, l = positions.length; i < l; i += 3 ) {
renderList.pushVertex( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] );
}
if ( geometry.index !== null ) {
var indices = geometry.index.array;
for ( var i = 0, l = indices.length; i < l; i += 2 ) {
renderList.pushLine( indices[ i ], indices[ i + 1 ] );
}
} else {
var step = object instanceof THREE.LineSegments ? 2 : 1;
for ( var i = 0, l = ( positions.length / 3 ) - 1; i < l; i += step ) {
renderList.pushLine( i, i + 1 );
}
}
}
} else if ( geometry instanceof THREE.Geometry ) {
_modelViewProjectionMatrix.multiplyMatrices( _viewProjectionMatrix, _modelMatrix );
var vertices = object.geometry.vertices;
if ( vertices.length === 0 ) continue;
v1 = getNextVertexInPool();
v1.positionScreen.copy( vertices[ 0 ] ).applyMatrix4( _modelViewProjectionMatrix );
var step = object instanceof THREE.LineSegments ? 2 : 1;
for ( var v = 1, vl = vertices.length; v < vl; v ++ ) {
v1 = getNextVertexInPool();
v1.positionScreen.copy( vertices[ v ] ).applyMatrix4( _modelViewProjectionMatrix );
if ( ( v + 1 ) % step > 0 ) continue;
v2 = _vertexPool[ _vertexCount - 2 ];
_clippedVertex1PositionScreen.copy( v1.positionScreen );
_clippedVertex2PositionScreen.copy( v2.positionScreen );
if ( clipLine( _clippedVertex1PositionScreen, _clippedVertex2PositionScreen ) === true ) {
// Perform the perspective divide
_clippedVertex1PositionScreen.multiplyScalar( 1 / _clippedVertex1PositionScreen.w );
_clippedVertex2PositionScreen.multiplyScalar( 1 / _clippedVertex2PositionScreen.w );
_line = getNextLineInPool();
_line.id = object.id;
_line.v1.positionScreen.copy( _clippedVertex1PositionScreen );
_line.v2.positionScreen.copy( _clippedVertex2PositionScreen );
_line.z = Math.max( _clippedVertex1PositionScreen.z, _clippedVertex2PositionScreen.z );
_line.renderOrder = object.renderOrder;
_line.material = object.material;
if ( object.material.vertexColors === THREE.VertexColors ) {
_line.vertexColors[ 0 ].copy( object.geometry.colors[ v ] );
_line.vertexColors[ 1 ].copy( object.geometry.colors[ v - 1 ] );
}
_renderData.elements.push( _line );
}
}
}
} else if ( object instanceof THREE.Sprite ) {
_vector4.set( _modelMatrix.elements[ 12 ], _modelMatrix.elements[ 13 ], _modelMatrix.elements[ 14 ], 1 );
_vector4.applyMatrix4( _viewProjectionMatrix );
var invW = 1 / _vector4.w;
_vector4.z *= invW;
if ( _vector4.z >= - 1 && _vector4.z <= 1 ) {
_sprite = getNextSpriteInPool();
_sprite.id = object.id;
_sprite.x = _vector4.x * invW;
_sprite.y = _vector4.y * invW;
_sprite.z = _vector4.z;
_sprite.renderOrder = object.renderOrder;
_sprite.object = object;
_sprite.rotation = object.rotation;
_sprite.scale.x = object.scale.x * Math.abs( _sprite.x - ( _vector4.x + camera.projectionMatrix.elements[ 0 ] ) / ( _vector4.w + camera.projectionMatrix.elements[ 12 ] ) );
_sprite.scale.y = object.scale.y * Math.abs( _sprite.y - ( _vector4.y + camera.projectionMatrix.elements[ 5 ] ) / ( _vector4.w + camera.projectionMatrix.elements[ 13 ] ) );
_sprite.material = object.material;
_renderData.elements.push( _sprite );
}
}
}
if ( sortElements === true ) {
_renderData.elements.sort( painterSort );
}
return _renderData;
};
// Pools
function getNextObjectInPool() {
if ( _objectCount === _objectPoolLength ) {
var object = new THREE.RenderableObject();
_objectPool.push( object );
_objectPoolLength ++;
_objectCount ++;
return object;
}
return _objectPool[ _objectCount ++ ];
}
function getNextVertexInPool() {
if ( _vertexCount === _vertexPoolLength ) {
var vertex = new THREE.RenderableVertex();
_vertexPool.push( vertex );
_vertexPoolLength ++;
_vertexCount ++;
return vertex;
}
return _vertexPool[ _vertexCount ++ ];
}
function getNextFaceInPool() {
if ( _faceCount === _facePoolLength ) {
var face = new THREE.RenderableFace();
_facePool.push( face );
_facePoolLength ++;
_faceCount ++;
return face;
}
return _facePool[ _faceCount ++ ];
}
function getNextLineInPool() {
if ( _lineCount === _linePoolLength ) {
var line = new THREE.RenderableLine();
_linePool.push( line );
_linePoolLength ++;
_lineCount ++;
return line;
}
return _linePool[ _lineCount ++ ];
}
function getNextSpriteInPool() {
if ( _spriteCount === _spritePoolLength ) {
var sprite = new THREE.RenderableSprite();
_spritePool.push( sprite );
_spritePoolLength ++;
_spriteCount ++;
return sprite;
}
return _spritePool[ _spriteCount ++ ];
}
//
function painterSort( a, b ) {
if ( a.renderOrder !== b.renderOrder ) {
return a.renderOrder - b.renderOrder;
} else if ( a.z !== b.z ) {
return b.z - a.z;
} else if ( a.id !== b.id ) {
return a.id - b.id;
} else {
return 0;
}
}
function clipLine( s1, s2 ) {
var alpha1 = 0, alpha2 = 1,
// Calculate the boundary coordinate of each vertex for the near and far clip planes,
// Z = -1 and Z = +1, respectively.
bc1near = s1.z + s1.w,
bc2near = s2.z + s2.w,
bc1far = - s1.z + s1.w,
bc2far = - s2.z + s2.w;
if ( bc1near >= 0 && bc2near >= 0 && bc1far >= 0 && bc2far >= 0 ) {
// Both vertices lie entirely within all clip planes.
return true;
} else if ( ( bc1near < 0 && bc2near < 0 ) || ( bc1far < 0 && bc2far < 0 ) ) {
// Both vertices lie entirely outside one of the clip planes.
return false;
} else {
// The line segment spans at least one clip plane.
if ( bc1near < 0 ) {
// v1 lies outside the near plane, v2 inside
alpha1 = Math.max( alpha1, bc1near / ( bc1near - bc2near ) );
} else if ( bc2near < 0 ) {
// v2 lies outside the near plane, v1 inside
alpha2 = Math.min( alpha2, bc1near / ( bc1near - bc2near ) );
}
if ( bc1far < 0 ) {
// v1 lies outside the far plane, v2 inside
alpha1 = Math.max( alpha1, bc1far / ( bc1far - bc2far ) );
} else if ( bc2far < 0 ) {
// v2 lies outside the far plane, v2 inside
alpha2 = Math.min( alpha2, bc1far / ( bc1far - bc2far ) );
}
if ( alpha2 < alpha1 ) {
// The line segment spans two boundaries, but is outside both of them.
// (This can't happen when we're only clipping against just near/far but good
// to leave the check here for future usage if other clip planes are added.)
return false;
} else {
// Update the s1 and s2 vertices to match the clipped line segment.
s1.lerp( s2, alpha1 );
s2.lerp( s1, 1 - alpha2 );
return true;
}
}
}
};

File diff suppressed because one or more lines are too long

606
src/js/main.js Normal file
View file

@ -0,0 +1,606 @@
'use strict';
// Open new windows in external browser
try {
var gui = require('nw.gui');
//Get the current window
var win = gui.Window.get();
//Listen to the new window event
win.on('new-win-policy', function (frame, url, policy) {
gui.Shell.openExternal(url);
policy.ignore();
});
} catch (ex) {
console.log("require does not exist, maybe inside chrome");
}
$(document).ready(function () {
// translate to user-selected language
localize();
// alternative - window.navigator.appVersion.match(/Chrome\/([0-9.]*)/)[1];
GUI.log(chrome.i18n.getMessage('infoVersions',[GUI.operating_system, window.navigator.appVersion.replace(/.*Chrome\/([0-9.]*).*/, "$1"), getManifestVersion()]));
$('#logo .version').text(getManifestVersion());
updateStatusBarVersion();
updateTopBarVersion();
// notification messages for various operating systems
switch (GUI.operating_system) {
case 'Windows':
break;
case 'MacOS':
// var main_chromium_version = window.navigator.appVersion.replace(/.*Chrome\/([0-9.]*).*/,"$1").split('.')[0];
break;
case 'ChromeOS':
break;
case 'Linux':
break;
case 'UNIX':
break;
}
if (GUI.operating_system !== 'ChromeOS') {
checkForConfiguratorUpdates();
}
chrome.storage.local.get('logopen', function (result) {
if (result.logopen) {
$("#showlog").trigger('click');
}
});
// log webgl capability
// it would seem the webgl "enabling" through advanced settings will be ignored in the future
// and webgl will be supported if gpu supports it by default (canary 40.0.2175.0), keep an eye on this one
var canvas = document.createElement('canvas');
// log library versions in console to make version tracking easier
console.log('Libraries: jQuery - ' + $.fn.jquery + ', d3 - ' + d3.version + ', three.js - ' + THREE.REVISION);
// Tabs
var ui_tabs = $('#tabs > ul');
$('a', ui_tabs).click(function () {
if ($(this).parent().hasClass('active') == false && !GUI.tab_switch_in_progress) { // only initialize when the tab isn't already active
var self = this,
tabClass = $(self).parent().prop('class');
var tabRequiresConnection = $(self).parent().hasClass('mode-connected');
var tab = tabClass.substring(4);
var tabName = $(self).text();
if (tabRequiresConnection && !CONFIGURATOR.connectionValid) {
GUI.log(chrome.i18n.getMessage('tabSwitchConnectionRequired'));
return;
}
if (GUI.connect_lock) { // tab switching disabled while operation is in progress
GUI.log(chrome.i18n.getMessage('tabSwitchWaitForOperation'));
return;
}
if (GUI.allowedTabs.indexOf(tab) < 0) {
GUI.log(chrome.i18n.getMessage('tabSwitchUpgradeRequired', [tabName]));
return;
}
GUI.tab_switch_in_progress = true;
GUI.tab_switch_cleanup(function () {
// disable previously active tab highlight
$('li', ui_tabs).removeClass('active');
// Highlight selected tab
$(self).parent().addClass('active');
// detach listeners and remove element data
var content = $('#content');
content.empty();
// display loading screen
$('#cache .data-loading').clone().appendTo(content);
function content_ready() {
GUI.tab_switch_in_progress = false;
}
switch (tab) {
case 'landing':
TABS.landing.initialize(content_ready);
break;
case 'firmware_flasher':
TABS.firmware_flasher.initialize(content_ready);
break;
case 'help':
TABS.help.initialize(content_ready);
break;
case 'auxiliary':
TABS.auxiliary.initialize(content_ready);
break;
case 'adjustments':
TABS.adjustments.initialize(content_ready);
break;
case 'ports':
TABS.ports.initialize(content_ready);
break;
case 'led_strip':
TABS.led_strip.initialize(content_ready);
break;
case 'failsafe':
TABS.failsafe.initialize(content_ready);
break;
case 'transponder':
TABS.transponder.initialize(content_ready);
break;
case 'osd':
TABS.osd.initialize(content_ready);
break;
case 'power':
TABS.power.initialize(content_ready);
break;
case 'setup':
TABS.setup.initialize(content_ready);
break;
case 'setup_osd':
TABS.setup_osd.initialize(content_ready);
break;
case 'configuration':
TABS.configuration.initialize(content_ready);
break;
case 'pid_tuning':
TABS.pid_tuning.initialize(content_ready);
break;
case 'receiver':
TABS.receiver.initialize(content_ready);
break;
case 'servos':
TABS.servos.initialize(content_ready);
break;
case 'gps':
TABS.gps.initialize(content_ready);
break;
case 'motors':
TABS.motors.initialize(content_ready);
break;
case 'sensors':
TABS.sensors.initialize(content_ready);
break;
case 'logging':
TABS.logging.initialize(content_ready);
break;
case 'onboard_logging':
TABS.onboard_logging.initialize(content_ready);
break;
case 'cli':
TABS.cli.initialize(content_ready);
break;
default:
console.log('Tab not found:' + tab);
}
});
}
});
$('#tabs ul.mode-disconnected li a:first').click();
// options
$('a#options').click(function () {
var el = $(this);
if (!el.hasClass('active')) {
el.addClass('active');
el.after('<div id="options-window"></div>');
$('div#options-window').load('./tabs/options.html', function () {
// translate to user-selected language
localize();
chrome.storage.local.get('permanentExpertMode', function (result) {
if (result.permanentExpertMode) {
$('div.permanentExpertMode input').prop('checked', true);
}
$('div.permanentExpertMode input').change(function () {
var checked = $(this).is(':checked');
chrome.storage.local.set({'permanentExpertMode': checked});
$('input[name="expertModeCheckbox"]').prop('checked', checked).change();
if (FEATURE_CONFIG) {
updateTabList(FEATURE_CONFIG.features);
}
}).change();
});
if (GUI.operating_system !== 'ChromeOS') {
chrome.storage.local.get('checkForConfiguratorUnstableVersions', function (result) {
if (result.checkForConfiguratorUnstableVersions) {
$('div.checkForConfiguratorUnstableVersions input').prop('checked', true);
}
$('div.checkForConfiguratorUnstableVersions input').change(function () {
var checked = $(this).is(':checked');
chrome.storage.local.set({'checkForConfiguratorUnstableVersions': checked});
checkForConfiguratorUpdates();
});
});
} else {
$('div.checkForConfiguratorUnstableVersions').hide();
}
function close_and_cleanup(e) {
if (e.type == 'click' && !$.contains($('div#options-window')[0], e.target) || e.type == 'keyup' && e.keyCode == 27) {
$(document).unbind('click keyup', close_and_cleanup);
$('div#options-window').slideUp(250, function () {
el.removeClass('active');
$(this).empty().remove();
});
}
}
$(document).bind('click keyup', close_and_cleanup);
$(this).slideDown(250);
});
}
});
// listen to all input change events and adjust the value within limits if necessary
$("#content").on('focus', 'input[type="number"]', function () {
var element = $(this),
val = element.val();
if (!isNaN(val)) {
element.data('previousValue', parseFloat(val));
}
});
$("#content").on('keydown', 'input[type="number"]', function (e) {
// whitelist all that we need for numeric control
var whitelist = [
96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, // numpad and standard number keypad
109, 189, // minus on numpad and in standard keyboard
8, 46, 9, // backspace, delete, tab
190, 110, // decimal point
37, 38, 39, 40, 13 // arrows and enter
];
if (whitelist.indexOf(e.keyCode) == -1) {
e.preventDefault();
}
});
$("#content").on('change', 'input[type="number"]', function () {
var element = $(this),
min = parseFloat(element.prop('min')),
max = parseFloat(element.prop('max')),
step = parseFloat(element.prop('step')),
val = parseFloat(element.val()),
decimal_places;
// only adjust minimal end if bound is set
if (element.prop('min')) {
if (val < min) {
element.val(min);
val = min;
}
}
// only adjust maximal end if bound is set
if (element.prop('max')) {
if (val > max) {
element.val(max);
val = max;
}
}
// if entered value is illegal use previous value instead
if (isNaN(val)) {
element.val(element.data('previousValue'));
val = element.data('previousValue');
}
// if step is not set or step is int and value is float use previous value instead
if (isNaN(step) || step % 1 === 0) {
if (val % 1 !== 0) {
element.val(element.data('previousValue'));
val = element.data('previousValue');
}
}
// if step is set and is float and value is int, convert to float, keep decimal places in float according to step *experimental*
if (!isNaN(step) && step % 1 !== 0) {
decimal_places = String(step).split('.')[1].length;
if (val % 1 === 0) {
element.val(val.toFixed(decimal_places));
} else if (String(val).split('.')[1].length != decimal_places) {
element.val(val.toFixed(decimal_places));
}
}
});
$("#showlog").on('click', function () {
var state = $(this).data('state');
if (state) {
$("#log").animate({height: 27}, 200, function () {
var command_log = $('div#log');
command_log.scrollTop($('div.wrapper', command_log).height());
});
$("#log").removeClass('active');
$("#content").removeClass('logopen');
$(".tab_container").removeClass('logopen');
$("#scrollicon").removeClass('active');
chrome.storage.local.set({'logopen': false});
state = false;
} else {
$("#log").animate({height: 111}, 200);
$("#log").addClass('active');
$("#content").addClass('logopen');
$(".tab_container").addClass('logopen');
$("#scrollicon").addClass('active');
chrome.storage.local.set({'logopen': true});
state = true;
}
$(this).text(state ? chrome.i18n.getMessage('logActionHide') : chrome.i18n.getMessage('logActionShow'));
$(this).data('state', state);
});
chrome.storage.local.get('permanentExpertMode', function (result) {
if (result.permanentExpertMode) {
$('input[name="expertModeCheckbox"]').prop('checked', true);
}
$('input[name="expertModeCheckbox"]').change(function () {
if (FEATURE_CONFIG) {
updateTabList(FEATURE_CONFIG.features);
}
}).change();
});
});
function checkForConfiguratorUpdates() {
var releaseChecker = new ReleaseChecker('configurator', 'https://api.github.com/repos/betaflight/betaflight-configurator/releases');
releaseChecker.loadReleaseData(notifyOutdatedVersion);
}
function notifyOutdatedVersion(releaseData) {
chrome.storage.local.get('checkForConfiguratorUnstableVersions', function (result) {
var showUnstableReleases = false;
if (result.checkForConfiguratorUnstableVersions) {
showUnstableReleases = true;
}
var versions = releaseData.filter(function (version) {
var semVerVersion = semver.parse(version.tag_name);
if (semVerVersion && (showUnstableReleases || semVerVersion.prerelease.length === 0)) {
return version;
}
}).sort(function (v1, v2) {
try {
return semver.compare(v2.tag_name, v1.tag_name);
} catch (e) {
return false;
}
});
if (versions.length > 0 && semver.lt(getManifestVersion(), versions[0].tag_name)) {
GUI.log(chrome.i18n.getMessage('configuratorUpdateNotice', [versions[0].tag_name, versions[0].html_url]));
var dialog = $('.dialogConfiguratorUpdate')[0];
$('.dialogConfiguratorUpdate-content').html(chrome.i18n.getMessage('configuratorUpdateNotice', [versions[0].tag_name, versions[0].html_url]));
$('.dialogConfiguratorUpdate-closebtn').click(function() {
dialog.close();
});
$('.dialogConfiguratorUpdate-websitebtn').click(function() {
dialog.close();
window.open(versions[0].html_url);
});
dialog.showModal();
}
});
}
function update_packet_error(caller) {
$('span.packet-error').html(caller.packet_error);
}
function microtime() {
var now = new Date().getTime() / 1000;
return now;
}
function millitime() {
var now = new Date().getTime();
return now;
}
var DEGREE_TO_RADIAN_RATIO = Math.PI / 180;
function degToRad(degrees) {
return degrees * DEGREE_TO_RADIAN_RATIO;
}
function bytesToSize(bytes) {
if (bytes < 1024) {
bytes = bytes + ' Bytes';
} else if (bytes < 1048576) {
bytes = (bytes / 1024).toFixed(3) + ' KB';
} else if (bytes < 1073741824) {
bytes = (bytes / 1048576).toFixed(3) + ' MB';
} else {
bytes = (bytes / 1073741824).toFixed(3) + ' GB';
}
return bytes;
}
function isExpertModeEnabled() {
return $('input[name="expertModeCheckbox"]').is(':checked');
}
function updateTabList(features) {
if (features.isEnabled('GPS') && isExpertModeEnabled()) {
$('#tabs ul.mode-connected li.tab_gps').show();
} else {
$('#tabs ul.mode-connected li.tab_gps').hide();
}
if (isExpertModeEnabled()) {
$('#tabs ul.mode-connected li.tab_failsafe').show();
} else {
$('#tabs ul.mode-connected li.tab_failsafe').hide();
}
if (isExpertModeEnabled()) {
$('#tabs ul.mode-connected li.tab_adjustments').show();
} else {
$('#tabs ul.mode-connected li.tab_adjustments').hide();
}
if (isExpertModeEnabled()) {
$('#tabs ul.mode-connected li.tab_servos').show();
} else {
$('#tabs ul.mode-connected li.tab_servos').hide();
}
if (features.isEnabled('LED_STRIP')) {
$('#tabs ul.mode-connected li.tab_led_strip').show();
} else {
$('#tabs ul.mode-connected li.tab_led_strip').hide();
}
if (isExpertModeEnabled()) {
$('#tabs ul.mode-connected li.tab_sensors').show();
} else {
$('#tabs ul.mode-connected li.tab_sensors').hide();
}
if (isExpertModeEnabled()) {
$('#tabs ul.mode-connected li.tab_logging').show();
} else {
$('#tabs ul.mode-connected li.tab_logging').hide();
}
if (features.isEnabled('TRANSPONDER')) {
$('#tabs ul.mode-connected li.tab_transponder').show();
} else {
$('#tabs ul.mode-connected li.tab_transponder').hide();
}
if (features.isEnabled('OSD')) {
$('#tabs ul.mode-connected li.tab_osd').show();
} else {
$('#tabs ul.mode-connected li.tab_osd').hide();
}
if (semver.gte(CONFIG.apiVersion, "1.36.0")) {
$('#tabs ul.mode-connected li.tab_power').show();
} else {
$('#tabs ul.mode-connected li.tab_power').hide();
}
}
function zeroPad(value, width) {
value = "" + value;
while (value.length < width) {
value = "0" + value;
}
return value;
}
function generateFilename(prefix, suffix) {
var date = new Date();
var filename = prefix;
if (CONFIG) {
if (CONFIG.flightControllerIdentifier) {
filename = CONFIG.flightControllerIdentifier + '_' + filename;
}
if(CONFIG.name && CONFIG.name.trim() !== '') {
filename = filename + '_' + CONFIG.name.trim().replace(' ', '_');
}
}
filename = filename + '_' + date.getFullYear()
+ zeroPad(date.getMonth() + 1, 2)
+ zeroPad(date.getDate(), 2)
+ '_' + zeroPad(date.getHours(), 2)
+ zeroPad(date.getMinutes(), 2)
+ zeroPad(date.getSeconds(), 2);
return filename + '.' + suffix;
}
function getFirmwareVersion(firmwareVersion, firmwareId, hardwareId) {
var versionText = '';
if (firmwareVersion) {
versionText += chrome.i18n.getMessage('versionLabelFirmware') + ': ' + firmwareId + ' ' + firmwareVersion;
if (hardwareId) {
versionText += ' (' + chrome.i18n.getMessage('versionLabelTarget') + ': ' + hardwareId + ')';
}
}
return versionText;
}
function getConfiguratorVersion() {
return chrome.i18n.getMessage('versionLabelConfigurator') + ': ' + getManifestVersion();
}
function updateTopBarVersion(firmwareVersion, firmwareId, hardwareId) {
var versionText = getConfiguratorVersion() + '<br />';
versionText = versionText + getFirmwareVersion(firmwareVersion, firmwareId, hardwareId);
$('#logo .logo_text').html(versionText);
}
function updateStatusBarVersion(firmwareVersion, firmwareId, hardwareId) {
var versionText = '';
versionText = versionText + getFirmwareVersion(firmwareVersion, firmwareId, hardwareId);
if (versionText !== '') {
versionText = versionText + ', ';
}
versionText = versionText + getConfiguratorVersion();
$('#status-bar .version').text(versionText);
}
function getManifestVersion(manifest) {
if (!manifest) {
manifest = chrome.runtime.getManifest();
}
var version = manifest.version_name;
if (!version) {
version = manifest.version;
}
return version;
}

285
src/js/tabs/adjustments.js Normal file
View file

@ -0,0 +1,285 @@
'use strict';
TABS.adjustments = {};
TABS.adjustments.initialize = function (callback) {
var self = this;
GUI.active_tab_ref = this;
GUI.active_tab = 'adjustments';
function get_adjustment_ranges() {
MSP.send_message(MSPCodes.MSP_ADJUSTMENT_RANGES, false, false, get_box_ids);
}
function get_box_ids() {
MSP.send_message(MSPCodes.MSP_BOXIDS, false, false, get_rc_data);
}
function get_rc_data() {
MSP.send_message(MSPCodes.MSP_RC, false, false, load_html);
}
function load_html() {
self.adjust_template();
$('#content').load("./tabs/adjustments.html", process_html);
}
MSP.send_message(MSPCodes.MSP_BOXNAMES, false, false, get_adjustment_ranges);
function addAdjustment(adjustmentIndex, adjustmentRange, auxChannelCount) {
var template = $('#tab-adjustments-templates .adjustments .adjustment');
var newAdjustment = template.clone();
$(newAdjustment).attr('id', 'adjustment-' + adjustmentIndex);
$(newAdjustment).data('index', adjustmentIndex);
//
// update selected slot
//
var adjustmentList = $(newAdjustment).find('.adjustmentSlot .slot');
adjustmentList.val(adjustmentRange.slotIndex);
//
// populate source channel select box
//
var channelList = $(newAdjustment).find('.channelInfo .channel');
var channelOptionTemplate = $(channelList).find('option');
channelOptionTemplate.remove();
for (var channelIndex = 0; channelIndex < auxChannelCount; channelIndex++) {
var channelOption = channelOptionTemplate.clone();
channelOption.text('AUX ' + (channelIndex + 1));
channelOption.val(channelIndex);
channelList.append(channelOption);
}
channelList.val(adjustmentRange.auxChannelIndex);
//
// update selected function
//
var functionList = $(newAdjustment).find('.functionSelection .function');
// update list of selected functions
functionList.val(adjustmentRange.adjustmentFunction);
//
// populate function channel select box
//
var switchList = $(newAdjustment).find('.functionSwitchChannel .channel');
var switchOptionTemplate = $(switchList).find('option');
switchOptionTemplate.remove();
var switchOption;
for (var switchIndex = 0; switchIndex < auxChannelCount; switchIndex++) {
switchOption = switchOptionTemplate.clone();
switchOption.text('AUX ' + (switchIndex + 1));
switchOption.val(switchIndex);
switchList.append(switchOption);
}
switchList.val(adjustmentRange.auxSwitchChannelIndex);
//
// configure range
//
var channel_range = {
'min': [ 900 ],
'max': [ 2100 ]
};
var rangeValues = [1300, 1700];
if (adjustmentRange.range != undefined) {
rangeValues = [adjustmentRange.range.start, adjustmentRange.range.end];
}
var rangeElement = $(newAdjustment).find('.range');
$(rangeElement).find('.channel-slider').noUiSlider({
start: rangeValues,
behaviour: 'snap-drag',
margin: 50,
step: 25,
connect: true,
range: channel_range,
format: wNumb({
decimals: 0
})
});
$(newAdjustment).find('.channel-slider').Link('lower').to($(newAdjustment).find('.lowerLimitValue'));
$(newAdjustment).find('.channel-slider').Link('upper').to($(newAdjustment).find('.upperLimitValue'));
$(rangeElement).find(".pips-channel-range").noUiSlider_pips({
mode: 'values',
values: [900, 1000, 1200, 1400, 1500, 1600, 1800, 2000, 2100],
density: 4,
stepped: true
});
//
// add the enable/disable behavior
//
var enableElement = $(newAdjustment).find('.enable');
$(enableElement).data('adjustmentElement', newAdjustment);
$(enableElement).change(function() {
var adjustmentElement = $(this).data('adjustmentElement');
if ($(this).prop("checked")) {
$(adjustmentElement).find(':input').prop("disabled", false);
$(adjustmentElement).find('.channel-slider').removeAttr("disabled");
var rangeElement = $(adjustmentElement).find('.range .channel-slider');
var range = $(rangeElement).val();
if (range[0] == range[1]) {
var defaultRangeValues = [1300, 1700];
$(rangeElement).val(defaultRangeValues);
}
} else {
$(adjustmentElement).find(':input').prop("disabled", true);
$(adjustmentElement).find('.channel-slider').attr("disabled", "disabled");
}
// keep this element enabled
$(this).prop("disabled", false);
});
var isEnabled = (adjustmentRange.range.start != adjustmentRange.range.end);
$(enableElement).prop("checked", isEnabled).change();
return newAdjustment;
}
function process_html() {
var auxChannelCount = RC.active_channels - 4;
var modeTableBodyElement = $('.tab-adjustments .adjustments tbody');
for (var adjustmentIndex = 0; adjustmentIndex < ADJUSTMENT_RANGES.length; adjustmentIndex++) {
var newAdjustment = addAdjustment(adjustmentIndex, ADJUSTMENT_RANGES[adjustmentIndex], auxChannelCount);
modeTableBodyElement.append(newAdjustment);
}
// translate to user-selected language
localize();
// UI Hooks
$('a.save').click(function () {
// update internal data structures based on current UI elements
var requiredAdjustmentRangeCount = ADJUSTMENT_RANGES.length;
ADJUSTMENT_RANGES = [];
var defaultAdjustmentRange = {
slotIndex: 0,
auxChannelIndex: 0,
range: {
start: 900,
end: 900
},
adjustmentFunction: 0,
auxSwitchChannelIndex: 0
};
$('.tab-adjustments .adjustments .adjustment').each(function () {
var adjustmentElement = $(this);
if ($(adjustmentElement).find('.enable').prop("checked")) {
var rangeValues = $(this).find('.range .channel-slider').val();
var adjustmentRange = {
slotIndex: parseInt($(this).find('.adjustmentSlot .slot').val()),
auxChannelIndex: parseInt($(this).find('.channelInfo .channel').val()),
range: {
start: rangeValues[0],
end: rangeValues[1]
},
adjustmentFunction: parseInt($(this).find('.functionSelection .function').val()),
auxSwitchChannelIndex: parseInt($(this).find('.functionSwitchChannel .channel').val())
};
ADJUSTMENT_RANGES.push(adjustmentRange);
} else {
ADJUSTMENT_RANGES.push(defaultAdjustmentRange);
}
});
for (var adjustmentRangeIndex = ADJUSTMENT_RANGES.length; adjustmentRangeIndex < requiredAdjustmentRangeCount; adjustmentRangeIndex++) {
ADJUSTMENT_RANGES.push(defaultAdjustmentRange);
}
//
// send data to FC
//
mspHelper.sendAdjustmentRanges(save_to_eeprom);
function save_to_eeprom() {
MSP.send_message(MSPCodes.MSP_EEPROM_WRITE, false, false, function () {
GUI.log(chrome.i18n.getMessage('adjustmentsEepromSaved'));
});
}
});
function update_marker(auxChannelIndex, channelPosition) {
if (channelPosition < 900) {
channelPosition = 900;
} else if (channelPosition > 2100) {
channelPosition = 2100;
}
var percentage = (channelPosition - 900) / (2100-900) * 100;
$('.adjustments .adjustment').each( function () {
var auxChannelCandidateIndex = $(this).find('.channel').val();
if (auxChannelCandidateIndex != auxChannelIndex) {
return;
}
$(this).find('.range .marker').css('left', percentage + '%');
});
}
// data pulling functions used inside interval timer
function get_rc_data() {
MSP.send_message(MSPCodes.MSP_RC, false, false, update_ui);
}
function update_ui() {
var auxChannelCount = RC.active_channels - 4;
for (var auxChannelIndex = 0; auxChannelIndex < auxChannelCount; auxChannelIndex++) {
update_marker(auxChannelIndex, RC.channels[auxChannelIndex + 4]);
}
}
// update ui instantly on first load
update_ui();
// enable data pulling
GUI.interval_add('aux_data_pull', get_rc_data, 50);
// status data pulled via separate timer with static speed
GUI.interval_add('status_pull', function () {
MSP.send_message(MSPCodes.MSP_STATUS);
}, 250, true);
GUI.content_ready(callback);
}
};
TABS.adjustments.cleanup = function (callback) {
if (callback) callback();
};
TABS.adjustments.adjust_template = function () {
var availableFunctionCount;
if (semver.lt(CONFIG.apiVersion, "1.31.0")) {
availableFunctionCount = 21; // Available in betaflight 2.9
} else {
availableFunctionCount = 24; // RC rate Yaw / D setpoint / D setpoint transition added to 3.1.0
}
var template = $('#tab-adjustments-templates .adjustments .adjustment');
var functionList = $(template).find('.functionSelection .function');
var functionListOptions = $(functionList).find('option').slice(0,availableFunctionCount);
functionList.empty().append(functionListOptions);
};

326
src/js/tabs/auxiliary.js Normal file
View file

@ -0,0 +1,326 @@
'use strict';
TABS.auxiliary = {};
TABS.auxiliary.initialize = function (callback) {
GUI.active_tab_ref = this;
GUI.active_tab = 'auxiliary';
var prevChannelsValues = null;
function get_mode_ranges() {
MSP.send_message(MSPCodes.MSP_MODE_RANGES, false, false, get_box_ids);
}
function get_box_ids() {
MSP.send_message(MSPCodes.MSP_BOXIDS, false, false, get_rc_data);
}
function get_rc_data() {
MSP.send_message(MSPCodes.MSP_RC, false, false, get_serial_config);
}
function get_serial_config() {
MSP.send_message(MSPCodes.MSP_CF_SERIAL_CONFIG, false, false, load_html);
}
function load_html() {
$('#content').load("./tabs/auxiliary.html", process_html);
}
MSP.send_message(MSPCodes.MSP_BOXNAMES, false, false, get_mode_ranges);
function createMode(modeIndex, modeId) {
var modeTemplate = $('#tab-auxiliary-templates .mode');
var newMode = modeTemplate.clone();
var modeName = AUX_CONFIG[modeIndex];
// Adjust the name of the box if a peripheral is selected
modeName = adjustBoxNameIfPeripheralWithModeID(modeId, modeName);
$(newMode).attr('id', 'mode-' + modeIndex);
$(newMode).find('.name').text(modeName);
$(newMode).data('index', modeIndex);
$(newMode).data('id', modeId);
$(newMode).find('.name').data('modeElement', newMode);
$(newMode).find('a.addRange').data('modeElement', newMode);
return newMode;
}
function configureRangeTemplate(auxChannelCount) {
var rangeTemplate = $('#tab-auxiliary-templates .range');
var channelList = $(rangeTemplate).find('.channel');
var channelOptionTemplate = $(channelList).find('option');
channelOptionTemplate.remove();
//add value to autodetect channel
var channelOption = channelOptionTemplate.clone();
channelOption.text(chrome.i18n.getMessage('auxiliaryAutoChannelSelect'));
channelOption.val(-1);
channelList.append(channelOption);
for (var channelIndex = 0; channelIndex < auxChannelCount; channelIndex++) {
var channelOption = channelOptionTemplate.clone();
channelOption.text('AUX ' + (channelIndex + 1));
channelOption.val(channelIndex);
channelList.append(channelOption);
}
channelOptionTemplate.val(-1);
}
function addRangeToMode(modeElement, auxChannelIndex, range) {
var modeIndex = $(modeElement).data('index');
var channel_range = {
'min': [ 900 ],
'max': [ 2100 ]
};
var rangeValues = [1300, 1700]; // matches MultiWii default values for the old checkbox MID range.
if (range != undefined) {
rangeValues = [range.start, range.end];
}
var rangeIndex = $(modeElement).find('.range').length;
var rangeElement = $('#tab-auxiliary-templates .range').clone();
rangeElement.attr('id', 'mode-' + modeIndex + '-range-' + rangeIndex);
modeElement.find('.ranges').append(rangeElement);
$(rangeElement).find('.channel-slider').noUiSlider({
start: rangeValues,
behaviour: 'snap-drag',
margin: 50,
step: 25,
connect: true,
range: channel_range,
format: wNumb({
decimals: 0,
})
});
var elementName = '#mode-' + modeIndex + '-range-' + rangeIndex;
$(elementName + ' .channel-slider').Link('lower').to($(elementName + ' .lowerLimitValue'));
$(elementName + ' .channel-slider').Link('upper').to($(elementName + ' .upperLimitValue'));
$(rangeElement).find(".pips-channel-range").noUiSlider_pips({
mode: 'values',
values: [900, 1000, 1200, 1400, 1500, 1600, 1800, 2000, 2100],
density: 4,
stepped: true
});
$(rangeElement).find('.deleteRange').data('rangeElement', rangeElement);
$(rangeElement).find('a.deleteRange').click(function () {
var rangeElement = $(this).data('rangeElement');
rangeElement.remove();
});
$(rangeElement).find('.channel').val(auxChannelIndex);
}
function process_html() {
var auxChannelCount = RC.active_channels - 4;
configureRangeTemplate(auxChannelCount);
var modeTableBodyElement = $('.tab-auxiliary .modes tbody')
for (var modeIndex = 0; modeIndex < AUX_CONFIG.length; modeIndex++) {
var modeId = AUX_CONFIG_IDS[modeIndex];
var newMode = createMode(modeIndex, modeId);
modeTableBodyElement.append(newMode);
// generate ranges from the supplied AUX names and MODE_RANGE data
for (var modeRangeIndex = 0; modeRangeIndex < MODE_RANGES.length; modeRangeIndex++) {
var modeRange = MODE_RANGES[modeRangeIndex];
if (modeRange.id != modeId) {
continue;
}
var range = modeRange.range;
if (!(range.start < range.end)) {
continue; // invalid!
}
addRangeToMode(newMode, modeRange.auxChannelIndex, range)
}
}
$('a.addRange').click(function () {
var modeElement = $(this).data('modeElement');
//auto select AUTO option
addRangeToMode(modeElement, -1);
});
// translate to user-selected language
localize();
// UI Hooks
$('a.save').click(function () {
// update internal data structures based on current UI elements
// we must send this many back to the FC - overwrite all of the old ones to be sure.
var requiredModesRangeCount = MODE_RANGES.length;
MODE_RANGES = [];
$('.tab-auxiliary .modes .mode').each(function () {
var modeElement = $(this);
var modeId = modeElement.data('id');
$(modeElement).find('.range').each(function() {
var rangeValues = $(this).find('.channel-slider').val();
var modeRange = {
id: modeId,
auxChannelIndex: parseInt($(this).find('.channel').val()),
range: {
start: rangeValues[0],
end: rangeValues[1]
}
};
MODE_RANGES.push(modeRange);
});
});
for (var modeRangeIndex = MODE_RANGES.length; modeRangeIndex < requiredModesRangeCount; modeRangeIndex++) {
var defaultModeRange = {
id: 0,
auxChannelIndex: 0,
range: {
start: 900,
end: 900
}
};
MODE_RANGES.push(defaultModeRange);
}
//
// send data to FC
//
mspHelper.sendModeRanges(save_to_eeprom);
function save_to_eeprom() {
MSP.send_message(MSPCodes.MSP_EEPROM_WRITE, false, false, function () {
GUI.log(chrome.i18n.getMessage('auxiliaryEepromSaved'));
});
}
});
function box_highlight(auxChannelIndex, channelPosition) {
if (channelPosition < 900) {
channelPosition = 900;
} else if (channelPosition > 2100) {
channelPosition = 2100;
}
}
function update_marker(auxChannelIndex, channelPosition) {
var percentage = (channelPosition - 900) / (2100-900) * 100;
$('.modes .ranges .range').each( function () {
var auxChannelCandidateIndex = $(this).find('.channel').val();
if (auxChannelCandidateIndex != auxChannelIndex) {
return;
}
$(this).find('.marker').css('left', percentage + '%');
});
}
// data pulling functions used inside interval timer
function get_rc_data() {
MSP.send_message(MSPCodes.MSP_RC, false, false, update_ui);
}
function update_ui() {
for (var i = 0; i < AUX_CONFIG.length; i++) {
var modeElement = $('#mode-' + i);
if (modeElement.find(' .range').length == 0) {
// if the mode is unused, skip it
modeElement.removeClass('off').removeClass('on');
continue;
}
if (bit_check(CONFIG.mode, i)) {
$('.mode .name').eq(i).data('modeElement').addClass('on').removeClass('off');
} else {
$('.mode .name').eq(i).data('modeElement').removeClass('on').addClass('off');
}
}
auto_select_channel(RC.channels);
var auxChannelCount = RC.active_channels - 4;
for (var i = 0; i < (auxChannelCount); i++) {
box_highlight(i, RC.channels[i + 4]);
update_marker(i, RC.channels[i + 4]);
}
}
/**
* Autodetect channel based on maximum deference with previous value
* minimum value to autodetect is 100 - to prevent auto select RSSI channel
* @param RC_channels
*/
function auto_select_channel(RC_channels) {
var auto_option = $('.tab-auxiliary select.channel option[value="-1"]:selected');
if (auto_option.length === 0) {
prevChannelsValues = null;
return;
}
var fillPrevChannelsValues = function () {
prevChannelsValues = RC_channels.slice(0); //clone array
}
if (!prevChannelsValues || RC_channels.length === 0) return fillPrevChannelsValues();
var diff_array = RC_channels.map(function(currentValue, index) {
return Math.abs(prevChannelsValues[index] - currentValue);
});
var largest = diff_array.reduce(function(x,y){
return (x > y) ? x : y;
}, 0);
//minimum change to autoselect is 100
if (largest < 100) return fillPrevChannelsValues();
var indexOfMaxValue = diff_array.indexOf(largest);
if (indexOfMaxValue >= 4){ //set channel
auto_option.parent().val(indexOfMaxValue - 4);
}
return fillPrevChannelsValues();
}
// update ui instantly on first load
update_ui();
// enable data pulling
GUI.interval_add('aux_data_pull', get_rc_data, 50);
// status data pulled via separate timer with static speed
GUI.interval_add('status_pull', function () {
MSP.send_message(MSPCodes.MSP_STATUS);
}, 250, true);
GUI.content_ready(callback);
}
};
TABS.auxiliary.cleanup = function (callback) {
if (callback) callback();
};

281
src/js/tabs/cli.js Normal file
View file

@ -0,0 +1,281 @@
'use strict';
TABS.cli = {
'validateText': "",
'currentLine': "",
'sequenceElements': 0,
lineDelayMs: 15,
profileSwitchDelayMs: 100,
outputHistory: ""
};
TABS.cli.initialize = function (callback) {
var self = this;
if (GUI.active_tab != 'cli') {
GUI.active_tab = 'cli';
}
self.outputHistory = "";
$('#content').load("./tabs/cli.html", function () {
// translate to user-selected language
localize();
CONFIGURATOR.cliActive = true;
var textarea = $('.tab-cli textarea');
$('.tab-cli .save').click(function() {
var prefix = 'cli';
var suffix = 'txt';
var filename = generateFilename(prefix, suffix);
var accepts = [{
extensions: [suffix],
}];
chrome.fileSystem.chooseEntry({type: 'saveFile', suggestedName: filename, accepts: accepts}, function(entry) {
if (chrome.runtime.lastError) {
console.error(chrome.runtime.lastError.message);
return;
}
if (!entry) {
console.log('No file selected');
return;
}
entry.createWriter(function (writer) {
writer.onerror = function (){
console.error('Failed to write file');
};
writer.onwriteend = function () {
if (writer.length === 0) {
writer.write(new Blob([self.outputHistory], {type: 'text/plain'}));
} else {
console.log('write complete');
};
}
writer.truncate(0);
}, function (){
console.error('Failed to get file writer');
});
});
});
textarea.keypress(function (event) {
if (event.which == 13) { // enter
event.preventDefault(); // prevent the adding of new line
var out_string = textarea.val();
self.history.add(out_string.trim());
var outputArray = out_string.split("\n");
Promise.reduce(outputArray, function(delay, line) {
return new Promise(function (resolve) {
GUI.timeout_add('CLI_send_slowly', function () {
var processingDelay = self.lineDelayMs;
if (line.toLowerCase().startsWith('profile')) {
processingDelay = self.profileSwitchDelayMs;
}
self.sendLine(line, function () {
resolve(processingDelay);
});
}, delay)
})
}, 0);
textarea.val('');
}
});
textarea.keyup(function (event) {
var keyUp = {38: true},
keyDown = {40: true};
if (event.keyCode in keyUp) {
textarea.val(self.history.prev());
}
if (event.keyCode in keyDown) {
textarea.val(self.history.next());
}
});
// give input element user focus
textarea.focus();
GUI.timeout_add('enter_cli', function enter_cli() {
// Enter CLI mode
var bufferOut = new ArrayBuffer(1);
var bufView = new Uint8Array(bufferOut);
bufView[0] = 0x23; // #
serial.send(bufferOut);
}, 250);
GUI.content_ready(callback);
});
};
TABS.cli.history = {
history: [],
index: 0
};
TABS.cli.history.add = function (str) {
this.history.push(str);
this.index = this.history.length;
};
TABS.cli.history.prev = function () {
if (this.index > 0) this.index -= 1;
return this.history[this.index];
};
TABS.cli.history.next = function () {
if (this.index < this.history.length) this.index += 1;
return this.history[this.index - 1];
};
TABS.cli.read = function (readInfo) {
/* Some info about handling line feeds and carriage return
line feed = LF = \n = 0x0A = 10
carriage return = CR = \r = 0x0D = 13
MAC only understands CR
Linux and Unix only understand LF
Windows understands (both) CRLF
Chrome OS currenty unknown
*/
var data = new Uint8Array(readInfo.data),
text = "";
for (var i = 0; i < data.length; i++) {
if (CONFIGURATOR.cliValid) {
if (data[i] == 27 || this.sequenceElements > 0) { // ESC + other
this.sequenceElements++;
// delete previous space
if (this.sequenceElements == 1) {
text = text.substring(0, text.length -1);
}
// Reset
if (this.sequenceElements >= 5) {
this.sequenceElements = 0;
}
}
if (this.sequenceElements == 0) {
switch (data[i]) {
case 10: // line feed
if (GUI.operating_system != "MacOS") {
text += "<br />";
}
this.currentLine = "";
break;
case 13: // carriage return
if (GUI.operating_system == "MacOS") {
text += "<br />";
}
this.currentLine = "";
break;
case 60:
text += '&lt';
break;
case 62:
text += '&gt';
break;
default:
text += String.fromCharCode(data[i]);
this.currentLine += String.fromCharCode(data[i]);
}
this.outputHistory += String.fromCharCode(data[i])
}
if (this.currentLine == 'Rebooting') {
CONFIGURATOR.cliActive = false;
CONFIGURATOR.cliValid = false;
GUI.log(chrome.i18n.getMessage('cliReboot'));
GUI.log(chrome.i18n.getMessage('deviceRebooting'));
if (BOARD.find_board_definition(CONFIG.boardIdentifier).vcp) { // VCP-based flight controls may crash old drivers, we catch and reconnect
$('a.connect').click();
GUI.timeout_add('start_connection',function start_connection() {
$('a.connect').click();
},2500);
} else {
GUI.timeout_add('waiting_for_bootup', function waiting_for_bootup() {
MSP.send_message(MSPCodes.MSP_STATUS, false, false, function() {
GUI.log(chrome.i18n.getMessage('deviceReady'));
if (!GUI.tab_switch_in_progress) {
$('#tabs ul.mode-connected .tab_setup a').click();
}
});
},1500); // 1500 ms seems to be just the right amount of delay to prevent data request timeouts
}
}
} else {
// try to catch part of valid CLI enter message
this.validateText += String.fromCharCode(data[i]);
text += String.fromCharCode(data[i]);
}
}
if (!CONFIGURATOR.cliValid && this.validateText.indexOf('CLI') != -1) {
GUI.log(chrome.i18n.getMessage('cliEnter'));
CONFIGURATOR.cliValid = true;
this.validateText = "";
}
$('.tab-cli .window .wrapper').append(text);
$('.tab-cli .window').scrollTop($('.tab-cli .window .wrapper').height());
};
TABS.cli.sendLine = function (line, callback) {
var bufferOut = new ArrayBuffer(line.length + 1);
var bufView = new Uint8Array(bufferOut);
for (var c_key = 0; c_key < line.length; c_key++) {
bufView[c_key] = line.charCodeAt(c_key);
}
bufView[line.length] = 0x0D; // enter (\n)
serial.send(bufferOut, callback);
}
TABS.cli.cleanup = function (callback) {
if (!(CONFIGURATOR.connectionValid && CONFIGURATOR.cliValid && CONFIGURATOR.cliActive)) {
if (callback) callback();
return;
}
var bufferOut = new ArrayBuffer(5);
var bufView = new Uint8Array(bufferOut);
bufView[0] = 0x65; // e
bufView[1] = 0x78; // x
bufView[2] = 0x69; // i
bufView[3] = 0x74; // t
bufView[4] = 0x0D; // enter
serial.send(bufferOut, function (writeInfo) {
// we could handle this "nicely", but this will do for now
// (another approach is however much more complicated):
// we can setup an interval asking for data lets say every 200ms, when data arrives, callback will be triggered and tab switched
// we could probably implement this someday
GUI.timeout_add('waiting_for_bootup', function waiting_for_bootup() {
if (callback) callback();
}, 1000); // if we dont allow enough time to reboot, CRC of "first" command sent will fail, keep an eye for this one
CONFIGURATOR.cliActive = false;
});
};

1124
src/js/tabs/configuration.js Normal file

File diff suppressed because it is too large Load diff

349
src/js/tabs/failsafe.js Normal file
View file

@ -0,0 +1,349 @@
'use strict';
TABS.failsafe = {};
TABS.failsafe.initialize = function (callback, scrollPosition) {
var self = this;
if (GUI.active_tab != 'failsafe') {
GUI.active_tab = 'failsafe';
}
function load_rx_config() {
MSP.send_message(MSPCodes.MSP_RX_CONFIG, false, false, load_failssafe_config);
}
function load_failssafe_config() {
MSP.send_message(MSPCodes.MSP_FAILSAFE_CONFIG, false, false, load_rxfail_config);
}
function load_rxfail_config() {
MSP.send_message(MSPCodes.MSP_RXFAIL_CONFIG, false, false, get_box_names);
}
function get_box_names() {
MSP.send_message(MSPCodes.MSP_BOXNAMES, false, false, get_mode_ranges);
}
function get_mode_ranges() {
MSP.send_message(MSPCodes.MSP_MODE_RANGES, false, false, get_box_ids);
}
function get_box_ids() {
MSP.send_message(MSPCodes.MSP_BOXIDS, false, false, get_ports_config);
}
function get_ports_config() {
MSP.send_message(MSPCodes.MSP_CF_SERIAL_CONFIG, false, false, get_rc_data);
}
function get_rc_data() {
MSP.send_message(MSPCodes.MSP_RC, false, false, load_feature_config);
}
function load_feature_config() {
MSP.send_message(MSPCodes.MSP_FEATURE_CONFIG, false, false, load_motor_config);
}
function load_motor_config() {
MSP.send_message(MSPCodes.MSP_MOTOR_CONFIG, false, false, load_compass_config);
}
function load_compass_config() {
MSP.send_message(MSPCodes.MSP_COMPASS_CONFIG, false, false, load_gps_config);
}
function load_gps_config() {
MSP.send_message(MSPCodes.MSP_GPS_CONFIG, false, false, load_html);
}
function load_html() {
$('#content').load("./tabs/failsafe.html", process_html);
}
load_rx_config();
function process_html() {
// fill stage 2 fields
function toggleStage2(doShow) {
if (doShow) {
$('div.stage2').show();
} else {
$('div.stage2').hide();
}
}
// FIXME cleanup oldpane html and css
var oldPane = $('div.oldpane');
oldPane.prop("disabled", true);
oldPane.hide();
// generate labels for assigned aux modes
var auxAssignment = [],
i,
element;
for (var channelIndex = 0; channelIndex < RC.active_channels - 4; channelIndex++) {
auxAssignment.push("");
}
for (var modeIndex = 0; modeIndex < AUX_CONFIG.length; modeIndex++) {
var modeId = AUX_CONFIG_IDS[modeIndex];
// scan mode ranges to find assignments
for (var modeRangeIndex = 0; modeRangeIndex < MODE_RANGES.length; modeRangeIndex++) {
var modeRange = MODE_RANGES[modeRangeIndex];
if (modeRange.id != modeId) {
continue;
}
var range = modeRange.range;
if (!(range.start < range.end)) {
continue; // invalid!
}
// Search for the real name if it belongs to a peripheral
var modeName = AUX_CONFIG[modeIndex];
modeName = adjustBoxNameIfPeripheralWithModeID(modeId, modeName);
auxAssignment[modeRange.auxChannelIndex] += "<span class=\"modename\">" + modeName + "</span>";
}
}
// generate full channel list
var channelNames = [
chrome.i18n.getMessage('controlAxisRoll'),
chrome.i18n.getMessage('controlAxisPitch'),
chrome.i18n.getMessage('controlAxisYaw'),
chrome.i18n.getMessage('controlAxisThrottle')
],
fullChannels_e = $('div.activechannellist'),
aux_index = 1,
aux_assignment_index = 0;
for (i = 0; i < RXFAIL_CONFIG.length; i++) {
if (i < channelNames.length) {
fullChannels_e.append('\
<div class="number">\
<div class="channelprimary">\
<span>' + channelNames[i] + '</span>\
</div>\
<div class="cf_tip channelsetting" title="' + chrome.i18n.getMessage("failsafeChannelFallbackSettingsAuto") + '">\
<select class="aux_set" id="' + i + '">\
<option value="0">Auto</option>\
<option value="1">Hold</option>\
</select>\
</div>\
</div>\
');
} else {
fullChannels_e.append('\
<div class="number">\
<div class="channelauxiliary">\
<span class="channelname">' + chrome.i18n.getMessage("controlAxisAux" + (aux_index++)) + '</span>\
' + auxAssignment[aux_assignment_index++] + '\
</div>\
<div class="cf_tip channelsetting" title="' + chrome.i18n.getMessage("failsafeChannelFallbackSettingsHold") + '">\
<select class="aux_set" id="' + i + '">\
<option value="1">Hold</option>\
<option value="2">Set</option>\
</select>\
</div>\
<div class="auxiliary"><input type="number" name="aux_value" min="750" max="2250" step="25" id="' + i + '"/></div>\
</div>\
');
}
}
var channel_mode_array = [];
$('.number', fullChannels_e).each(function () {
channel_mode_array.push($('select.aux_set' , this));
});
var channel_value_array = [];
$('.number', fullChannels_e).each(function () {
channel_value_array.push($('input[name="aux_value"]' , this));
});
var channelMode = $('select.aux_set');
var channelValue = $('input[name="aux_value"]');
// UI hooks
channelMode.change(function () {
var currentMode = parseInt($(this).val());
var i = parseInt($(this).prop("id"));
RXFAIL_CONFIG[i].mode = currentMode;
if (currentMode == 2) {
channel_value_array[i].prop("disabled", false);
channel_value_array[i].show();
} else {
channel_value_array[i].prop("disabled", true);
channel_value_array[i].hide();
}
});
// UI hooks
channelValue.change(function () {
var i = parseInt($(this).prop("id"));
RXFAIL_CONFIG[i].value = parseInt($(this).val());
});
// for some odd reason chrome 38+ changes scroll according to the touched select element
// i am guessing this is a bug, since this wasn't happening on 37
// code below is a temporary fix, which we will be able to remove in the future (hopefully)
$('#content').scrollTop((scrollPosition) ? scrollPosition : 0);
// fill stage 1 Valid Pulse Range Settings
$('input[name="rx_min_usec"]').val(RX_CONFIG.rx_min_usec);
$('input[name="rx_max_usec"]').val(RX_CONFIG.rx_max_usec);
// fill fallback settings (mode and value) for all channels
for (i = 0; i < RXFAIL_CONFIG.length; i++) {
channel_value_array[i].val(RXFAIL_CONFIG[i].value);
channel_mode_array[i].val(RXFAIL_CONFIG[i].mode);
channel_mode_array[i].change();
}
FEATURE_CONFIG.features.generateElements($('.tab-failsafe .featuresNew'));
if (semver.gte(CONFIG.apiVersion, "1.36.0")) {
$('tbody.rxFailsafe').hide();
toggleStage2(true);
} else {
var failsafeFeature = $('input[name="FAILSAFE"]');
failsafeFeature.change(function () {
toggleStage2($(this).is(':checked'));
});
toggleStage2(FEATURE_CONFIG.features.isEnabled('FAILSAFE'));
}
$('input[name="failsafe_throttle"]').val(FAILSAFE_CONFIG.failsafe_throttle);
$('input[name="failsafe_off_delay"]').val(FAILSAFE_CONFIG.failsafe_off_delay);
$('input[name="failsafe_throttle_low_delay"]').val(FAILSAFE_CONFIG.failsafe_throttle_low_delay);
$('input[name="failsafe_delay"]').val(FAILSAFE_CONFIG.failsafe_delay);
// set stage 2 failsafe procedure
$('input[type="radio"].procedure').change(function () {
var element = $(this),
checked = element.is(':checked'),
id = element.attr('id');
switch(id) {
case 'drop':
if (checked) {
$('input[name="failsafe_throttle"]').prop("disabled", true);
$('input[name="failsafe_off_delay"]').prop("disabled", true);
}
break;
case 'land':
if (checked) {
$('input[name="failsafe_throttle"]').prop("disabled", false);
$('input[name="failsafe_off_delay"]').prop("disabled", false);
}
break;
}
});
switch(FAILSAFE_CONFIG.failsafe_procedure) {
default:
case 0:
element = $('input[id="land"]') ;
element.prop('checked', true);
element.change();
break;
case 1:
element = $('input[id="drop"]');
element.prop('checked', true);
element.change();
break;
}
// set stage 2 kill switch option
$('input[name="failsafe_kill_switch"]').prop('checked', FAILSAFE_CONFIG.failsafe_kill_switch);
$('a.save').click(function () {
// gather data that doesn't have automatic change event bound
FEATURE_CONFIG.features.updateData($('input[name="FAILSAFE"]'));
RX_CONFIG.rx_min_usec = parseInt($('input[name="rx_min_usec"]').val());
RX_CONFIG.rx_max_usec = parseInt($('input[name="rx_max_usec"]').val());
FAILSAFE_CONFIG.failsafe_throttle = parseInt($('input[name="failsafe_throttle"]').val());
FAILSAFE_CONFIG.failsafe_off_delay = parseInt($('input[name="failsafe_off_delay"]').val());
FAILSAFE_CONFIG.failsafe_throttle_low_delay = parseInt($('input[name="failsafe_throttle_low_delay"]').val());
FAILSAFE_CONFIG.failsafe_delay = parseInt($('input[name="failsafe_delay"]').val());
if( $('input[id="land"]').is(':checked')) {
FAILSAFE_CONFIG.failsafe_procedure = 0;
} else if( $('input[id="drop"]').is(':checked')) {
FAILSAFE_CONFIG.failsafe_procedure = 1;
}
FAILSAFE_CONFIG.failsafe_kill_switch = $('input[name="failsafe_kill_switch"]').is(':checked') ? 1 : 0;
function save_failssafe_config() {
MSP.send_message(MSPCodes.MSP_SET_FAILSAFE_CONFIG, mspHelper.crunch(MSPCodes.MSP_SET_FAILSAFE_CONFIG), false, save_rxfail_config);
}
function save_rxfail_config() {
mspHelper.sendRxFailConfig(save_feature_config);
}
function save_feature_config() {
MSP.send_message(MSPCodes.MSP_SET_FEATURE_CONFIG, mspHelper.crunch(MSPCodes.MSP_SET_FEATURE_CONFIG), false, save_to_eeprom);
}
function save_to_eeprom() {
MSP.send_message(MSPCodes.MSP_EEPROM_WRITE, false, false, reboot);
}
function reboot() {
GUI.log(chrome.i18n.getMessage('configurationEepromSaved'));
GUI.tab_switch_cleanup(function() {
MSP.send_message(MSPCodes.MSP_SET_REBOOT, false, false, reinitialize);
});
}
function reinitialize() {
GUI.log(chrome.i18n.getMessage('deviceRebooting'));
if (BOARD.find_board_definition(CONFIG.boardIdentifier).vcp) { // VCP-based flight controls may crash old drivers, we catch and reconnect
$('a.connect').click();
GUI.timeout_add('start_connection',function start_connection() {
$('a.connect').click();
},2500);
} else {
GUI.timeout_add('waiting_for_bootup', function waiting_for_bootup() {
MSP.send_message(MSPCodes.MSP_STATUS, false, false, function() {
GUI.log(chrome.i18n.getMessage('deviceReady'));
TABS.failsafe.initialize(false, $('#content').scrollTop());
});
},1500); // 1500 ms seems to be just the right amount of delay to prevent data request timeouts
}
}
MSP.send_message(MSPCodes.MSP_SET_RX_CONFIG, mspHelper.crunch(MSPCodes.MSP_SET_RX_CONFIG), false, save_failssafe_config);
});
// translate to user-selected language
localize();
// status data pulled via separate timer with static speed
GUI.interval_add('status_pull', function status_pull() {
MSP.send_message(MSPCodes.MSP_STATUS);
}, 250, true);
GUI.content_ready(callback);
}
};
TABS.failsafe.cleanup = function (callback) {
if (callback) callback();
};

512
src/js/tabs/firmware_flasher.js Executable file
View file

@ -0,0 +1,512 @@
'use strict';
TABS.firmware_flasher = {
releases: null,
releaseChecker: new ReleaseChecker('firmware', 'https://api.github.com/repos/betaflight/betaflight/releases')
};
TABS.firmware_flasher.initialize = function (callback) {
var self = this;
if (GUI.active_tab != 'firmware_flasher') {
GUI.active_tab = 'firmware_flasher';
}
var intel_hex = false, // standard intel hex in string format
parsed_hex = false; // parsed raw hex in array format
$('#content').load("./tabs/firmware_flasher.html", function () {
function parse_hex(str, callback) {
// parsing hex in different thread
var worker = new Worker('./js/workers/hex_parser.js');
// "callback"
worker.onmessage = function (event) {
callback(event.data);
};
// send data/string over for processing
worker.postMessage(str);
}
function buildBoardOptions(releaseData) {
if (!releaseData) {
$('select[name="board"]').empty().append('<option value="0">Offline</option>');
$('select[name="firmware_version"]').empty().append('<option value="0">Offline</option>');
} else {
var boards_e = $('select[name="board"]').empty();
var showDevReleases = ($('input.show_development_releases').is(':checked'));
boards_e.append($("<option value='0'>{0}</option>".format(chrome.i18n.getMessage('firmwareFlasherOptionLabelSelectBoard'))));
var versions_e = $('select[name="firmware_version"]').empty();
versions_e.append($("<option value='0'>{0}</option>".format(chrome.i18n.getMessage('firmwareFlasherOptionLabelSelectFirmwareVersion'))));
var releases = {};
var sortedTargets = [];
var unsortedTargets = [];
releaseData.forEach(function(release){
release.assets.forEach(function(asset){
var targetFromFilenameExpression = /betaflight_([\d.]+)?_?(\w+)(\-.*)?\.(.*)/;
var match = targetFromFilenameExpression.exec(asset.name);
if ((!showDevReleases && release.prerelease) || !match) {
return;
}
var target = match[2];
if($.inArray(target, unsortedTargets) == -1) {
unsortedTargets.push(target);
}
});
sortedTargets = unsortedTargets.sort();
});
sortedTargets.forEach(function(release) {
releases[release] = [];
});
releaseData.forEach(function(release){
var versionFromTagExpression = /v?(.*)/;
var matchVersionFromTag = versionFromTagExpression.exec(release.tag_name);
var version = matchVersionFromTag[1];
release.assets.forEach(function(asset){
var targetFromFilenameExpression = /betaflight_([\d.]+)?_?(\w+)(\-.*)?\.(.*)/;
var match = targetFromFilenameExpression.exec(asset.name);
if ((!showDevReleases && release.prerelease) || !match) {
return;
}
var target = match[2];
var format = match[4];
if (format != 'hex') {
return;
}
var date = new Date(release.published_at);
var formattedDate = ("0" + date.getDate()).slice(-2) + "-" + ("0"+(date.getMonth()+1)).slice(-2) + "-" + date.getFullYear() + " " + ("0" + date.getHours()).slice(-2) + ":" + ("0" + date.getMinutes()).slice(-2);
var descriptor = {
"releaseUrl": release.html_url,
"name" : version,
"version" : version,
"url" : asset.browser_download_url,
"file" : asset.name,
"target" : target,
"date" : formattedDate,
"notes" : release.body,
"status" : release.prerelease ? "release-candidate" : "stable"
};
releases[target].push(descriptor);
});
});
var selectTargets = [];
Object.keys(releases)
.sort()
.forEach(function(target, i) {
var descriptors = releases[target];
descriptors.forEach(function(descriptor){
if($.inArray(target, selectTargets) == -1) {
selectTargets.push(target);
var select_e =
$("<option value='{0}'>{0}</option>".format(
descriptor.target
)).data('summary', descriptor);
boards_e.append(select_e);
}
});
});
TABS.firmware_flasher.releases = releases;
chrome.storage.local.get('selected_board', function (result) {
if (result.selected_board) {
$('select[name="board"]').val(result.selected_board);
$('select[name="board"]').trigger("change");
}
});
}
};
// translate to user-selected language
localize();
// bind events
$('input.show_development_releases').click(function () {
self.releaseChecker.loadReleaseData(buildBoardOptions);
});
$('select[name="board"]').change(function() {
$("a.load_remote_file").addClass('disabled');
var target = $(this).val();
if (!GUI.connect_lock) {
$('.progress').val(0).removeClass('valid invalid');
$('span.progressLabel').text(chrome.i18n.getMessage('firmwareFlasherLoadFirmwareFile'));
$('div.git_info').slideUp();
$('div.release_info').slideUp();
$('a.flash_firmware').addClass('disabled');
var versions_e = $('select[name="firmware_version"]').empty();
if(target == 0) {
versions_e.append($("<option value='0'>{0}</option>".format(chrome.i18n.getMessage('firmwareFlasherOptionLabelSelectFirmwareVersion'))));
} else {
versions_e.append($("<option value='0'>{0} {1}</option>".format(chrome.i18n.getMessage('firmwareFlasherOptionLabelSelectFirmwareVersionFor'), target)));
}
TABS.firmware_flasher.releases[target].forEach(function(descriptor) {
var select_e =
$("<option value='{0}'>{0} - {1} - {2} ({3})</option>".format(
descriptor.version,
descriptor.target,
descriptor.date,
descriptor.status
)).data('summary', descriptor);
versions_e.append(select_e);
});
}
chrome.storage.local.set({'selected_board': target});
});
// UI Hooks
$('a.load_file').click(function () {
chrome.fileSystem.chooseEntry({type: 'openFile', accepts: [{extensions: ['hex']}]}, function (fileEntry) {
if (chrome.runtime.lastError) {
console.error(chrome.runtime.lastError.message);
return;
}
// hide github info (if it exists)
$('div.git_info').slideUp();
chrome.fileSystem.getDisplayPath(fileEntry, function (path) {
console.log('Loading file from: ' + path);
fileEntry.file(function (file) {
var reader = new FileReader();
reader.onprogress = function (e) {
if (e.total > 1048576) { // 1 MB
// dont allow reading files bigger then 1 MB
console.log('File limit (1 MB) exceeded, aborting');
reader.abort();
}
};
reader.onloadend = function(e) {
if (e.total != 0 && e.total == e.loaded) {
console.log('File loaded');
intel_hex = e.target.result;
parse_hex(intel_hex, function (data) {
parsed_hex = data;
if (parsed_hex) {
$('a.flash_firmware').removeClass('disabled');
$('span.progressLabel').text('Loaded Local Firmware: (' + parsed_hex.bytes_total + ' bytes)');
} else {
$('span.progressLabel').text(chrome.i18n.getMessage('firmwareFlasherHexCorrupted'));
}
});
}
};
reader.readAsText(file);
});
});
});
});
/**
* Lock / Unlock the firmware download button according to the firmware selection dropdown.
*/
$('select[name="firmware_version"]').change(function(evt){
$('div.release_info').slideUp();
$('a.flash_firmware').addClass('disabled');
if (evt.target.value=="0") {
$("a.load_remote_file").addClass('disabled');
}
else {
$("a.load_remote_file").removeClass('disabled');
}
});
$('a.load_remote_file').click(function (evt) {
if ($('select[name="firmware_version"]').val() == "0") {
GUI.log(chrome.i18n.getMessage('firmwareFlasherNoFirmwareSelected'));
return;
}
function process_hex(data, summary) {
intel_hex = data;
parse_hex(intel_hex, function (data) {
parsed_hex = data;
if (parsed_hex) {
var url;
$('span.progressLabel').html('<a class="save_firmware" href="#" title="Save Firmware">Loaded Online Firmware: (' + parsed_hex.bytes_total + ' bytes)</a>');
$('a.flash_firmware').removeClass('disabled');
$('div.release_info .target').text(summary.target);
$('div.release_info .name').text(summary.version).prop('href', summary.releaseUrl);
$('div.release_info .date').text(summary.date);
$('div.release_info .status').text(summary.status);
$('div.release_info .file').text(summary.file).prop('href', summary.url);
var formattedNotes = summary.notes.trim('\r').replace(/\r/g, '<br />');
$('div.release_info .notes').html(formattedNotes);
$('div.release_info').slideDown();
} else {
$('span.progressLabel').text(chrome.i18n.getMessage('firmwareFlasherHexCorrupted'));
}
});
}
function failed_to_load() {
$('span.progressLabel').text(chrome.i18n.getMessage('firmwareFlasherFailedToLoadOnlineFirmware'));
$('a.flash_firmware').addClass('disabled');
$("a.load_remote_file").removeClass('disabled');
$("a.load_remote_file").text(chrome.i18n.getMessage('firmwareFlasherButtonLoadOnline'));
}
var summary = $('select[name="firmware_version"] option:selected').data('summary');
if (summary) { // undefined while list is loading or while running offline
$("a.load_remote_file").text(chrome.i18n.getMessage('firmwareFlasherButtonDownloading'));
$("a.load_remote_file").addClass('disabled');
$.get(summary.url, function (data) {
process_hex(data, summary);
$("a.load_remote_file").removeClass('disabled');
$("a.load_remote_file").text(chrome.i18n.getMessage('firmwareFlasherButtonLoadOnline'));
}).fail(failed_to_load);
} else {
$('span.progressLabel').text(chrome.i18n.getMessage('firmwareFlasherFailedToLoadOnlineFirmware'));
}
});
$('a.flash_firmware').click(function () {
if (!$(this).hasClass('disabled')) {
if (!GUI.connect_lock) { // button disabled while flashing is in progress
if (parsed_hex != false) {
var options = {};
if ($('input.erase_chip').is(':checked')) {
options.erase_chip = true;
}
if (String($('div#port-picker #port').val()) != 'DFU') {
if (String($('div#port-picker #port').val()) != '0') {
var port = String($('div#port-picker #port').val()),
baud;
baud = 115200;
if ($('input.updating').is(':checked')) {
options.no_reboot = true;
} else {
options.reboot_baud = parseInt($('div#port-picker #baud').val());
}
if ($('input.flash_manual_baud').is(':checked')) {
baud = parseInt($('#flash_manual_baud_rate').val());
}
STM32.connect(port, baud, parsed_hex, options);
} else {
console.log('Please select valid serial port');
GUI.log(chrome.i18n.getMessage('firmwareFlasherNoValidPort'));
}
} else {
STM32DFU.connect(usbDevices.STM32DFU, parsed_hex, options);
}
} else {
$('span.progressLabel').text(chrome.i18n.getMessage('firmwareFlasherFirmwareNotLoaded'));
}
}
}
});
$(document).on('click', 'span.progressLabel a.save_firmware', function () {
var summary = $('select[name="firmware_version"] option:selected').data('summary');
chrome.fileSystem.chooseEntry({type: 'saveFile', suggestedName: summary.file, accepts: [{extensions: ['hex']}]}, 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(chrome.i18n.getMessage('firmwareFlasherWritePermissions'));
}
});
});
});
});
chrome.storage.local.get('no_reboot_sequence', function (result) {
if (result.no_reboot_sequence) {
$('input.updating').prop('checked', true);
$('.flash_on_connect_wrapper').show();
} else {
$('input.updating').prop('checked', false);
}
// bind UI hook so the status is saved on change
$('input.updating').change(function() {
var status = $(this).is(':checked');
if (status) {
$('.flash_on_connect_wrapper').show();
} else {
$('input.flash_on_connect').prop('checked', false).change();
$('.flash_on_connect_wrapper').hide();
}
chrome.storage.local.set({'no_reboot_sequence': status});
});
$('input.updating').change();
});
chrome.storage.local.get('flash_manual_baud', function (result) {
if (result.flash_manual_baud) {
$('input.flash_manual_baud').prop('checked', true);
} else {
$('input.flash_manual_baud').prop('checked', false);
}
// bind UI hook so the status is saved on change
$('input.flash_manual_baud').change(function() {
var status = $(this).is(':checked');
chrome.storage.local.set({'flash_manual_baud': status});
});
$('input.flash_manual_baud').change();
});
chrome.storage.local.get('flash_manual_baud_rate', function (result) {
$('#flash_manual_baud_rate').val(result.flash_manual_baud_rate);
// bind UI hook so the status is saved on change
$('#flash_manual_baud_rate').change(function() {
var baud = parseInt($('#flash_manual_baud_rate').val());
chrome.storage.local.set({'flash_manual_baud_rate': baud});
});
$('input.flash_manual_baud_rate').change();
});
$('input.flash_on_connect').change(function () {
var status = $(this).is(':checked');
if (status) {
var catch_new_port = function () {
PortHandler.port_detected('flash_detected_device', function (result) {
var port = result[0];
if (!GUI.connect_lock) {
GUI.log(chrome.i18n.getMessage('firmwareFlasherFlashTrigger', [port]));
console.log('Detected: ' + port + ' - triggering flash on connect');
// Trigger regular Flashing sequence
GUI.timeout_add('initialization_timeout', function () {
$('a.flash_firmware').click();
}, 100); // timeout so bus have time to initialize after being detected by the system
} else {
GUI.log(chrome.i18n.getMessage('firmwareFlasherPreviousDevice', [port]));
}
// Since current port_detected request was consumed, create new one
catch_new_port();
}, false, true);
};
catch_new_port();
} else {
PortHandler.flush_callbacks();
}
}).change();
chrome.storage.local.get('erase_chip', function (result) {
if (result.erase_chip) {
$('input.erase_chip').prop('checked', true);
} else {
$('input.erase_chip').prop('checked', false);
}
$('input.erase_chip').change(function () {
chrome.storage.local.set({'erase_chip': $(this).is(':checked')});
}).change();
});
chrome.storage.local.get('show_development_releases', function (result) {
if (result.show_development_releases) {
$('input.show_development_releases').prop('checked', true);
} else {
$('input.show_development_releases').prop('checked', false);
}
self.releaseChecker.loadReleaseData(buildBoardOptions);
$('input.show_development_releases').change(function () {
chrome.storage.local.set({'show_development_releases': $(this).is(':checked')});
}).change();
});
$(document).keypress(function (e) {
if (e.which == 13) { // enter
// Trigger regular Flashing sequence
$('a.flash_firmware').click();
}
});
GUI.content_ready(callback);
});
};
TABS.firmware_flasher.cleanup = function (callback) {
PortHandler.flush_callbacks();
// unbind "global" events
$(document).unbind('keypress');
$(document).off('click', 'span.progressLabel a');
if (callback) callback();
};

158
src/js/tabs/gps.js Normal file
View file

@ -0,0 +1,158 @@
'use strict';
TABS.gps = {};
TABS.gps.initialize = function (callback) {
var self = this;
if (GUI.active_tab !== 'gps') {
GUI.active_tab = 'gps';
}
function load_html() {
$('#content').load("./tabs/gps.html", process_html);
}
MSP.send_message(MSPCodes.MSP_STATUS, false, false, load_html);
function set_online(){
$('#connect').hide();
$('#waiting').show();
$('#loadmap').hide();
}
function set_offline(){
$('#connect').show();
$('#waiting').hide();
$('#loadmap').hide();
}
function process_html() {
// translate to user-selected languageconsole.log('Online');
localize();
function get_raw_gps_data() {
MSP.send_message(MSPCodes.MSP_RAW_GPS, false, false, get_comp_gps_data);
}
function get_comp_gps_data() {
MSP.send_message(MSPCodes.MSP_COMP_GPS, false, false, get_gpsvinfo_data);
}
function get_gpsvinfo_data() {
MSP.send_message(MSPCodes.MSP_GPS_SV_INFO, false, false, update_ui);
}
function update_ui() {
var lat = GPS_DATA.lat / 10000000;
var lon = GPS_DATA.lon / 10000000;
var url = 'https://maps.google.com/?q=' + lat + ',' + lon;
$('.GPS_info td.fix').html((GPS_DATA.fix) ? chrome.i18n.getMessage('gpsFixTrue') : chrome.i18n.getMessage('gpsFixFalse'));
$('.GPS_info td.alt').text((GPS_DATA.alt / 10) + ' m');
$('.GPS_info td.lat a').prop('href', url).text(lat.toFixed(4) + ' deg');
$('.GPS_info td.lon a').prop('href', url).text(lon.toFixed(4) + ' deg');
$('.GPS_info td.speed').text(GPS_DATA.speed + ' cm/s');
$('.GPS_info td.sats').text(GPS_DATA.numSat);
$('.GPS_info td.distToHome').text(GPS_DATA.distanceToHome + ' m');
// Update GPS Signal Strengths
var e_ss_table = $('div.GPS_signal_strength table tr:not(.titles)');
for (var i = 0; i < GPS_DATA.chn.length; i++) {
var row = e_ss_table.eq(i);
$('td', row).eq(0).text(GPS_DATA.svid[i]);
$('td', row).eq(1).text(GPS_DATA.quality[i]);
$('td', row).eq(2).find('progress').val(GPS_DATA.cno[i]);
}
var message = {
action: 'center',
lat: lat,
lon: lon
};
var frame = document.getElementById('map');
if (navigator.onLine) {
$('#connect').hide();
//if(lat != 0 && lon != 0){
if(GPS_DATA.fix){
frame.contentWindow.postMessage(message, '*');
$('#loadmap').show();
$('#waiting').hide();
}else{
$('#loadmap').hide();
$('#waiting').show();
}
}else{
$('#connect').show();
$('#waiting').hide();
$('#loadmap').hide();
}
}
// enable data pulling
GUI.interval_add('gps_pull', function gps_update() {
// avoid usage of the GPS commands until a GPS sensor is detected for targets that are compiled without GPS support.
if (!have_sensor(CONFIG.activeSensors, 'gps')) {
//return;
}
get_raw_gps_data();
}, 75, true);
// status data pulled via separate timer with static speed
GUI.interval_add('status_pull', function status_pull() {
MSP.send_message(MSPCodes.MSP_STATUS);
}, 250, true);
//check for internet connection on load
if (navigator.onLine) {
console.log('Online');
set_online();
} else {
console.log('Offline');
set_offline();
}
$("#check").on('click',function(){
if (navigator.onLine) {
console.log('Online');
set_online();
} else {
console.log('Offline');
set_offline();
}
});
var frame = document.getElementById('map');
$('#zoom_in').click(function() {
console.log('zoom in');
var message = {
action: 'zoom_in'
};
frame.contentWindow.postMessage(message, '*');
});
$('#zoom_out').click(function() {
console.log('zoom out');
var message = {
action: 'zoom_out'
};
frame.contentWindow.postMessage(message, '*');
});
GUI.content_ready(callback);
}
};
TABS.gps.cleanup = function (callback) {
if (callback) callback();
};

20
src/js/tabs/help.js Normal file
View file

@ -0,0 +1,20 @@
'use strict';
TABS.help = {};
TABS.help.initialize = function (callback) {
var self = this;
if (GUI.active_tab != 'help') {
GUI.active_tab = 'help';
}
$('#content').load("./tabs/help.html", function () {
localize();
GUI.content_ready(callback);
});
};
TABS.help.cleanup = function (callback) {
if (callback) callback();
};

42
src/js/tabs/landing.js Normal file
View file

@ -0,0 +1,42 @@
'use strict';
TABS.landing = {};
TABS.landing.initialize = function (callback) {
var self = this;
if (GUI.active_tab != 'landing') {
GUI.active_tab = 'landing';
}
$('#content').load("./tabs/landing.html", function () {
// translate to user-selected language
localize();
// load changelog content
$('#changelog .log').load('./changelog.html');
/** changelog trigger **/
$("#changelog_toggle").on('click', function() {
var state = $(this).data('state2');
if (state) {
$("#changelog").animate({right: -245}, 200, function () {
$("#content").removeClass('log_open');
});
state = false;
} else {
$("#changelog").animate({right: 0}, 200);
$("#content").addClass('log_open');
state = true;
}
$(this).text(state ? chrome.i18n.getMessage('close') : chrome.i18n.getMessage('defaultChangelogAction'));
$(this).data('state2', state);
});
GUI.content_ready(callback);
});
};
TABS.landing.cleanup = function (callback) {
if (callback) callback();
};

1144
src/js/tabs/led_strip.js Normal file

File diff suppressed because it is too large Load diff

322
src/js/tabs/logging.js Normal file
View file

@ -0,0 +1,322 @@
'use strict';
TABS.logging = {};
TABS.logging.initialize = function (callback) {
var self = this;
if (GUI.active_tab != 'logging') {
GUI.active_tab = 'logging';
}
var requested_properties = [],
samples = 0,
requests = 0,
log_buffer = [];
if (CONFIGURATOR.connectionValid) {
var get_motor_data = function () {
MSP.send_message(MSPCodes.MSP_MOTOR, false, false, load_html);
}
var load_html = function () {
$('#content').load("./tabs/logging.html", process_html);
}
MSP.send_message(MSPCodes.MSP_RC, false, false, get_motor_data);
}
function process_html() {
// translate to user-selected language
localize();
// UI hooks
$('a.log_file').click(prepare_file);
$('a.logging').click(function () {
if (GUI.connected_to) {
if (fileEntry != null) {
var clicks = $(this).data('clicks');
if (!clicks) {
// reset some variables before start
samples = 0;
requests = 0;
log_buffer = [];
requested_properties = [];
$('.properties input:checked').each(function () {
requested_properties.push($(this).prop('name'));
});
if (requested_properties.length) {
// print header for the csv file
print_head();
var log_data_poll = function () {
if (requests) {
// save current data (only after everything is initialized)
crunch_data();
}
// request new
for (var i = 0; i < requested_properties.length; i++, requests++) {
MSP.send_message(MSPCodes[requested_properties[i]]);
}
}
GUI.interval_add('log_data_poll', log_data_poll, parseInt($('select.speed').val()), true); // refresh rate goes here
GUI.interval_add('write_data', function write_data() {
if (log_buffer.length) { // only execute when there is actual data to write
if (fileWriter.readyState == 0 || fileWriter.readyState == 2) {
append_to_file(log_buffer.join('\n'));
$('.samples').text(samples += log_buffer.length);
log_buffer = [];
} else {
console.log('IO having trouble keeping up with the data flow');
}
}
}, 1000);
$('.speed').prop('disabled', true);
$(this).text(chrome.i18n.getMessage('loggingStop'));
$(this).data("clicks", !clicks);
} else {
GUI.log(chrome.i18n.getMessage('loggingErrorOneProperty'));
}
} else {
GUI.interval_kill_all();
$('.speed').prop('disabled', false);
$(this).text(chrome.i18n.getMessage('loggingStart'));
$(this).data("clicks", !clicks);
}
} else {
GUI.log(chrome.i18n.getMessage('loggingErrorLogFile'));
}
} else {
GUI.log(chrome.i18n.getMessage('loggingErrorNotConnected'));
}
});
chrome.storage.local.get('logging_file_entry', function (result) {
if (result.logging_file_entry) {
chrome.fileSystem.restoreEntry(result.logging_file_entry, function (entry) {
fileEntry = entry;
prepare_writer(true);
});
}
});
GUI.content_ready(callback);
}
function print_head() {
var head = "timestamp";
for (var i = 0; i < requested_properties.length; i++) {
switch (requested_properties[i]) {
case 'MSP_RAW_IMU':
head += ',' + 'gyroscopeX';
head += ',' + 'gyroscopeY';
head += ',' + 'gyroscopeZ';
head += ',' + 'accelerometerX';
head += ',' + 'accelerometerY';
head += ',' + 'accelerometerZ';
head += ',' + 'magnetometerX';
head += ',' + 'magnetometerY';
head += ',' + 'magnetometerZ';
break;
case 'MSP_ATTITUDE':
head += ',' + 'kinematicsX';
head += ',' + 'kinematicsY';
head += ',' + 'kinematicsZ';
break;
case 'MSP_ALTITUDE':
head += ',' + 'altitude';
break;
case 'MSP_RAW_GPS':
head += ',' + 'gpsFix';
head += ',' + 'gpsNumSat';
head += ',' + 'gpsLat';
head += ',' + 'gpsLon';
head += ',' + 'gpsAlt';
head += ',' + 'gpsSpeed';
head += ',' + 'gpsGroundCourse';
break;
case 'MSP_ANALOG':
head += ',' + 'voltage';
head += ',' + 'amperage';
head += ',' + 'mAhdrawn';
head += ',' + 'rssi';
break;
case 'MSP_RC':
for (var chan = 0; chan < RC.active_channels; chan++) {
head += ',' + 'RC' + chan;
}
break;
case 'MSP_MOTOR':
for (var motor = 0; motor < MOTOR_DATA.length; motor++) {
head += ',' + 'Motor' + motor;
}
break;
case 'MSP_DEBUG':
for (var debug = 0; debug < SENSOR_DATA.debug.length; debug++) {
head += ',' + 'Debug' + debug;
}
break;
}
}
append_to_file(head);
}
function crunch_data() {
var sample = millitime();
for (var i = 0; i < requested_properties.length; i++) {
switch (requested_properties[i]) {
case 'MSP_RAW_IMU':
sample += ',' + SENSOR_DATA.gyroscope;
sample += ',' + SENSOR_DATA.accelerometer;
sample += ',' + SENSOR_DATA.magnetometer;
break;
case 'MSP_ATTITUDE':
sample += ',' + SENSOR_DATA.kinematics[0];
sample += ',' + SENSOR_DATA.kinematics[1];
sample += ',' + SENSOR_DATA.kinematics[2];
break;
case 'MSP_ALTITUDE':
sample += ',' + SENSOR_DATA.altitude;
break;
case 'MSP_RAW_GPS':
sample += ',' + GPS_DATA.fix;
sample += ',' + GPS_DATA.numSat;
sample += ',' + (GPS_DATA.lat / 10000000);
sample += ',' + (GPS_DATA.lon / 10000000);
sample += ',' + GPS_DATA.alt;
sample += ',' + GPS_DATA.speed;
sample += ',' + GPS_DATA.ground_course;
break;
case 'MSP_ANALOG':
sample += ',' + ANALOG.voltage;
sample += ',' + ANALOG.amperage;
sample += ',' + ANALOG.mAhdrawn;
sample += ',' + ANALOG.rssi;
break;
case 'MSP_RC':
for (var chan = 0; chan < RC.active_channels; chan++) {
sample += ',' + RC.channels[chan];
}
break;
case 'MSP_MOTOR':
sample += ',' + MOTOR_DATA;
break;
case 'MSP_DEBUG':
sample += ',' + SENSOR_DATA.debug;
break;
}
}
log_buffer.push(sample);
}
// IO related methods
var fileEntry = null,
fileWriter = null;
function prepare_file() {
var prefix = 'log';
var suffix = 'csv';
var filename = generateFilename(prefix, suffix);
var accepts = [{
extensions: [suffix],
}];
// create or load the file
chrome.fileSystem.chooseEntry({type: 'saveFile', suggestedName: filename, accepts: accepts}, function(entry) {
if (!entry) {
console.log('No file selected');
return;
}
fileEntry = entry;
// echo/console log path specified
chrome.fileSystem.getDisplayPath(fileEntry, function(path) {
console.log('Log file path: ' + path);
});
// change file entry from read only to read/write
chrome.fileSystem.getWritableEntry(fileEntry, function(fileEntryWritable) {
// check if file is writable
chrome.fileSystem.isWritableEntry(fileEntryWritable, function(isWritable) {
if (isWritable) {
fileEntry = fileEntryWritable;
// save entry for next use
chrome.storage.local.set({'logging_file_entry': chrome.fileSystem.retainEntry(fileEntry)});
// reset sample counter in UI
$('.samples').text(0);
prepare_writer();
} else {
console.log('File appears to be read only, sorry.');
}
});
});
});
}
function prepare_writer(retaining) {
fileEntry.createWriter(function (writer) {
fileWriter = writer;
fileWriter.onerror = function (e) {
console.error(e);
// stop logging if the procedure was/is still running
if ($('a.logging').data('clicks')) $('a.logging').click();
};
fileWriter.onwriteend = function () {
$('.size').text(bytesToSize(fileWriter.length));
};
if (retaining) {
chrome.fileSystem.getDisplayPath(fileEntry, function (path) {
GUI.log(chrome.i18n.getMessage('loggingAutomaticallyRetained', [path]));
});
}
// update log size in UI on fileWriter creation
$('.size').text(bytesToSize(fileWriter.length));
}, function (e) {
// File is not readable or does not exist!
console.error(e);
if (retaining) {
fileEntry = null;
}
});
}
function append_to_file(data) {
if (fileWriter.position < fileWriter.length) {
fileWriter.seek(fileWriter.length);
}
fileWriter.write(new Blob([data + '\n'], {type: 'text/plain'}));
}
};
TABS.logging.cleanup = function (callback) {
if (callback) callback();
};

76
src/js/tabs/map.js Normal file
View file

@ -0,0 +1,76 @@
window.addEventListener('message', function (e) {
var mainWindow = e.source;
var result = '';
try {
switch(e.data.action){
case 'zoom_in':
var zoom = map.getZoom();
zoom++;
map.setZoom(zoom);
break;
case 'zoom_out':
var zoom = map.getZoom();
zoom--;
map.setZoom(zoom);
break;
case 'center':
map.setCenter(new google.maps.LatLng(e.data.lat, e.data.lon));
marker.setPosition( new google.maps.LatLng( e.data.lat, e.data.lon ) );
map.panTo( new google.maps.LatLng( e.data.lat, e.data.lon ) );
}
} catch (e) {
console.log('message error');
}
});
function loadMapScript() {
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = 'https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=true&callback=initialize';
document.head.appendChild(script);
}
window.onload = loadMapScript;
var map;
var marker;
function zoom_in(){
var zoom = map.getZoom();
zoom++;
map.setZoom(zoom);
}
function initialize() {
var mapOptions = {
zoom: 17,
zoomControl: false,
streetViewControl: false,
center: {lat: 53.570645, lng: 10.001362}
};
map = new google.maps.Map(document.getElementById('map-canvas'),mapOptions);
var image = {
url: '/images/icons/cf_icon_position.png',
scaledSize: new google.maps.Size(70, 70)
};
marker = new google.maps.Marker({
icon : image,
position: new google.maps.LatLng(53.570645, 10.001362),
map:map
});
var infowindow = new google.maps.InfoWindow();
google.maps.event.addListener(marker, 'click', function() {
infowindow.setContent('<p>Your Location: ' + map.getCenter() + '</p>');
infowindow.open(map, marker);
});
window.addEventListener('message', function(e) {
var data = e.data;
var origin = e.origin;
});
}

634
src/js/tabs/motors.js Normal file
View file

@ -0,0 +1,634 @@
'use strict';
TABS.motors = {
feature3DEnabled: false,
escProtocolIsDshot: false,
sensor: "gyro",
sensorGyroRate: 20,
sensorGyroScale: 2000,
sensorAccelRate: 20,
sensorAccelScale: 2,
sensorSelectValues: {
"gyroScale": {"50":50,"100":100,"200":200,"300":300,"400":400,"500":500,"1000":1000,"2000":2000},
"accelScale": {"0.05":0.05,"0.1":0.1,"0.2":0.2,"0.3":0.3,"0.4":0.4,"0.5":0.5,"1":1,"2":2}
},
// These are translated into proper Dshot values on the flight controller
DSHOT_DISARMED_VALUE: 1000,
DSHOT_MAX_VALUE: 2000,
DSHOT_3D_NEUTRAL: 1500
};
TABS.motors.initialize = function (callback) {
var self = this;
self.armed = false;
self.escProtocolIsDshot = false;
if (GUI.active_tab != 'motors') {
GUI.active_tab = 'motors';
}
function get_arm_status() {
MSP.send_message(MSPCodes.MSP_STATUS, false, false, load_feature_config);
}
function load_feature_config() {
MSP.send_message(MSPCodes.MSP_FEATURE_CONFIG, false, false, load_motor_3d_config);
}
function load_motor_3d_config() {
MSP.send_message(MSPCodes.MSP_MOTOR_3D_CONFIG, false, false, load_esc_protocol);
}
function load_esc_protocol() {
MSP.send_message(MSPCodes.MSP_ADVANCED_CONFIG, false, false, load_motor_data);
}
function load_motor_data() {
MSP.send_message(MSPCodes.MSP_MOTOR, false, false, load_html);
}
function load_html() {
$('#content').load("./tabs/motors.html", process_html);
}
// Get information from Betaflight
if (semver.gte(CONFIG.apiVersion, "1.36.0")) {
// BF 3.2.0+
MSP.send_message(MSPCodes.MSP_MOTOR_CONFIG, false, false, get_arm_status);
} else {
// BF 3.1.x or older
MSP.send_message(MSPCodes.MSP_MISC, false, false, get_arm_status);
}
function update_arm_status() {
self.armed = bit_check(CONFIG.mode, 0);
}
function initSensorData() {
for (var i = 0; i < 3; i++) {
SENSOR_DATA.accelerometer[i] = 0;
SENSOR_DATA.gyroscope[i] = 0;
}
}
function initDataArray(length) {
var data = new Array(length);
for (var i = 0; i < length; i++) {
data[i] = [];
data[i].min = -1;
data[i].max = 1;
}
return data;
}
function addSampleToData(data, sampleNumber, sensorData) {
for (var i = 0; i < data.length; i++) {
var dataPoint = sensorData[i];
data[i].push([sampleNumber, dataPoint]);
if (dataPoint < data[i].min) {
data[i].min = dataPoint;
}
if (dataPoint > data[i].max) {
data[i].max = dataPoint;
}
}
while (data[0].length > 300) {
for (i = 0; i < data.length; i++) {
data[i].shift();
}
}
return sampleNumber + 1;
}
var margin = {top: 20, right: 30, bottom: 10, left: 20};
function updateGraphHelperSize(helpers) {
helpers.width = helpers.targetElement.width() - margin.left - margin.right;
helpers.height = helpers.targetElement.height() - margin.top - margin.bottom;
helpers.widthScale.range([0, helpers.width]);
helpers.heightScale.range([helpers.height, 0]);
helpers.xGrid.tickSize(-helpers.height, 0, 0);
helpers.yGrid.tickSize(-helpers.width, 0, 0);
}
function initGraphHelpers(selector, sampleNumber, heightDomain) {
var helpers = {selector: selector, targetElement: $(selector), dynamicHeightDomain: !heightDomain};
helpers.widthScale = d3.scale.linear()
.clamp(true)
.domain([(sampleNumber - 299), sampleNumber]);
helpers.heightScale = d3.scale.linear()
.clamp(true)
.domain(heightDomain || [1, -1]);
helpers.xGrid = d3.svg.axis();
helpers.yGrid = d3.svg.axis();
updateGraphHelperSize(helpers);
helpers.xGrid
.scale(helpers.widthScale)
.orient("bottom")
.ticks(5)
.tickFormat("");
helpers.yGrid
.scale(helpers.heightScale)
.orient("left")
.ticks(5)
.tickFormat("");
helpers.xAxis = d3.svg.axis()
.scale(helpers.widthScale)
.ticks(5)
.orient("bottom")
.tickFormat(function (d) {return d;});
helpers.yAxis = d3.svg.axis()
.scale(helpers.heightScale)
.ticks(5)
.orient("left")
.tickFormat(function (d) {return d;});
helpers.line = d3.svg.line()
.x(function (d) { return helpers.widthScale(d[0]); })
.y(function (d) { return helpers.heightScale(d[1]); });
return helpers;
}
function drawGraph(graphHelpers, data, sampleNumber) {
var svg = d3.select(graphHelpers.selector);
if (graphHelpers.dynamicHeightDomain) {
var limits = [];
$.each(data, function (idx, datum) {
limits.push(datum.min);
limits.push(datum.max);
});
graphHelpers.heightScale.domain(d3.extent(limits));
}
graphHelpers.widthScale.domain([(sampleNumber - 299), sampleNumber]);
svg.select(".x.grid").call(graphHelpers.xGrid);
svg.select(".y.grid").call(graphHelpers.yGrid);
svg.select(".x.axis").call(graphHelpers.xAxis);
svg.select(".y.axis").call(graphHelpers.yAxis);
var group = svg.select("g.data");
var lines = group.selectAll("path").data(data, function (d, i) {return i;});
lines.enter().append("path").attr("class", "line");
lines.attr('d', graphHelpers.line);
}
function update_model(mixer) {
var reverse = "";
if (semver.gte(CONFIG.apiVersion, "1.36.0")) {
reverse = MIXER_CONFIG.reverseMotorDir ? "_reversed" : "";
}
$('.mixerPreview img').attr('src', './resources/motor_order/' + mixerList[mixer - 1].image + reverse + '.svg');
}
function process_html() {
// translate to user-selected language
localize();
update_arm_status();
self.feature3DEnabled = FEATURE_CONFIG.features.isEnabled('3D');
if (PID_ADVANCED_CONFIG.fast_pwm_protocol >= TABS.configuration.DSHOT_PROTOCOL_MIN_VALUE) {
self.escProtocolIsDshot = true;
} else {
self.escProtocolIsDshot = false;
}
$('#motorsEnableTestMode').prop('checked', false);
update_model(MIXER_CONFIG.mixer);
// Always start with default/empty sensor data array, clean slate all
initSensorData();
// Setup variables
var samples_gyro_i = 0,
gyro_data = initDataArray(3),
gyro_helpers = initGraphHelpers('#graph', samples_gyro_i, [-2, 2]),
gyro_max_read = [0, 0, 0];
var samples_accel_i = 0,
accel_data = initDataArray(3),
accel_helpers = initGraphHelpers('#graph', samples_accel_i, [-2, 2]),
accel_max_read = [0, 0, 0],
accel_offset = [0, 0, 0],
accel_offset_established = false;
var raw_data_text_ements = {
x: [],
y: [],
z: [],
rms: []
};
$('.plot_control .x, .plot_control .y, .plot_control .z, .plot_control .rms').each(function () {
var el = $(this);
if (el.hasClass('x')) {
raw_data_text_ements.x.push(el);
} else if (el.hasClass('y')) {
raw_data_text_ements.y.push(el);
} else if (el.hasClass('z')) {
raw_data_text_ements.z.push(el);
} else if (el.hasClass('rms')) {
raw_data_text_ements.rms.push(el);
}
});
// set refresh speeds according to configuration saved in storage
chrome.storage.local.get(['motors_tab_sensor_settings', 'motors_tab_gyro_settings', 'motors_tab_accel_settings'], function (result) {
if (result.motors_tab_sensor_settings) {
var sensor = result.motors_tab_sensor_settings.sensor;
$('.tab-motors select[name="sensor_choice"]').val(result.motors_tab_sensor_settings.sensor);
}
if (result.motors_tab_gyro_settings) {
TABS.motors.sensorGyroRate = result.motors_tab_gyro_settings.rate;
TABS.motors.sensorGyroScale = result.motors_tab_gyro_settings.scale;
}
if (result.motors_tab_accel_settings) {
TABS.motors.sensorAccelRate = result.motors_tab_accel_settings.rate;
TABS.motors.sensorAccelScale = result.motors_tab_accel_settings.scale;
}
$('.tab-motors .sensor select:first').change();
});
function loadScaleSelector(selectorValues, selectedValue) {
$('.tab-motors select[name="scale"]').find('option').remove();
$.each(selectorValues, function(key, val) {
$('.tab-motors select[name="scale"]').append(new Option(key, val));
});
$('.tab-motors select[name="scale"]').val(selectedValue);
}
function selectRefresh(refreshValue){
$('.tab-motors select[name="rate"]').val(refreshValue);
}
$('.tab-motors .sensor select').change(function(){
TABS.motors.sensor = $('.tab-motors select[name="sensor_choice"]').val()
chrome.storage.local.set({'motors_tab_sensor_settings': {'sensor': TABS.motors.sensor}});
switch(TABS.motors.sensor){
case "gyro":
loadScaleSelector(TABS.motors.sensorSelectValues.gyroScale,
TABS.motors.sensorGyroScale);
selectRefresh(TABS.motors.sensorGyroRate);
break;
case "accel":
loadScaleSelector(TABS.motors.sensorSelectValues.accelScale,
TABS.motors.sensorAccelScale);
selectRefresh(TABS.motors.sensorAccelRate);
break;
}
$('.tab-motors .rate select:first').change();
});
$('.tab-motors .rate select, .tab-motors .scale select').change(function () {
var rate = parseInt($('.tab-motors select[name="rate"]').val(), 10);
var scale = parseFloat($('.tab-motors select[name="scale"]').val());
GUI.interval_kill_all(['motor_and_status_pull']);
switch(TABS.motors.sensor) {
case "gyro":
chrome.storage.local.set({'motors_tab_gyro_settings': {'rate': rate, 'scale': scale}});
TABS.motors.sensorGyroRate = rate;
TABS.motors.sensorGyroScale = scale;
gyro_helpers = initGraphHelpers('#graph', samples_gyro_i, [-scale, scale]);
GUI.interval_add('IMU_pull', function imu_data_pull() {
MSP.send_message(MSPCodes.MSP_RAW_IMU, false, false, update_gyro_graph);
}, rate, true);
break;
case "accel":
chrome.storage.local.set({'motors_tab_accel_settings': {'rate': rate, 'scale': scale}});
TABS.motors.sensorAccelRate = rate;
TABS.motors.sensorAccelScale = scale;
accel_helpers = initGraphHelpers('#graph', samples_accel_i, [-scale, scale]);
GUI.interval_add('IMU_pull', function imu_data_pull() {
MSP.send_message(MSPCodes.MSP_RAW_IMU, false, false, update_accel_graph);
}, rate, true);
break;
}
function update_accel_graph() {
if (!accel_offset_established) {
for (var i = 0; i < 3; i++) {
accel_offset[i] = SENSOR_DATA.accelerometer[i] * -1;
}
accel_offset_established = true;
}
var accel_with_offset = [
accel_offset[0] + SENSOR_DATA.accelerometer[0],
accel_offset[1] + SENSOR_DATA.accelerometer[1],
accel_offset[2] + SENSOR_DATA.accelerometer[2]
];
updateGraphHelperSize(accel_helpers);
samples_accel_i = addSampleToData(accel_data, samples_accel_i, accel_with_offset);
drawGraph(accel_helpers, accel_data, samples_accel_i);
for (var i = 0; i < 3; i++) {
if (Math.abs(accel_with_offset[i]) > Math.abs(accel_max_read[i])) accel_max_read[i] = accel_with_offset[i];
}
computeAndUpdate(accel_with_offset, accel_data, accel_max_read);
}
function update_gyro_graph() {
var gyro = [
SENSOR_DATA.gyroscope[0],
SENSOR_DATA.gyroscope[1],
SENSOR_DATA.gyroscope[2]
];
updateGraphHelperSize(gyro_helpers);
samples_gyro_i = addSampleToData(gyro_data, samples_gyro_i, gyro);
drawGraph(gyro_helpers, gyro_data, samples_gyro_i);
for (var i = 0; i < 3; i++) {
if (Math.abs(gyro[i]) > Math.abs(gyro_max_read[i])) gyro_max_read[i] = gyro[i];
}
computeAndUpdate(gyro, gyro_data, gyro_max_read);
}
function computeAndUpdate(sensor_data, data, max_read) {
var sum = 0.0;
for (var j = 0, jlength = data.length; j < jlength; j++) {
for (var k = 0, klength = data[j].length; k < klength; k++){
sum += data[j][k][1]*data[j][k][1];
}
}
var rms = Math.sqrt(sum/(data[0].length+data[1].length+data[2].length));
raw_data_text_ements.x[0].text(sensor_data[0].toFixed(2) + ' (' + max_read[0].toFixed(2) + ')');
raw_data_text_ements.y[0].text(sensor_data[1].toFixed(2) + ' (' + max_read[1].toFixed(2) + ')');
raw_data_text_ements.z[0].text(sensor_data[2].toFixed(2) + ' (' + max_read[2].toFixed(2) + ')');
raw_data_text_ements.rms[0].text(rms.toFixed(4));
}
});
$('a.reset_max').click(function () {
gyro_max_read = [0, 0, 0];
accel_max_read = [0, 0, 0];
accel_offset_established = false;
});
var number_of_valid_outputs = (MOTOR_DATA.indexOf(0) > -1) ? MOTOR_DATA.indexOf(0) : 8;
var rangeMin;
var rangeMax;
var neutral3d;
if (self.escProtocolIsDshot) {
rangeMin = self.DSHOT_DISARMED_VALUE;
rangeMax = self.DSHOT_MAX_VALUE;
neutral3d = self.DSHOT_3D_NEUTRAL;
} else {
rangeMin = MOTOR_CONFIG.mincommand;
rangeMax = MOTOR_CONFIG.maxthrottle;
//Arbitrary sanity checks
//Note: values may need to be revisited
neutral3d = (MOTOR_3D_CONFIG.neutral > 1575 || MOTOR_3D_CONFIG.neutral < 1425) ? 1500 : MOTOR_3D_CONFIG.neutral;
}
var motors_wrapper = $('.motors .bar-wrapper'),
servos_wrapper = $('.servos .bar-wrapper');
for (var i = 0; i < 8; i++) {
motors_wrapper.append('\
<div class="m-block motor-' + i + '">\
<div class="meter-bar">\
<div class="label"></div>\
<div class="indicator">\
<div class="label">\
<div class="label"></div>\
</div>\
</div>\
</div>\
</div>\
');
servos_wrapper.append('\
<div class="m-block servo-' + (7 - i) + '">\
<div class="meter-bar">\
<div class="label"></div>\
<div class="indicator">\
<div class="label">\
<div class="label"></div>\
</div>\
</div>\
</div>\
</div>\
');
}
$('div.sliders input').prop('min', rangeMin)
.prop('max', rangeMax);
$('div.values li:not(:last)').text(rangeMin);
// UI hooks
function setSlidersDefault() {
// change all values to default
if (self.feature3DEnabled) {
$('div.sliders input').val(neutral3d);
} else {
$('div.sliders input').val(rangeMin);
}
}
function setSlidersEnabled(isEnabled) {
if (isEnabled && !self.armed) {
$('div.sliders input').slice(0, number_of_valid_outputs).prop('disabled', false);
// unlock master slider
$('div.sliders input:last').prop('disabled', false);
} else {
setSlidersDefault();
// disable sliders / min max
$('div.sliders input').prop('disabled', true);
}
$('div.sliders input').trigger('input');
}
setSlidersDefault();
$('#motorsEnableTestMode').change(function () {
var enabled = $(this).is(':checked');
setSlidersEnabled(enabled);
$('div.sliders input').trigger('input');
mspHelper.setArmingEnabled(enabled);
}).change();
var buffering_set_motor = [],
buffer_delay = false;
$('div.sliders input:not(.master)').on('input', function () {
var index = $(this).index(),
buffer = [];
$('div.values li').eq(index).text($(this).val());
for (var i = 0; i < 8; i++) {
var val = parseInt($('div.sliders input').eq(i).val());
buffer.push16(val);
}
buffering_set_motor.push(buffer);
if (!buffer_delay) {
buffer_delay = setTimeout(function () {
buffer = buffering_set_motor.pop();
MSP.send_message(MSPCodes.MSP_SET_MOTOR, buffer);
buffering_set_motor = [];
buffer_delay = false;
}, 10);
}
});
$('div.sliders input.master').on('input', function () {
var val = $(this).val();
$('div.sliders input:not(:disabled, :last)').val(val);
$('div.values li:not(:last)').slice(0, number_of_valid_outputs).text(val);
$('div.sliders input:not(:last):first').trigger('input');
});
// check if motors are already spinning
var motors_running = false;
for (var i = 0; i < number_of_valid_outputs; i++) {
if (!self.feature3DEnabled) {
if (MOTOR_DATA[i] > rangeMin) {
motors_running = true;
}
} else {
if ((MOTOR_DATA[i] < MOTOR_3D_CONFIG.deadband3d_low) || (MOTOR_DATA[i] > MOTOR_3D_CONFIG.deadband3d_high)) {
motors_running = true;
}
}
}
if (motors_running) {
$('#motorsEnableTestMode').prop('checked', true).change();
// motors are running adjust sliders to current values
var sliders = $('div.sliders input:not(.master)');
var master_value = MOTOR_DATA[0];
for (var i = 0; i < MOTOR_DATA.length; i++) {
if (MOTOR_DATA[i] > 0) {
sliders.eq(i).val(MOTOR_DATA[i]);
if (master_value != MOTOR_DATA[i]) {
master_value = false;
}
}
}
// only fire events when all values are set
sliders.trigger('input');
// slide master slider if condition is valid
if (master_value) {
$('div.sliders input.master').val(master_value)
.trigger('input');
}
}
// data pulling functions used inside interval timer
function get_status() {
// status needed for arming flag
MSP.send_message(MSPCodes.MSP_STATUS, false, false, get_motor_data);
}
function get_motor_data() {
MSP.send_message(MSPCodes.MSP_MOTOR, false, false, get_servo_data);
}
function get_servo_data() {
MSP.send_message(MSPCodes.MSP_SERVO, false, false, update_ui);
}
var full_block_scale = rangeMax - rangeMin;
function update_ui() {
var previousArmState = self.armed;
var block_height = $('div.m-block:first').height();
for (var i = 0; i < MOTOR_DATA.length; i++) {
var motorValue = MOTOR_DATA[i];
var barHeight = motorValue - rangeMin,
margin_top = block_height - (barHeight * (block_height / full_block_scale)).clamp(0, block_height),
height = (barHeight * (block_height / full_block_scale)).clamp(0, block_height),
color = parseInt(barHeight * 0.009);
$('.motor-' + i + ' .label', motors_wrapper).text(motorValue);
$('.motor-' + i + ' .indicator', motors_wrapper).css({
'margin-top' : margin_top + 'px',
'height' : height + 'px',
'background-color' : 'rgba(255,187,0,1.'+ color +')'
});
}
// servo indicators are still using old (not flexible block scale), it will be changed in the future accordingly
for (var i = 0; i < SERVO_DATA.length; i++) {
var data = SERVO_DATA[i] - 1000,
margin_top = block_height - (data * (block_height / 1000)).clamp(0, block_height),
height = (data * (block_height / 1000)).clamp(0, block_height),
color = parseInt(data * 0.009);
$('.servo-' + i + ' .label', servos_wrapper).text(SERVO_DATA[i]);
$('.servo-' + i + ' .indicator', servos_wrapper).css({'margin-top' : margin_top + 'px', 'height' : height + 'px', 'background-color' : 'rgba(255,187,0,1'+ color +')'});
}
//keep the following here so at least we get a visual cue of our motor setup
update_arm_status();
if (previousArmState != self.armed) {
console.log('arm state change detected');
$('#motorsEnableTestMode').change();
}
}
// enable Status and Motor data pulling
GUI.interval_add('motor_and_status_pull', get_status, 50, true);
GUI.content_ready(callback);
}
};
TABS.motors.cleanup = function (callback) {
if (callback) callback();
};

View file

@ -0,0 +1,509 @@
'use strict';
var
sdcardTimer;
TABS.onboard_logging = {
blockSize: 128,
BLOCK_SIZE: 4096,
VCP_BLOCK_SIZE_3_0: 512,
VCP_BLOCK_SIZE: 4096
};
TABS.onboard_logging.initialize = function (callback) {
var
self = this,
saveCancelled, eraseCancelled;
if (GUI.active_tab !== 'onboard_logging') {
GUI.active_tab = 'onboard_logging';
}
if (CONFIGURATOR.connectionValid) {
MSP.send_message(MSPCodes.MSP_FEATURE_CONFIG, false, false, function() {
MSP.send_message(MSPCodes.MSP_DATAFLASH_SUMMARY, false, false, function() {
MSP.send_message(MSPCodes.MSP_SDCARD_SUMMARY, false, false, function() {
MSP.send_message(MSPCodes.MSP_BLACKBOX_CONFIG, false, false, function() {
MSP.send_message(MSPCodes.MSP_ADVANCED_CONFIG, false, false, function() {
MSP.send_message(MSPCodes.MSP_NAME, false, false, load_html);
});
});
});
});
});
}
function gcd(a, b) {
if (b === 0)
return a;
return gcd(b, a % b);
}
function save_to_eeprom() {
MSP.send_message(MSPCodes.MSP_EEPROM_WRITE, false, false, reboot);
}
function reboot() {
GUI.log(chrome.i18n.getMessage('configurationEepromSaved'));
GUI.tab_switch_cleanup(function() {
MSP.send_message(MSPCodes.MSP_SET_REBOOT, false, false, reinitialize);
});
}
function reinitialize() {
GUI.log(chrome.i18n.getMessage('deviceRebooting'));
if (BOARD.find_board_definition(CONFIG.boardIdentifier).vcp) { // VCP-based flight controls may crash old drivers, we catch and reconnect
$('a.connect').click();
GUI.timeout_add('start_connection',function start_connection() {
$('a.connect').click();
},2000);
} else {
GUI.timeout_add('waiting_for_bootup', function waiting_for_bootup() {
MSP.send_message(MSPCodes.MSP_STATUS, false, false, function() {
GUI.log(chrome.i18n.getMessage('deviceReady'));
TABS.onboard_logging.initialize(false, $('#content').scrollTop());
});
},1500); // 1500 ms seems to be just the right amount of delay to prevent data request timeouts
}
}
function load_html() {
$('#content').load("./tabs/onboard_logging.html", function() {
// translate to user-selected language
localize();
var
dataflashPresent = DATAFLASH.totalSize > 0,
blackboxSupport;
/*
* Pre-1.11.0 firmware supported DATAFLASH API (on targets with SPI flash) but not the BLACKBOX config API.
*
* The best we can do on those targets is check the BLACKBOX feature bit to identify support for Blackbox instead.
*/
if ((BLACKBOX.supported || DATAFLASH.supported) && (semver.gte(CONFIG.apiVersion, "1.33.0") || FEATURE_CONFIG.features.isEnabled('BLACKBOX'))) {
blackboxSupport = 'yes';
} else {
blackboxSupport = 'no';
}
$(".tab-onboard_logging")
.addClass("serial-supported")
.toggleClass("dataflash-supported", DATAFLASH.supported)
.toggleClass("dataflash-present", dataflashPresent)
.toggleClass("sdcard-supported", SDCARD.supported)
.toggleClass("blackbox-config-supported", BLACKBOX.supported)
.toggleClass("blackbox-supported", blackboxSupport === 'yes')
.toggleClass("blackbox-maybe-supported", blackboxSupport === 'maybe')
.toggleClass("blackbox-unsupported", blackboxSupport === 'no');
if (dataflashPresent) {
// UI hooks
$('.tab-onboard_logging a.erase-flash').click(ask_to_erase_flash);
$('.tab-onboard_logging a.erase-flash-confirm').click(flash_erase);
$('.tab-onboard_logging a.erase-flash-cancel').click(flash_erase_cancel);
$('.tab-onboard_logging a.save-flash').click(flash_save_begin);
$('.tab-onboard_logging a.save-flash-cancel').click(flash_save_cancel);
$('.tab-onboard_logging a.save-flash-dismiss').click(dismiss_saving_dialog);
}
var deviceSelect = $(".blackboxDevice select");
var loggingRatesSelect = $(".blackboxRate select");
if (BLACKBOX.supported) {
$(".tab-onboard_logging a.save-settings").click(function() {
if (semver.gte(CONFIG.apiVersion, "1.36.0")) {
BLACKBOX.blackboxPDenom = parseInt(loggingRatesSelect.val(), 10);
} else {
var rate = loggingRatesSelect.val().split('/');
BLACKBOX.blackboxRateNum = parseInt(rate[0], 10);
BLACKBOX.blackboxRateDenom = parseInt(rate[1], 10);
}
BLACKBOX.blackboxDevice = parseInt(deviceSelect.val(), 10);
MSP.send_message(MSPCodes.MSP_SET_BLACKBOX_CONFIG, mspHelper.crunch(MSPCodes.MSP_SET_BLACKBOX_CONFIG), false, save_to_eeprom);
});
}
populateLoggingRates(loggingRatesSelect);
populateDevices(deviceSelect);
deviceSelect.change(function() {
if ($(this).val() === "0") {
$("div.blackboxRate").hide();
} else {
$("div.blackboxRate").show();
}
}).change();
update_html();
GUI.content_ready(callback);
});
}
function populateDevices(deviceSelect) {
deviceSelect.empty();
if (semver.gte(CONFIG.apiVersion, "1.33.0")) {
deviceSelect.append('<option value="0">' + chrome.i18n.getMessage('blackboxLoggingNone') + '</option>');
if (DATAFLASH.supported) {
deviceSelect.append('<option value="1">' + chrome.i18n.getMessage('blackboxLoggingFlash') + '</option>');
}
if (SDCARD.supported) {
deviceSelect.append('<option value="2">' + chrome.i18n.getMessage('blackboxLoggingSdCard') + '</option>');
}
deviceSelect.append('<option value="3">' + chrome.i18n.getMessage('blackboxLoggingSerial') + '</option>');
} else {
deviceSelect.append('<option value="0">' + chrome.i18n.getMessage('blackboxLoggingSerial') + '</option>');
if (DATAFLASH.ready) {
deviceSelect.append('<option value="1">' + chrome.i18n.getMessage('blackboxLoggingFlash') + '</option>');
}
if (SDCARD.supported) {
deviceSelect.append('<option value="2">' + chrome.i18n.getMessage('blackboxLoggingSdCard') + '</option>');
}
}
deviceSelect.val(BLACKBOX.blackboxDevice);
}
function populateLoggingRates(loggingRatesSelect) {
// Offer a reasonable choice of logging rates (if people want weird steps they can use CLI)
var loggingRates = [];
var pidRate = 8000 / PID_ADVANCED_CONFIG.gyro_sync_denom / PID_ADVANCED_CONFIG.pid_process_denom;
if (semver.gte(CONFIG.apiVersion, "1.36.0")) {
loggingRates = [
{text: "Disabled", hz: 0, p_denom: 0},
{text: "500 Hz", hz: 500, p_denom: 16},
{text: "1 kHz", hz: 1000, p_denom: 32},
{text: "1.5 kHz", hz: 1500, p_denom: 48},
{text: "2 kHz", hz: 2000, p_denom: 64},
{text: "4 kHz", hz: 4000, p_denom: 128},
{text: "8 kHz", hz: 8000, p_denom: 256},
{text: "16 kHz", hz: 16000, p_denom: 512},
{text: "32 kHz", hz: 32000, p_denom: 1024},
];
$.each(loggingRates, function(index, item) {
if (pidRate >= item.hz || item.hz == 0) {
loggingRatesSelect.append(new Option(item.text, item.p_denom));
}
});
loggingRatesSelect.val(BLACKBOX.blackboxPDenom);
}
else {
loggingRates = [
{num: 1, denom: 1},
{num: 1, denom: 2},
{num: 1, denom: 3},
{num: 1, denom: 4},
{num: 1, denom: 5},
{num: 1, denom: 6},
{num: 1, denom: 7},
{num: 1, denom: 8},
{num: 1, denom: 16},
{num: 1, denom: 32}
];
for (var i = 0; i < loggingRates.length; i++) {
var loggingRate = Math.round(pidRate / loggingRates[i].denom);
var loggingRateUnit = " Hz";
if (loggingRate !== Infinity) {
if (gcd(loggingRate, 1000) === 1000) {
loggingRate /= 1000;
loggingRateUnit = " KHz";
}
}
loggingRatesSelect.append('<option value="' + loggingRates[i].num + '/' + loggingRates[i].denom + '">'
+ loggingRate + loggingRateUnit + ' (' + Math.round(loggingRates[i].num / loggingRates[i].denom * 100) + '%)</option>');
}
loggingRatesSelect.val(BLACKBOX.blackboxRateNum + '/' + BLACKBOX.blackboxRateDenom);
}
}
function formatFilesizeKilobytes(kilobytes) {
if (kilobytes < 1024) {
return Math.round(kilobytes) + "kB";
}
var
megabytes = kilobytes / 1024,
gigabytes;
if (megabytes < 900) {
return megabytes.toFixed(1) + "MB";
} else {
gigabytes = megabytes / 1024;
return gigabytes.toFixed(1) + "GB";
}
}
function formatFilesizeBytes(bytes) {
if (bytes < 1024) {
return bytes + "B";
}
return formatFilesizeKilobytes(bytes / 1024);
}
function update_bar_width(bar, value, total, label, valuesAreKilobytes) {
if (value > 0) {
bar.css({
width: (value / total * 100) + "%",
display: 'block'
});
$("div", bar).text((label ? label + " " : "") + (valuesAreKilobytes ? formatFilesizeKilobytes(value) : formatFilesizeBytes(value)));
} else {
bar.css({
display: 'none'
});
}
}
function update_html() {
update_bar_width($(".tab-onboard_logging .dataflash-used"), DATAFLASH.usedSize, DATAFLASH.totalSize, chrome.i18n.getMessage('dataflashUsedSpace'), false);
update_bar_width($(".tab-onboard_logging .dataflash-free"), DATAFLASH.totalSize - DATAFLASH.usedSize, DATAFLASH.totalSize, chrome.i18n.getMessage('dataflashFreeSpace'), false);
update_bar_width($(".tab-onboard_logging .sdcard-other"), SDCARD.totalSizeKB - SDCARD.freeSizeKB, SDCARD.totalSizeKB, chrome.i18n.getMessage('dataflashUnavSpace'), true);
update_bar_width($(".tab-onboard_logging .sdcard-free"), SDCARD.freeSizeKB, SDCARD.totalSizeKB, chrome.i18n.getMessage('dataflashLogsSpace'), true);
$(".btn a.erase-flash, .btn a.save-flash").toggleClass("disabled", DATAFLASH.usedSize === 0);
$(".tab-onboard_logging")
.toggleClass("sdcard-error", SDCARD.state === MSP.SDCARD_STATE_FATAL)
.toggleClass("sdcard-initializing", SDCARD.state === MSP.SDCARD_STATE_CARD_INIT || SDCARD.state === MSP.SDCARD_STATE_FS_INIT)
.toggleClass("sdcard-ready", SDCARD.state === MSP.SDCARD_STATE_READY);
switch (SDCARD.state) {
case MSP.SDCARD_STATE_NOT_PRESENT:
$(".sdcard-status").text(chrome.i18n.getMessage('sdcardStatusNoCard'));
break;
case MSP.SDCARD_STATE_FATAL:
$(".sdcard-status").html(chrome.i18n.getMessage('sdcardStatusReboot'));
break;
case MSP.SDCARD_STATE_READY:
$(".sdcard-status").text(chrome.i18n.getMessage('sdcardStatusReady'));
break;
case MSP.SDCARD_STATE_CARD_INIT:
$(".sdcard-status").text(chrome.i18n.getMessage('sdcardStatusStarting'));
break;
case MSP.SDCARD_STATE_FS_INIT:
$(".sdcard-status").text(chrome.i18n.getMessage('sdcardStatusFileSystem'));
break;
default:
$(".sdcard-status").text(chrome.i18n.getMessage('sdcardStatusUnknown',[SDCARD.state]));
}
if (SDCARD.supported && !sdcardTimer) {
// Poll for changes in SD card status
sdcardTimer = setTimeout(function() {
sdcardTimer = false;
if (CONFIGURATOR.connectionValid) {
MSP.send_message(MSPCodes.MSP_SDCARD_SUMMARY, false, false, function() {
update_html();
});
}
}, 2000);
}
}
// IO related methods
function flash_save_cancel() {
saveCancelled = true;
}
function show_saving_dialog() {
$(".dataflash-saving progress").attr("value", 0);
saveCancelled = false;
$(".dataflash-saving").removeClass("done");
$(".dataflash-saving")[0].showModal();
}
function dismiss_saving_dialog() {
$(".dataflash-saving")[0].close();
}
function mark_saving_dialog_done(startTime, totalBytes, totalBytesCompressed) {
var totalTime = (new Date().getTime() - startTime) / 1000;
console.log('Received ' + totalBytes + ' bytes in ' + totalTime.toFixed(2) + 's ('
+ (totalBytes / totalTime / 1024).toFixed(2) + 'kB / s) with block size ' + self.blockSize + '.');
if (totalBytesCompressed) {
console.log('Compressed into', totalBytesCompressed, 'bytes with mean compression factor of', totalBytes / totalBytesCompressed);
}
$(".dataflash-saving").addClass("done");
}
function flash_update_summary(onDone) {
MSP.send_message(MSPCodes.MSP_DATAFLASH_SUMMARY, false, false, function() {
update_html();
if (onDone) {
onDone();
}
});
}
function flash_save_begin() {
if (GUI.connected_to) {
if (BOARD.find_board_definition(CONFIG.boardIdentifier).vcp) {
if (semver.gte(CONFIG.apiVersion, "1.31.0")) {
self.blockSize = self.VCP_BLOCK_SIZE;
} else {
self.blockSize = self.VCP_BLOCK_SIZE_3_0;
}
} else {
self.blockSize = self.BLOCK_SIZE;
}
// Begin by refreshing the occupied size in case it changed while the tab was open
flash_update_summary(function() {
var maxBytes = DATAFLASH.usedSize;
prepare_file(function(fileWriter) {
var nextAddress = 0;
var totalBytesCompressed = 0;
show_saving_dialog();
function onChunkRead(chunkAddress, chunkDataView, bytesCompressed) {
if (chunkDataView !== null) {
// Did we receive any data?
if (chunkDataView.byteLength > 0) {
nextAddress += chunkDataView.byteLength;
totalBytesCompressed += bytesCompressed;
$(".dataflash-saving progress").attr("value", nextAddress / maxBytes * 100);
var blob = new Blob([chunkDataView]);
fileWriter.onwriteend = function(e) {
if (saveCancelled || nextAddress >= maxBytes) {
if (saveCancelled) {
dismiss_saving_dialog();
} else {
mark_saving_dialog_done(startTime, nextAddress, totalBytesCompressed);
}
} else {
mspHelper.dataflashRead(nextAddress, self.blockSize, onChunkRead);
}
};
fileWriter.write(blob);
} else {
// A zero-byte block indicates end-of-file, so we're done
mark_saving_dialog_done(startTime, nextAddress, totalBytesCompressed);
}
} else {
// There was an error with the received block (address didn't match the one we asked for), retry
mspHelper.dataflashRead(nextAddress, self.blockSize, onChunkRead);
}
}
var startTime = new Date().getTime();
// Fetch the initial block
mspHelper.dataflashRead(nextAddress, self.blockSize, onChunkRead);
});
});
}
}
function prepare_file(onComplete) {
var prefix = 'BLACKBOX_LOG';
var suffix = 'BBL';
var filename = generateFilename(prefix, suffix);
chrome.fileSystem.chooseEntry({type: 'saveFile', suggestedName: filename,
accepts: [{extensions: [suffix]}]}, function(fileEntry) {
var error = chrome.runtime.lastError;
if (error) {
console.error(error.message);
if (error.message !== "User cancelled") {
GUI.log(chrome.i18n.getMessage('dataflashFileWriteFailed'));
}
return;
}
// echo/console log path specified
chrome.fileSystem.getDisplayPath(fileEntry, function(path) {
console.log('Dataflash dump file path: ' + path);
});
fileEntry.createWriter(function (fileWriter) {
fileWriter.onerror = function (e) {
console.error(e);
// stop logging if the procedure was/is still running
};
onComplete(fileWriter);
}, function (e) {
// File is not readable or does not exist!
console.error(e);
GUI.log(chrome.i18n.getMessage('dataflashFileWriteFailed'));
});
});
}
function ask_to_erase_flash() {
eraseCancelled = false;
$(".dataflash-confirm-erase").removeClass('erasing');
$(".dataflash-confirm-erase")[0].showModal();
}
function poll_for_erase_completion() {
flash_update_summary(function() {
if (CONFIGURATOR.connectionValid && !eraseCancelled) {
if (DATAFLASH.ready) {
$(".dataflash-confirm-erase")[0].close();
} else {
setTimeout(poll_for_erase_completion, 500);
}
}
});
}
function flash_erase() {
$(".dataflash-confirm-erase").addClass('erasing');
MSP.send_message(MSPCodes.MSP_DATAFLASH_ERASE, false, false, poll_for_erase_completion);
}
function flash_erase_cancel() {
eraseCancelled = true;
$(".dataflash-confirm-erase")[0].close();
}
};
TABS.onboard_logging.cleanup = function (callback) {
if (sdcardTimer) {
clearTimeout(sdcardTimer);
sdcardTimer = false;
}
if (callback) {
callback();
}
};

1815
src/js/tabs/osd.js Executable file

File diff suppressed because it is too large Load diff

1350
src/js/tabs/pid_tuning.js Executable file

File diff suppressed because it is too large Load diff

349
src/js/tabs/ports.js Normal file
View file

@ -0,0 +1,349 @@
'use strict';
TABS.ports = {};
TABS.ports.initialize = function (callback, scrollPosition) {
var self = this;
var board_definition = {};
var functionRules = [
{name: 'MSP', groups: ['configuration', 'msp'], maxPorts: 2},
{name: 'GPS', groups: ['sensors'], maxPorts: 1},
{name: 'TELEMETRY_FRSKY', groups: ['telemetry'], sharableWith: ['msp'], notSharableWith: ['peripherals'], maxPorts: 1},
{name: 'TELEMETRY_HOTT', groups: ['telemetry'], sharableWith: ['msp'], notSharableWith: ['peripherals'], maxPorts: 1},
{name: 'TELEMETRY_SMARTPORT', groups: ['telemetry'], maxPorts: 1},
{name: 'RX_SERIAL', groups: ['rx'], maxPorts: 1},
{name: 'BLACKBOX', groups: ['peripherals'], sharableWith: ['msp'], notSharableWith: ['telemetry'], maxPorts: 1}
];
if (semver.gte(CONFIG.apiVersion, "1.15.0")) {
var ltmFunctionRule = {name: 'TELEMETRY_LTM', groups: ['telemetry'], sharableWith: ['msp'], notSharableWith: ['peripherals'], maxPorts: 1};
functionRules.push(ltmFunctionRule);
} else {
var mspFunctionRule = {name: 'TELEMETRY_MSP', groups: ['telemetry'], sharableWith: ['msp'], notSharableWith: ['peripherals'], maxPorts: 1};
functionRules.push(mspFunctionRule);
}
if (semver.gte(CONFIG.apiVersion, "1.18.0")) {
var mavlinkFunctionRule = {name: 'TELEMETRY_MAVLINK', groups: ['telemetry'], sharableWith: ['msp'], notSharableWith: ['peripherals'], maxPorts: 1};
functionRules.push(mavlinkFunctionRule);
}
if (semver.gte(CONFIG.apiVersion, "1.31.0")) {
functionRules.push({ name: 'ESC_SENSOR', groups: ['sensors'], maxPorts: 1 });
functionRules.push({ name: 'TBS_SMARTAUDIO', groups: ['peripherals'], maxPorts: 1 });
}
if (semver.gte(CONFIG.apiVersion, "1.27.0")) {
functionRules.push({ name: 'IRC_TRAMP', groups: ['peripherals'], maxPorts: 1 });
}
if (semver.gte(CONFIG.apiVersion, "1.32.0")) {
functionRules.push({ name: 'TELEMETRY_IBUS', groups: ['telemetry'], maxPorts: 1 });
}
if (semver.gte(CONFIG.apiVersion, "1.36.0")) {
functionRules.push({ name: 'RUNCAM_DEVICE_CONTROL', groups: ['peripherals'], maxPorts: 1 });
}
for (var i = 0; i < functionRules.length; i++) {
functionRules[i].displayName = chrome.i18n.getMessage('portsFunction_' + functionRules[i].name);
}
var mspBaudRates = [
'9600',
'19200',
'38400',
'57600',
'115200',
'230400',
'250000'
];
if (semver.gte(CONFIG.apiVersion, "1.31.0")) {
mspBaudRates = mspBaudRates.concat(['500000', '1000000']);
}
var gpsBaudRates = [
'AUTO',
'9600',
'19200',
'38400',
'57600',
'115200'
];
var telemetryBaudRates = [
'AUTO',
'9600',
'19200',
'38400',
'57600',
'115200'
];
var blackboxBaudRates = [
'AUTO',
'19200',
'38400',
'57600',
'115200',
'230400',
'250000'
];
var columns = ['configuration', 'peripherals', 'sensors', 'telemetry', 'rx'];
if (GUI.active_tab != 'ports') {
GUI.active_tab = 'ports';
}
load_configuration_from_fc();
function load_configuration_from_fc() {
MSP.send_message(MSPCodes.MSP_CF_SERIAL_CONFIG, false, false, on_configuration_loaded_handler);
function on_configuration_loaded_handler() {
$('#content').load("./tabs/ports.html", on_tab_loaded_handler);
board_definition = BOARD.find_board_definition(CONFIG.boardIdentifier);
console.log('Using board definition', board_definition);
}
}
function update_ui() {
if (semver.lt(CONFIG.apiVersion, "1.6.0")) {
$(".tab-ports").removeClass("supported");
return;
}
$(".tab-ports").addClass("supported");
var portIdentifierToNameMapping = {
0: 'UART1',
1: 'UART2',
2: 'UART3',
3: 'UART4',
4: 'UART5',
5: 'UART6',
6: 'UART7',
7: 'UART8',
8: 'UART9',
9: 'UART10',
20: 'USB VCP',
30: 'SOFTSERIAL1',
31: 'SOFTSERIAL2'
};
var gps_baudrate_e = $('select.gps_baudrate');
for (var i = 0; i < gpsBaudRates.length; i++) {
gps_baudrate_e.append('<option value="' + gpsBaudRates[i] + '">' + gpsBaudRates[i] + '</option>');
}
var msp_baudrate_e = $('select.msp_baudrate');
for (var i = 0; i < mspBaudRates.length; i++) {
msp_baudrate_e.append('<option value="' + mspBaudRates[i] + '">' + mspBaudRates[i] + '</option>');
}
var telemetry_baudrate_e = $('select.telemetry_baudrate');
for (var i = 0; i < telemetryBaudRates.length; i++) {
telemetry_baudrate_e.append('<option value="' + telemetryBaudRates[i] + '">' + telemetryBaudRates[i] + '</option>');
}
var blackbox_baudrate_e = $('select.blackbox_baudrate');
for (var i = 0; i < blackboxBaudRates.length; i++) {
blackbox_baudrate_e.append('<option value="' + blackboxBaudRates[i] + '">' + blackboxBaudRates[i] + '</option>');
}
var ports_e = $('.tab-ports .ports');
var port_configuration_template_e = $('#tab-ports-templates .portConfiguration');
for (var portIndex = 0; portIndex < SERIAL_CONFIG.ports.length; portIndex++) {
var port_configuration_e = port_configuration_template_e.clone();
var serialPort = SERIAL_CONFIG.ports[portIndex];
port_configuration_e.data('serialPort', serialPort);
var msp_baudrate_e = port_configuration_e.find('select.msp_baudrate');
msp_baudrate_e.val(serialPort.msp_baudrate);
var telemetry_baudrate_e = port_configuration_e.find('select.telemetry_baudrate');
telemetry_baudrate_e.val(serialPort.telemetry_baudrate);
var gpsBaudrate;
if (serialPort.functions.indexOf('GPS') >= 0) {
gpsBaudrate = serialPort.gps_baudrate;
} else {
gpsBaudrate = 'AUTO';
}
var gps_baudrate_e = port_configuration_e.find('select.gps_baudrate');
gps_baudrate_e.val(gpsBaudrate);
var blackboxBaudrate;
if (serialPort.functions.indexOf('BLACKBOX') >= 0) {
blackboxBaudrate = serialPort.blackbox_baudrate;
} else {
blackboxBaudrate = 'AUTO';
}
var blackbox_baudrate_e = port_configuration_e.find('select.blackbox_baudrate');
blackbox_baudrate_e.val(blackboxBaudrate);
port_configuration_e.find('.identifier').text(portIdentifierToNameMapping[serialPort.identifier]);
port_configuration_e.data('index', portIndex);
port_configuration_e.data('port', serialPort);
for (var columnIndex = 0; columnIndex < columns.length; columnIndex++) {
var column = columns[columnIndex];
var functions_e = $(port_configuration_e).find('.functionsCell-' + column);
for (var i = 0; i < functionRules.length; i++) {
var functionRule = functionRules[i];
var functionName = functionRule.name;
if (functionRule.groups.indexOf(column) == -1) {
continue;
}
var select_e;
if (column !== 'telemetry' && column !== 'sensors' && column !== 'peripherals') {
var checkboxId = 'functionCheckbox-' + portIndex + '-' + columnIndex + '-' + i;
functions_e.prepend('<span class="function"><input type="checkbox" class="togglemedium" id="' + checkboxId + '" value="' + functionName + '" /><label for="' + checkboxId + '"></label></span>');
if (serialPort.functions.indexOf(functionName) >= 0) {
var checkbox_e = functions_e.find('#' + checkboxId);
checkbox_e.prop("checked", true);
}
} else {
var selectElementName = 'function-' + column;
var selectElementSelector = 'select[name=' + selectElementName + ']';
select_e = functions_e.find(selectElementSelector);
if (select_e.size() == 0) {
functions_e.prepend('<span class="function"><select name="' + selectElementName + '" /></span>');
select_e = functions_e.find(selectElementSelector);
var disabledText = chrome.i18n.getMessage('portsTelemetryDisabled');
select_e.append('<option value="">' + disabledText + '</option>');
}
select_e.append('<option value="' + functionName + '">' + functionRule.displayName + '</option>');
if (serialPort.functions.indexOf(functionName) >= 0) {
select_e.val(functionName);
}
}
}
}
ports_e.find('tbody').append(port_configuration_e);
}
}
function on_tab_loaded_handler() {
localize();
update_ui();
$('a.save').click(on_save_handler);
// status data pulled via separate timer with static speed
GUI.interval_add('status_pull', function status_pull() {
MSP.send_message(MSPCodes.MSP_STATUS);
}, 250, true);
GUI.content_ready(callback);
}
function on_save_handler() {
// update configuration based on current ui state
SERIAL_CONFIG.ports = [];
var ports_e = $('.tab-ports .portConfiguration').each(function (portConfiguration_e) {
var portConfiguration_e = this;
var oldSerialPort = $(this).data('serialPort');
var functions = $(portConfiguration_e).find('input:checkbox:checked').map(function() {
return this.value;
}).get();
var telemetryFunction = $(portConfiguration_e).find('select[name=function-telemetry]').val();
if (telemetryFunction) {
functions.push(telemetryFunction);
}
var sensorFunction = $(portConfiguration_e).find('select[name=function-sensors]').val();
if (sensorFunction) {
functions.push(sensorFunction);
}
var peripheralFunction = $(portConfiguration_e).find('select[name=function-peripherals]').val();
if (peripheralFunction) {
functions.push(peripheralFunction);
}
var gpsBaudrate = $(portConfiguration_e).find('.gps_baudrate').val();
if (gpsBaudrate === 'AUTO') {
gpsBaudrate = '57600';
}
var blackboxBaudrate = $(portConfiguration_e).find('.blackbox_baudrate').val();
if (blackboxBaudrate === 'AUTO') {
blackboxBaudrate = '115200';
}
var serialPort = {
functions: functions,
msp_baudrate: $(portConfiguration_e).find('.msp_baudrate').val(),
telemetry_baudrate: $(portConfiguration_e).find('.telemetry_baudrate').val(),
gps_baudrate: gpsBaudrate,
blackbox_baudrate: blackboxBaudrate,
identifier: oldSerialPort.identifier
};
SERIAL_CONFIG.ports.push(serialPort);
});
MSP.send_message(MSPCodes.MSP_SET_CF_SERIAL_CONFIG, mspHelper.crunch(MSPCodes.MSP_SET_CF_SERIAL_CONFIG), false, save_to_eeprom);
function save_to_eeprom() {
MSP.send_message(MSPCodes.MSP_EEPROM_WRITE, false, false, on_saved_handler);
}
function on_saved_handler() {
GUI.log(chrome.i18n.getMessage('configurationEepromSaved'));
GUI.tab_switch_cleanup(function() {
MSP.send_message(MSPCodes.MSP_SET_REBOOT, false, false, on_reboot_success_handler);
});
}
function on_reboot_success_handler() {
GUI.log(chrome.i18n.getMessage('deviceRebooting'));
if (BOARD.find_board_definition(CONFIG.boardIdentifier).vcp) { // VCP-based flight controls may crash old drivers, we catch and reconnect
$('a.connect').click();
GUI.timeout_add('start_connection',function start_connection() {
$('a.connect').click();
},2500);
} else {
GUI.timeout_add('waiting_for_bootup', function waiting_for_bootup() {
MSP.send_message(MSPCodes.MSP_STATUS, false, false, function() {
GUI.log(chrome.i18n.getMessage('deviceReady'));
TABS.ports.initialize(false, $('#content').scrollTop());
});
}, 1500); // seems to be just the right amount of delay to prevent data request timeouts
}
}
}
};
TABS.ports.cleanup = function (callback) {
if (callback) callback();
};

343
src/js/tabs/power.js Normal file
View file

@ -0,0 +1,343 @@
'use strict';
TABS.power = {
supported: false,
};
TABS.power.initialize = function (callback) {
var self = this;
if (GUI.active_tab != 'power') {
GUI.active_tab = 'power';
// Disabled on merge into betaflight-configurator
//googleAnalytics.sendAppView('Power');
}
function load_status() {
MSP.send_message(MSPCodes.MSP_STATUS, false, false, load_voltage_meters);
}
function load_voltage_meters() {
MSP.send_message(MSPCodes.MSP_VOLTAGE_METERS, false, false, load_current_meters);
}
function load_current_meters() {
MSP.send_message(MSPCodes.MSP_CURRENT_METERS, false, false, load_current_meter_configs);
}
function load_current_meter_configs() {
MSP.send_message(MSPCodes.MSP_CURRENT_METER_CONFIG, false, false, load_voltage_meter_configs);
}
function load_voltage_meter_configs() {
MSP.send_message(MSPCodes.MSP_VOLTAGE_METER_CONFIG, false, false, load_battery_state);
}
function load_battery_state() {
MSP.send_message(MSPCodes.MSP_BATTERY_STATE, false, false, load_battery_config);
}
function load_battery_config() {
MSP.send_message(MSPCodes.MSP_BATTERY_CONFIG, false, false, load_html);
}
function load_html() {
$('#content').load("./tabs/power.html", process_html);
}
this.supported = semver.gte(CONFIG.apiVersion, "1.33.0");
if (!this.supported) {
load_html();
} else {
load_status();
}
function updateDisplay(voltageDataSource, currentDataSource) {
// voltage meters
if (BATTERY_CONFIG.voltageMeterSource == 0) {
$('.boxVoltageConfiguration').hide();
} else {
$('.boxVoltageConfiguration').show();
}
if (!voltageDataSource) {
voltageDataSource = [];
for (var index = 0; index < VOLTAGE_METER_CONFIGS.length; index++) {
voltageDataSource[index] = {
vbatscale: parseInt($('input[name="vbatscale-' + index + '"]').val()),
vbatresdivval: parseInt($('input[name="vbatresdivval-' + index + '"]').val()),
vbatresdivmultiplier: parseInt($('input[name="vbatresdivmultiplier-' + index + '"]').val())
};
}
}
var template = $('#tab-power-templates .voltage-meters .voltage-meter');
var destination = $('.tab-power .voltage-meters');
destination.empty();
for (var index = 0; index < VOLTAGE_METERS.length; index++) {
var meterElement = template.clone();
$(meterElement).attr('id', 'voltage-meter-' + index);
var message = chrome.i18n.getMessage('powerVoltageId' + VOLTAGE_METERS[index].id);
$(meterElement).find('.label').text(message)
destination.append(meterElement);
meterElement.hide();
if ((BATTERY_CONFIG.voltageMeterSource == 1 && VOLTAGE_METERS[index].id == 10) // TODO: replace hardcoded constants
|| (BATTERY_CONFIG.voltageMeterSource == 2 && VOLTAGE_METERS[index].id >= 50)) {
meterElement.show();
}
}
var template = $('#tab-power-templates .voltage-configuration');
for (var index = 0; index < VOLTAGE_METER_CONFIGS.length; index++) {
var destination = $('#voltage-meter-' + index + ' .configuration');
var element = template.clone();
var attributeNames = ["vbatscale", "vbatresdivval", "vbatresdivmultiplier"];
for (let attributeName of attributeNames) {
$(element).find('input[name="' + attributeName + '"]').attr('name', attributeName + '-' + index);
}
destination.append(element);
$('input[name="vbatscale-' + index + '"]').val(voltageDataSource[index].vbatscale);
$('input[name="vbatresdivval-' + index + '"]').val(voltageDataSource[index].vbatresdivval);
$('input[name="vbatresdivmultiplier-' + index + '"]').val(voltageDataSource[index].vbatresdivmultiplier);
}
// amperage meters
if (BATTERY_CONFIG.currentMeterSource == 0) {
$('.boxAmperageConfiguration').hide();
} else {
$('.boxAmperageConfiguration').show();
}
if (!currentDataSource) {
currentDataSource = [];
for (var index = 0; index < CURRENT_METER_CONFIGS.length; index++) {
currentDataSource[index] = {
scale: parseInt($('input[name="amperagescale-' + index + '"]').val()),
offset: parseInt($('input[name="amperageoffset-' + index + '"]').val())
};
}
}
var template = $('#tab-power-templates .amperage-meters .amperage-meter');
var destination = $('.tab-power .amperage-meters');
destination.empty();
for (var index = 0; index < CURRENT_METERS.length; index++) {
var meterElement = template.clone();
$(meterElement).attr('id', 'amperage-meter-' + index);
var message = chrome.i18n.getMessage('powerAmperageId' + CURRENT_METERS[index].id);
$(meterElement).find('.label').text(message)
destination.append(meterElement);
meterElement.hide();
if ((BATTERY_CONFIG.currentMeterSource == 1 && CURRENT_METERS[index].id == 10) // TODO: replace constants
|| (BATTERY_CONFIG.currentMeterSource == 2 && CURRENT_METERS[index].id == 80)
|| (BATTERY_CONFIG.currentMeterSource == 3 && CURRENT_METERS[index].id >= 50 && CURRENT_METERS[index].id < 80)) {
meterElement.show();
}
}
var template = $('#tab-power-templates .amperage-configuration');
for (var index = 0; index < CURRENT_METER_CONFIGS.length; index++) {
var destination = $('#amperage-meter-' + index + ' .configuration');
var element = template.clone();
var attributeNames = ["amperagescale", "amperageoffset"];
for (let attributeName of attributeNames) {
$(element).find('input[name="' + attributeName + '"]').attr('name', attributeName + '-' + index);
}
destination.append(element);
$('input[name="amperagescale-' + index + '"]').val(currentDataSource[index].scale);
$('input[name="amperageoffset-' + index + '"]').val(currentDataSource[index].offset);
}
}
function initDisplay() {
if (!TABS.power.supported) {
$(".tab-power").removeClass("supported");
return;
}
$(".tab-power").addClass("supported");
// battery
var template = $('#tab-power-templates .battery-state .battery-state');
var destination = $('.tab-power .battery-state');
var element = template.clone();
$(element).find('.connection-state').attr('id', 'battery-connection-state');
$(element).find('.voltage').attr('id', 'battery-voltage');
$(element).find('.mah-drawn').attr('id', 'battery-mah-drawn');
$(element).find('.amperage').attr('id', 'battery-amperage');
destination.append(element.children());
var template = $('#tab-power-templates .battery-configuration');
var destination = $('.tab-power .battery .configuration');
var element = template.clone();
destination.append(element);
$('input[name="mincellvoltage"]').val(BATTERY_CONFIG.vbatmincellvoltage);
$('input[name="maxcellvoltage"]').val(BATTERY_CONFIG.vbatmaxcellvoltage);
$('input[name="warningcellvoltage"]').val(BATTERY_CONFIG.vbatwarningcellvoltage);
$('input[name="capacity"]').val(BATTERY_CONFIG.capacity);
var haveFc = (semver.lt(CONFIG.apiVersion, "1.35.0") || (CONFIG.boardType == 0 || CONFIG.boardType == 2));
var batteryMeterTypes = [
chrome.i18n.getMessage('powerBatteryVoltageMeterTypeNone'),
chrome.i18n.getMessage('powerBatteryVoltageMeterTypeAdc'),
];
if (haveFc) {
batteryMeterTypes.push(chrome.i18n.getMessage('powerBatteryVoltageMeterTypeEsc'));
}
var batteryMeterType_e = $('select.batterymetersource');
for (var i = 0; i < batteryMeterTypes.length; i++) {
batteryMeterType_e.append('<option value="' + i + '">' + batteryMeterTypes[i] + '</option>');
}
// fill current
var currentMeterTypes = [
chrome.i18n.getMessage('powerBatteryCurrentMeterTypeNone'),
chrome.i18n.getMessage('powerBatteryCurrentMeterTypeAdc'),
];
if (haveFc) {
currentMeterTypes.push(chrome.i18n.getMessage('powerBatteryCurrentMeterTypeVirtual'));
currentMeterTypes.push(chrome.i18n.getMessage('powerBatteryCurrentMeterTypeEsc'));
if (semver.gte(CONFIG.apiVersion, "1.36.0")) {
currentMeterTypes.push(chrome.i18n.getMessage('powerBatteryCurrentMeterTypeMsp'));
}
}
var currentMeterType_e = $('select.currentmetersource');
for (var i = 0; i < currentMeterTypes.length; i++) {
currentMeterType_e.append('<option value="' + i + '">' + currentMeterTypes[i] + '</option>');
}
updateDisplay(VOLTAGE_METER_CONFIGS, CURRENT_METER_CONFIGS);
var batteryMeterType_e = $('select.batterymetersource');
batteryMeterType_e.val(BATTERY_CONFIG.voltageMeterSource);
batteryMeterType_e.change(function () {
BATTERY_CONFIG.voltageMeterSource = parseInt($(this).val());
updateDisplay();
});
var currentMeterType_e = $('select.currentmetersource');
currentMeterType_e.val(BATTERY_CONFIG.currentMeterSource);
currentMeterType_e.change(function () {
BATTERY_CONFIG.currentMeterSource = parseInt($(this).val());
updateDisplay();
});
function get_slow_data() {
MSP.send_message(MSPCodes.MSP_VOLTAGE_METERS, false, false, function () {
for (var i = 0; i < VOLTAGE_METERS.length; i++) {
var elementName = '#voltage-meter-' + i + ' .value';
var element = $(elementName);
element.text(chrome.i18n.getMessage('powerVoltageValue', [VOLTAGE_METERS[i].voltage]));
}
});
MSP.send_message(MSPCodes.MSP_CURRENT_METERS, false, false, function () {
for (var i = 0; i < CURRENT_METERS.length; i++) {
var elementName = '#amperage-meter-' + i + ' .value';
var element = $(elementName);
element.text(chrome.i18n.getMessage('powerAmperageValue', [CURRENT_METERS[i].amperage.toFixed(2)]));
}
});
MSP.send_message(MSPCodes.MSP_BATTERY_STATE, false, false, function () {
var elementPrefix = '#battery';
var element;
element = $(elementPrefix + '-connection-state .value');
element.text(BATTERY_STATE.cellCount > 0 ? chrome.i18n.getMessage('powerBatteryConnectedValueYes', [BATTERY_STATE.cellCount]) : chrome.i18n.getMessage('powerBatteryConnectedValueNo'));
element = $(elementPrefix + '-voltage .value');
element.text(chrome.i18n.getMessage('powerVoltageValue', [BATTERY_STATE.voltage]));
element = $(elementPrefix + '-mah-drawn .value');
element.text(chrome.i18n.getMessage('powerMahValue', [BATTERY_STATE.mAhDrawn]));
element = $(elementPrefix + '-amperage .value');
element.text(chrome.i18n.getMessage('powerAmperageValue', [BATTERY_STATE.amperage]));
});
}
$('a.save').click(function () {
for (var index = 0; index < VOLTAGE_METER_CONFIGS.length; index++) {
VOLTAGE_METER_CONFIGS[index].vbatscale = parseInt($('input[name="vbatscale-' + index + '"]').val());
VOLTAGE_METER_CONFIGS[index].vbatresdivval = parseInt($('input[name="vbatresdivval-' + index + '"]').val());
VOLTAGE_METER_CONFIGS[index].vbatresdivmultiplier = parseInt($('input[name="vbatresdivmultiplier-' + index + '"]').val());
}
for (var index = 0; index < CURRENT_METER_CONFIGS.length; index++) {
CURRENT_METER_CONFIGS[index].scale = parseInt($('input[name="amperagescale-' + index + '"]').val());
CURRENT_METER_CONFIGS[index].offset = parseInt($('input[name="amperageoffset-' + index + '"]').val());
}
BATTERY_CONFIG.vbatmincellvoltage = parseFloat($('input[name="mincellvoltage"]').val());
BATTERY_CONFIG.vbatmaxcellvoltage = parseFloat($('input[name="maxcellvoltage"]').val());
BATTERY_CONFIG.vbatwarningcellvoltage = parseFloat($('input[name="warningcellvoltage"]').val());
BATTERY_CONFIG.capacity = parseInt($('input[name="capacity"]').val());
function save_battery_config() {
MSP.send_message(MSPCodes.MSP_SET_BATTERY_CONFIG, mspHelper.crunch(MSPCodes.MSP_SET_BATTERY_CONFIG), false, save_voltage_config);
}
function save_voltage_config() {
if (semver.gte(CONFIG.apiVersion, "1.36.0")) {
mspHelper.sendVoltageConfig(save_amperage_config);
} else {
MSP.send_message(MSPCodes.MSP_SET_VOLTAGE_METER_CONFIG, mspHelper.crunch(MSPCodes.MSP_SET_VOLTAGE_METER_CONFIG), false, save_amperage_config);
}
}
function save_amperage_config() {
if (semver.gte(CONFIG.apiVersion, "1.36.0")) {
mspHelper.sendCurrentConfig(save_to_eeprom);
} else {
MSP.send_message(MSPCodes.MSP_SET_CURRENT_METER_CONFIG, mspHelper.crunch(MSPCodes.MSP_SET_CURRENT_METER_CONFIG), false, save_to_eeprom);
}
}
function save_to_eeprom() {
MSP.send_message(MSPCodes.MSP_EEPROM_WRITE, false, false, save_completed);
}
function save_completed() {
GUI.log(chrome.i18n.getMessage('configurationEepromSaved'));
TABS.power.initialize();
}
save_battery_config();
});
GUI.interval_add('setup_data_pull_slow', get_slow_data, 200, true); // 5hz
}
function process_html() {
initDisplay();
// translate to user-selected language
localize();
GUI.content_ready(callback);
}
};
TABS.power.cleanup = function (callback) {
if (callback) callback();
};

512
src/js/tabs/receiver.js Normal file
View file

@ -0,0 +1,512 @@
'use strict';
TABS.receiver = {
rateChartHeight: 117,
useSuperExpo: false,
deadband: 0,
yawDeadband: 0
};
TABS.receiver.initialize = function (callback) {
var tab = this;
if (GUI.active_tab != 'receiver') {
GUI.active_tab = 'receiver';
}
function get_rc_data() {
MSP.send_message(MSPCodes.MSP_RC, false, false, get_rssi_config);
}
function get_rssi_config() {
MSP.send_message(MSPCodes.MSP_RSSI_CONFIG, false, false, get_rc_tuning);
}
function get_rc_tuning() {
MSP.send_message(MSPCodes.MSP_RC_TUNING, false, false, get_rc_map);
}
function get_rc_map() {
MSP.send_message(MSPCodes.MSP_RX_MAP, false, false, load_rc_configs);
}
function load_rc_configs() {
var next_callback = load_rx_config;
if (semver.gte(CONFIG.apiVersion, "1.15.0")) {
MSP.send_message(MSPCodes.MSP_RC_DEADBAND, false, false, next_callback);
} else {
next_callback();
}
}
function load_rx_config() {
var next_callback = load_html;
if (semver.gte(CONFIG.apiVersion, "1.20.0")) {
MSP.send_message(MSPCodes.MSP_RX_CONFIG, false, false, next_callback);
} else {
next_callback();
}
}
function load_html() {
$('#content').load("./tabs/receiver.html", process_html);
}
MSP.send_message(MSPCodes.MSP_FEATURE_CONFIG, false, false, get_rc_data);
function process_html() {
// translate to user-selected language
localize();
chrome.storage.local.get('rx_refresh_rate', function (result) {
if (result.rx_refresh_rate) {
$('select[name="rx_refresh_rate"]').val(result.rx_refresh_rate).change();
} else {
$('select[name="rx_refresh_rate"]').change(); // start with default value
}
});
if (semver.lt(CONFIG.apiVersion, "1.15.0")) {
$('.deadband').hide();
} else {
$('.deadband input[name="yaw_deadband"]').val(RC_DEADBAND_CONFIG.yaw_deadband);
$('.deadband input[name="deadband"]').val(RC_DEADBAND_CONFIG.deadband);
$('.deadband input[name="3ddeadbandthrottle"]').val(RC_DEADBAND_CONFIG.deadband3d_throttle);
$('.deadband input[name="deadband"]').change(function () {
tab.deadband = parseInt($(this).val());
}).change();
$('.deadband input[name="yaw_deadband"]').change(function () {
tab.yawDeadband = parseInt($(this).val());
}).change();
}
if (semver.lt(CONFIG.apiVersion, "1.15.0")) {
$('.sticks').hide();
} else {
$('.sticks input[name="stick_min"]').val(RX_CONFIG.stick_min);
$('.sticks input[name="stick_center"]').val(RX_CONFIG.stick_center);
$('.sticks input[name="stick_max"]').val(RX_CONFIG.stick_max);
}
if (semver.gte(CONFIG.apiVersion, "1.20.0")) {
$('select[name="rcInterpolation-select"]').val(RX_CONFIG.rcInterpolation);
$('input[name="rcInterpolationInterval-number"]').val(RX_CONFIG.rcInterpolationInterval);
$('select[name="rcInterpolation-select"]').change(function () {
tab.updateRcInterpolationParameters();
}).change();
} else {
$('.tab-receiver div.rcInterpolation').hide();
}
// generate bars
var bar_names = [
chrome.i18n.getMessage('controlAxisRoll'),
chrome.i18n.getMessage('controlAxisPitch'),
chrome.i18n.getMessage('controlAxisYaw'),
chrome.i18n.getMessage('controlAxisThrottle')
],
bar_container = $('.tab-receiver .bars'),
aux_index = 1;
var num_bars = (RC.active_channels > 0) ? RC.active_channels : 8;
for (var i = 0; i < num_bars; i++) {
var name;
if (i < bar_names.length) {
name = bar_names[i];
} else {
name = chrome.i18n.getMessage("controlAxisAux" + (aux_index++));
}
bar_container.append('\
<ul>\
<li class="name">' + name + '</li>\
<li class="meter">\
<div class="meter-bar">\
<div class="label"></div>\
<div class="fill' + (RC.active_channels == 0 ? 'disabled' : '') + '">\
<div class="label"></div>\
</div>\
</div>\
</li>\
</ul>\
');
}
// we could probably use min and max throttle for the range, will see
var meter_scale = {
'min': 800,
'max': 2200
};
var meter_fill_array = [];
$('.meter .fill', bar_container).each(function () {
meter_fill_array.push($(this));
});
var meter_label_array = [];
$('.meter', bar_container).each(function () {
meter_label_array.push($('.label' , this));
});
// correct inner label margin on window resize (i don't know how we could do this in css)
tab.resize = function () {
var containerWidth = $('.meter:first', bar_container).width(),
labelWidth = $('.meter .label:first', bar_container).width(),
margin = (containerWidth / 2) - (labelWidth / 2);
for (var i = 0; i < meter_label_array.length; i++) {
meter_label_array[i].css('margin-left', margin);
}
};
$(window).on('resize', tab.resize).resize(); // trigger so labels get correctly aligned on creation
// handle rcmap & rssi aux channel
var RC_MAP_Letters = ['A', 'E', 'R', 'T', '1', '2', '3', '4'];
var strBuffer = [];
for (var i = 0; i < RC_MAP.length; i++) {
strBuffer[RC_MAP[i]] = RC_MAP_Letters[i];
}
// reconstruct
var str = strBuffer.join('');
// set current value
$('input[name="rcmap"]').val(str);
// validation / filter
var last_valid = str;
$('input[name="rcmap"]').on('input', function () {
var val = $(this).val();
// limit length to max 8
if (val.length > 8) {
val = val.substr(0, 8);
$(this).val(val);
}
});
$('input[name="rcmap"]').focusout(function () {
var val = $(this).val(),
strBuffer = val.split(''),
duplicityBuffer = [];
if (val.length != 8) {
$(this).val(last_valid);
return false;
}
// check if characters inside are all valid, also check for duplicity
for (var i = 0; i < val.length; i++) {
if (RC_MAP_Letters.indexOf(strBuffer[i]) < 0) {
$(this).val(last_valid);
return false;
}
if (duplicityBuffer.indexOf(strBuffer[i]) < 0) {
duplicityBuffer.push(strBuffer[i]);
} else {
$(this).val(last_valid);
return false;
}
}
});
// handle helper
$('select[name="rcmap_helper"]').val(0); // go out of bounds
$('select[name="rcmap_helper"]').change(function () {
$('input[name="rcmap"]').val($(this).val());
});
// rssi
var rssi_channel_e = $('select[name="rssi_channel"]');
rssi_channel_e.append('<option value="0">' + chrome.i18n.getMessage("receiverRssiChannelDisabledOption") + '</option>');
//1-4 reserved for Roll Pitch Yaw & Throttle, starting at 5
for (var i = 5; i < RC.active_channels + 1; i++) {
rssi_channel_e.append('<option value="' + i + '">' + chrome.i18n.getMessage("controlAxisAux" + (i-4)) + '</option>');
}
$('select[name="rssi_channel"]').val(RSSI_CONFIG.channel);
var rateHeight = TABS.receiver.rateChartHeight;
// UI Hooks
$('a.refresh').click(function () {
// Todo: refresh data here
});
$('a.update').click(function () {
if (semver.gte(CONFIG.apiVersion, "1.15.0")) {
RX_CONFIG.stick_max = parseInt($('.sticks input[name="stick_max"]').val());
RX_CONFIG.stick_center = parseInt($('.sticks input[name="stick_center"]').val());
RX_CONFIG.stick_min = parseInt($('.sticks input[name="stick_min"]').val());
RC_DEADBAND_CONFIG.yaw_deadband = parseInt($('.deadband input[name="yaw_deadband"]').val());
RC_DEADBAND_CONFIG.deadband = parseInt($('.deadband input[name="deadband"]').val());
RC_DEADBAND_CONFIG.deadband3d_throttle = ($('.deadband input[name="3ddeadbandthrottle"]').val());
}
// catch rc map
var RC_MAP_Letters = ['A', 'E', 'R', 'T', '1', '2', '3', '4'];
var strBuffer = $('input[name="rcmap"]').val().split('');
for (var i = 0; i < RC_MAP.length; i++) {
RC_MAP[i] = strBuffer.indexOf(RC_MAP_Letters[i]);
}
// catch rssi aux
RSSI_CONFIG.channel = parseInt($('select[name="rssi_channel"]').val());
if (semver.gte(CONFIG.apiVersion, "1.20.0")) {
RX_CONFIG.rcInterpolation = parseInt($('select[name="rcInterpolation-select"]').val());
RX_CONFIG.rcInterpolationInterval = parseInt($('input[name="rcInterpolationInterval-number"]').val());
}
function save_rssi_config() {
MSP.send_message(MSPCodes.MSP_SET_RSSI_CONFIG, mspHelper.crunch(MSPCodes.MSP_SET_RSSI_CONFIG), false, save_rc_configs);
}
function save_rc_configs() {
var next_callback = save_rx_config;
if (semver.gte(CONFIG.apiVersion, "1.15.0")) {
MSP.send_message(MSPCodes.MSP_SET_RC_DEADBAND, mspHelper.crunch(MSPCodes.MSP_SET_RC_DEADBAND), false, next_callback);
} else {
next_callback();
}
}
function save_rx_config() {
var next_callback = save_to_eeprom;
if (semver.gte(CONFIG.apiVersion, "1.20.0")) {
MSP.send_message(MSPCodes.MSP_SET_RX_CONFIG, mspHelper.crunch(MSPCodes.MSP_SET_RX_CONFIG), false, next_callback);
} else {
next_callback();
}
}
function save_to_eeprom() {
MSP.send_message(MSPCodes.MSP_EEPROM_WRITE, false, false, function () {
GUI.log(chrome.i18n.getMessage('receiverEepromSaved'));
});
}
MSP.send_message(MSPCodes.MSP_SET_RX_MAP, mspHelper.crunch(MSPCodes.MSP_SET_RX_MAP), false, save_rssi_config);
});
$("a.sticks").click(function() {
var
windowWidth = 370,
windowHeight = 510;
chrome.app.window.create("/tabs/receiver_msp.html", {
id: "receiver_msp",
innerBounds: {
minWidth: windowWidth, minHeight: windowHeight,
width: windowWidth, height: windowHeight,
maxWidth: windowWidth, maxHeight: windowHeight
},
alwaysOnTop: true
}, function(createdWindow) {
// Give the window a callback it can use to send the channels (otherwise it can't see those objects)
createdWindow.contentWindow.setRawRx = function(channels) {
if (CONFIGURATOR.connectionValid && GUI.active_tab != 'cli') {
mspHelper.setRawRx(channels);
return true;
} else {
return false;
}
}
});
});
// Only show the MSP control sticks if the MSP Rx feature is enabled
$(".sticks_btn").toggle(FEATURE_CONFIG.features.isEnabled('RX_MSP'));
$('select[name="rx_refresh_rate"]').change(function () {
var plot_update_rate = parseInt($(this).val(), 10);
// save update rate
chrome.storage.local.set({'rx_refresh_rate': plot_update_rate});
function get_rc_data() {
MSP.send_message(MSPCodes.MSP_RC, false, false, update_ui);
}
// setup plot
var RX_plot_data = new Array(RC.active_channels);
for (var i = 0; i < RX_plot_data.length; i++) {
RX_plot_data[i] = [];
}
var samples = 0,
svg = d3.select("svg"),
RX_plot_e = $('#RX_plot'),
margin = {top: 20, right: 0, bottom: 10, left: 40},
width, height, widthScale, heightScale;
function update_receiver_plot_size() {
width = RX_plot_e.width() - margin.left - margin.right;
height = RX_plot_e.height() - margin.top - margin.bottom;
widthScale.range([0, width]);
heightScale.range([height, 0]);
}
function update_ui() {
// update bars with latest data
for (var i = 0; i < RC.active_channels; i++) {
meter_fill_array[i].css('width', ((RC.channels[i] - meter_scale.min) / (meter_scale.max - meter_scale.min) * 100).clamp(0, 100) + '%');
meter_label_array[i].text(RC.channels[i]);
}
// push latest data to the main array
for (var i = 0; i < RC.active_channels; i++) {
RX_plot_data[i].push([samples, RC.channels[i]]);
}
// Remove old data from array
while (RX_plot_data.length > 300) {
for (var i = 0; i < RX_plot_data.length; i++) {
RX_plot_data[i].shift();
}
}
// update required parts of the plot
widthScale = d3.scale.linear().
domain([(samples - 299), samples]);
heightScale = d3.scale.linear().
domain([800, 2200]);
update_receiver_plot_size();
var xGrid = d3.svg.axis().
scale(widthScale).
orient("bottom").
tickSize(-height, 0, 0).
tickFormat("");
var yGrid = d3.svg.axis().
scale(heightScale).
orient("left").
tickSize(-width, 0, 0).
tickFormat("");
var xAxis = d3.svg.axis().
scale(widthScale).
orient("bottom").
tickFormat(function (d) {return d;});
var yAxis = d3.svg.axis().
scale(heightScale).
orient("left").
tickFormat(function (d) {return d;});
var line = d3.svg.line().
x(function (d) {return widthScale(d[0]);}).
y(function (d) {return heightScale(d[1]);});
svg.select(".x.grid").call(xGrid);
svg.select(".y.grid").call(yGrid);
svg.select(".x.axis").call(xAxis);
svg.select(".y.axis").call(yAxis);
var data = svg.select("g.data"),
lines = data.selectAll("path").data(RX_plot_data, function (d, i) {return i;}),
newLines = lines.enter().append("path").attr("class", "line");
lines.attr('d', line);
samples++;
}
// timer initialization
GUI.interval_remove('receiver_pull');
// enable RC data pulling
GUI.interval_add('receiver_pull', get_rc_data, plot_update_rate, true);
});
// Setup model for preview
tab.initModelPreview();
tab.renderModel();
// TODO: Combine two polls together
GUI.interval_add('receiver_pull_for_model_preview', tab.getRecieverData, 33, false);
// status data pulled via separate timer with static speed
GUI.interval_add('status_pull', function status_pull() {
MSP.send_message(MSPCodes.MSP_STATUS);
}, 250, true);
GUI.content_ready(callback);
}
};
TABS.receiver.getRecieverData = function () {
MSP.send_message(MSPCodes.MSP_RC, false, false);
};
TABS.receiver.initModelPreview = function () {
this.keepRendering = true;
this.model = new Model($('.model_preview'), $('.model_preview canvas'));
this.useSuperExpo = false;
if (semver.gte(CONFIG.apiVersion, "1.16.0")) {
this.useSuperExpo = FEATURE_CONFIG.features.isEnabled('SUPEREXPO_RATES');
}
var useOldRateCurve = false;
if (CONFIG.flightControllerIdentifier == 'CLFL' && semver.lt(CONFIG.apiVersion, '2.0.0')) {
useOldRateCurve = true;
}
if (CONFIG.flightControllerIdentifier == 'BTFL' && semver.lt(CONFIG.flightControllerVersion, '2.8.0')) {
useOldRateCurve = true;
}
this.rateCurve = new RateCurve(useOldRateCurve);
$(window).on('resize', $.proxy(this.model.resize, this.model));
};
TABS.receiver.renderModel = function () {
if (this.keepRendering) { requestAnimationFrame(this.renderModel.bind(this)); }
if (!this.clock) { this.clock = new THREE.Clock(); }
if (RC.channels[0] && RC.channels[1] && RC.channels[2]) {
var delta = this.clock.getDelta();
var roll = delta * this.rateCurve.rcCommandRawToDegreesPerSecond(RC.channels[0], RC_tuning.roll_rate, RC_tuning.RC_RATE, RC_tuning.RC_EXPO, this.useSuperExpo, this.deadband),
pitch = delta * this.rateCurve.rcCommandRawToDegreesPerSecond(RC.channels[1], RC_tuning.pitch_rate, RC_tuning.RC_RATE, RC_tuning.RC_EXPO, this.useSuperExpo, this.deadband),
yaw = delta * this.rateCurve.rcCommandRawToDegreesPerSecond(RC.channels[2], RC_tuning.yaw_rate, RC_tuning.rcYawRate, RC_tuning.RC_YAW_EXPO, this.useSuperExpo, this.yawDeadband);
this.model.rotateBy(-degToRad(pitch), -degToRad(yaw), -degToRad(roll));
}
};
TABS.receiver.cleanup = function (callback) {
$(window).off('resize', this.resize);
if (this.model) {
$(window).off('resize', $.proxy(this.model.resize, this.model));
}
this.keepRendering = false;
if (callback) callback();
};
TABS.receiver.updateRcInterpolationParameters = function () {
if (semver.gte(CONFIG.apiVersion, "1.20.0")) {
if ($('select[name="rcInterpolation-select"]').val() === '3') {
$('.tab-receiver .rcInterpolationInterval').show();
} else {
$('.tab-receiver .rcInterpolationInterval').hide();
}
}
};

185
src/js/tabs/receiver_msp.js Normal file
View file

@ -0,0 +1,185 @@
"use strict";
var
CHANNEL_MIN_VALUE = 1000,
CHANNEL_MID_VALUE = 1500,
CHANNEL_MAX_VALUE = 2000,
// What's the index of each channel in the MSP channel list?
channelMSPIndexes = {
roll: 0,
pitch: 1,
throttle: 2,
yaw: 3,
aux1: 4,
aux2: 5,
aux3: 6,
aux4: 7,
},
// Set reasonable initial stick positions (Mode 2)
stickValues = {
throttle: CHANNEL_MIN_VALUE,
pitch: CHANNEL_MID_VALUE,
roll: CHANNEL_MID_VALUE,
yaw: CHANNEL_MID_VALUE,
aux1: CHANNEL_MIN_VALUE,
aux2: CHANNEL_MIN_VALUE,
aux3: CHANNEL_MIN_VALUE,
aux4: CHANNEL_MIN_VALUE
},
// First the vertical axis, then the horizontal:
gimbals = [
["throttle", "yaw"],
["pitch", "roll"],
],
gimbalElems,
sliderElems,
enableTX = false;
function transmitChannels() {
var
channelValues = [0, 0, 0, 0, 0, 0, 0, 0];
if (!enableTX) {
return;
}
for (var stickName in stickValues) {
channelValues[channelMSPIndexes[stickName]] = stickValues[stickName];
}
// Callback given to us by the window creator so we can have it send data over MSP for us:
if (!window.setRawRx(channelValues)) {
// MSP connection has gone away
chrome.app.window.current().close();
}
}
function stickPortionToChannelValue(portion) {
portion = Math.min(Math.max(portion, 0.0), 1.0);
return Math.round(portion * (CHANNEL_MAX_VALUE - CHANNEL_MIN_VALUE) + CHANNEL_MIN_VALUE);
}
function channelValueToStickPortion(channel) {
return (channel - CHANNEL_MIN_VALUE) / (CHANNEL_MAX_VALUE - CHANNEL_MIN_VALUE);
}
function updateControlPositions() {
for (var stickName in stickValues) {
var
stickValue = stickValues[stickName];
// Look for the gimbal which corresponds to this stick name
for (var gimbalIndex in gimbals) {
var
gimbal = gimbals[gimbalIndex],
gimbalElem = gimbalElems.get(gimbalIndex),
gimbalSize = $(gimbalElem).width(),
stickElem = $(".control-stick", gimbalElem);
if (gimbal[0] == stickName) {
stickElem.css('top', (1.0 - channelValueToStickPortion(stickValue)) * gimbalSize + "px");
break;
} else if (gimbal[1] == stickName) {
stickElem.css('left', channelValueToStickPortion(stickValue) * gimbalSize + "px");
break;
}
}
}
}
function handleGimbalMouseDrag(e) {
var
gimbal = $(gimbalElems.get(e.data.gimbalIndex)),
gimbalOffset = gimbal.offset(),
gimbalSize = gimbal.width();
stickValues[gimbals[e.data.gimbalIndex][0]] = stickPortionToChannelValue(1.0 - (e.pageY - gimbalOffset.top) / gimbalSize);
stickValues[gimbals[e.data.gimbalIndex][1]] = stickPortionToChannelValue((e.pageX - gimbalOffset.left) / gimbalSize);
updateControlPositions();
}
function localizeAxisNames() {
for (var gimbalIndex in gimbals) {
var
gimbal = gimbalElems.get(gimbalIndex);
$(".gimbal-label-vert", gimbal).text(chrome.i18n.getMessage("controlAxis" + gimbals[gimbalIndex][0]));
$(".gimbal-label-horz", gimbal).text(chrome.i18n.getMessage("controlAxis" + gimbals[gimbalIndex][1]));
}
for (var sliderIndex = 0; sliderIndex < 4; sliderIndex++) {
$(".slider-label", sliderElems.get(sliderIndex)).text(chrome.i18n.getMessage("controlAxisAux" + (sliderIndex + 1)));
}
}
$(document).ready(function() {
$(".button-enable").click(function() {
var
shrinkHeight = $(".warning").height();
$(".warning").slideUp("short", function() {
chrome.app.window.current().innerBounds.minHeight -= shrinkHeight;
chrome.app.window.current().innerBounds.height -= shrinkHeight;
chrome.app.window.current().innerBounds.maxHeight -= shrinkHeight;
});
enableTX = true;
});
gimbalElems = $(".control-gimbal");
sliderElems = $(".control-slider");
gimbalElems.each(function(gimbalIndex) {
$(this).on('mousedown', {gimbalIndex: gimbalIndex}, function(e) {
if (e.which == 1) { // Only move sticks on left mouse button
handleGimbalMouseDrag(e);
$(window).on('mousemove', {gimbalIndex: gimbalIndex}, handleGimbalMouseDrag);
}
});
});
$(".slider", sliderElems).each(function(sliderIndex) {
var
initialValue = stickValues["aux" + (sliderIndex + 1)];
$(this)
.noUiSlider({
start: initialValue,
range: {
min: CHANNEL_MIN_VALUE,
max: CHANNEL_MAX_VALUE
}
}).on('slide change set', function(e, value) {
value = Math.round(parseFloat(value));
stickValues["aux" + (sliderIndex + 1)] = value;
$(".tooltip", this).text(value);
});
$(this).append('<div class="tooltip"></div>');
$(".tooltip", this).text(initialValue);
});
/*
* Mouseup handler needs to be bound to the window in order to receive mouseup if mouse leaves window.
*/
$(window).mouseup(function(e) {
$(this).off('mousemove', handleGimbalMouseDrag);
});
localizeAxisNames();
updateControlPositions();
setInterval(transmitChannels, 50);
});

470
src/js/tabs/sensors.js Normal file
View file

@ -0,0 +1,470 @@
'use strict';
TABS.sensors = {};
TABS.sensors.initialize = function (callback) {
var self = this;
if (GUI.active_tab != 'sensors') {
GUI.active_tab = 'sensors';
}
function initSensorData(){
for (var i = 0; i < 3; i++) {
SENSOR_DATA.accelerometer[i] = 0;
SENSOR_DATA.gyroscope[i] = 0;
SENSOR_DATA.magnetometer[i] = 0;
SENSOR_DATA.sonar = 0;
SENSOR_DATA.altitude = 0;
SENSOR_DATA.debug[i] = 0;
}
}
function initDataArray(length) {
var data = new Array(length);
for (var i = 0; i < length; i++) {
data[i] = new Array();
data[i].min = -1;
data[i].max = 1;
}
return data;
}
function addSampleToData(data, sampleNumber, sensorData) {
for (var i = 0; i < data.length; i++) {
var dataPoint = sensorData[i];
data[i].push([sampleNumber, dataPoint]);
if (dataPoint < data[i].min) {
data[i].min = dataPoint;
}
if (dataPoint > data[i].max) {
data[i].max = dataPoint;
}
}
while (data[0].length > 300) {
for (i = 0; i < data.length; i++) {
data[i].shift();
}
}
return sampleNumber + 1;
}
var margin = {top: 20, right: 10, bottom: 10, left: 40};
function updateGraphHelperSize(helpers) {
helpers.width = helpers.targetElement.width() - margin.left - margin.right;
helpers.height = helpers.targetElement.height() - margin.top - margin.bottom;
helpers.widthScale.range([0, helpers.width]);
helpers.heightScale.range([helpers.height, 0]);
helpers.xGrid.tickSize(-helpers.height, 0, 0);
helpers.yGrid.tickSize(-helpers.width, 0, 0);
}
function initGraphHelpers(selector, sampleNumber, heightDomain) {
var helpers = {selector: selector, targetElement: $(selector), dynamicHeightDomain: !heightDomain};
helpers.widthScale = d3.scale.linear()
.clamp(true)
.domain([(sampleNumber - 299), sampleNumber]);
helpers.heightScale = d3.scale.linear()
.clamp(true)
.domain(heightDomain || [1, -1]);
helpers.xGrid = d3.svg.axis();
helpers.yGrid = d3.svg.axis();
updateGraphHelperSize(helpers);
helpers.xGrid
.scale(helpers.widthScale)
.orient("bottom")
.ticks(5)
.tickFormat("");
helpers.yGrid
.scale(helpers.heightScale)
.orient("left")
.ticks(5)
.tickFormat("");
helpers.xAxis = d3.svg.axis()
.scale(helpers.widthScale)
.ticks(5)
.orient("bottom")
.tickFormat(function (d) {return d;});
helpers.yAxis = d3.svg.axis()
.scale(helpers.heightScale)
.ticks(5)
.orient("left")
.tickFormat(function (d) {return d;});
helpers.line = d3.svg.line()
.x(function (d) {return helpers.widthScale(d[0]);})
.y(function (d) {return helpers.heightScale(d[1]);});
return helpers;
}
function drawGraph(graphHelpers, data, sampleNumber) {
var svg = d3.select(graphHelpers.selector);
if (graphHelpers.dynamicHeightDomain) {
var limits = [];
$.each(data, function (idx, datum) {
limits.push(datum.min);
limits.push(datum.max);
});
graphHelpers.heightScale.domain(d3.extent(limits));
}
graphHelpers.widthScale.domain([(sampleNumber - 299), sampleNumber]);
svg.select(".x.grid").call(graphHelpers.xGrid);
svg.select(".y.grid").call(graphHelpers.yGrid);
svg.select(".x.axis").call(graphHelpers.xAxis);
svg.select(".y.axis").call(graphHelpers.yAxis);
var group = svg.select("g.data");
var lines = group.selectAll("path").data(data, function (d, i) {return i;});
var newLines = lines.enter().append("path").attr("class", "line");
lines.attr('d', graphHelpers.line);
}
function plot_gyro(enable) {
if (enable) {
$('.wrapper.gyro').show();
} else {
$('.wrapper.gyro').hide();
}
}
function plot_accel(enable) {
if (enable) {
$('.wrapper.accel').show();
} else {
$('.wrapper.accel').hide();
}
}
function plot_mag(enable) {
if (enable) {
$('.wrapper.mag').show();
} else {
$('.wrapper.mag').hide();
}
}
function plot_baro(enable) {
if (enable) {
$('.wrapper.baro').show();
} else {
$('.wrapper.baro').hide();
}
}
function plot_sonar(enable) {
if (enable) {
$('.wrapper.sonar').show();
} else {
$('.wrapper.sonar').hide();
}
}
function plot_debug(enable) {
if (enable) {
$('.wrapper.debug').show();
} else {
$('.wrapper.debug').hide();
}
}
$('#content').load("./tabs/sensors.html", function load_html() {
// translate to user-selected language
localize();
// disable graphs for sensors that are missing
var checkboxes = $('.tab-sensors .info .checkboxes input');
checkboxes.parent().show();
if (CONFIG.boardType == 0 || CONFIG.boardType == 2) {
if (!have_sensor(CONFIG.activeSensors, 'acc')) {
checkboxes.eq(1).prop('disabled', true);
}
if (!have_sensor(CONFIG.activeSensors, 'mag')) {
checkboxes.eq(2).prop('disabled', true);
}
if (!have_sensor(CONFIG.activeSensors, 'baro')) {
checkboxes.eq(3).prop('disabled', true);
}
if (!have_sensor(CONFIG.activeSensors, 'sonar')) {
checkboxes.eq(4).prop('disabled', true);
}
} else {
for (var i = 0; i <= 4; i++) {
checkboxes.eq(i).prop('disabled', true);
checkboxes.eq(i).parent().hide();
}
}
$('.tab-sensors .info .checkboxes input').change(function () {
var enable = $(this).prop('checked');
var index = $(this).parent().index();
switch (index) {
case 0:
plot_gyro(enable);
break;
case 1:
plot_accel(enable);
break;
case 2:
plot_mag(enable);
break;
case 3:
plot_baro(enable);
break;
case 4:
plot_sonar(enable);
break;
case 5:
plot_debug(enable);
break;
}
var checkboxes = [];
$('.tab-sensors .info .checkboxes input').each(function () {
checkboxes.push($(this).prop('checked'));
});
$('.tab-sensors .rate select:first').change();
chrome.storage.local.set({'graphs_enabled': checkboxes});
});
chrome.storage.local.get('graphs_enabled', function (result) {
if (result.graphs_enabled) {
var checkboxes = $('.tab-sensors .info .checkboxes input');
for (var i = 0; i < result.graphs_enabled.length; i++) {
checkboxes.eq(i).not(':disabled').prop('checked', result.graphs_enabled[i]).change();
}
} else {
$('.tab-sensors .info input:lt(4):not(:disabled)').prop('checked', true).change();
}
});
// Always start with default/empty sensor data array, clean slate all
initSensorData();
// Setup variables
var samples_gyro_i = 0,
samples_accel_i = 0,
samples_mag_i = 0,
samples_baro_i = 0,
samples_sonar_i = 0,
samples_debug_i = 0,
gyro_data = initDataArray(3),
accel_data = initDataArray(3),
mag_data = initDataArray(3),
baro_data = initDataArray(1),
sonar_data = initDataArray(1),
debug_data = [
initDataArray(1),
initDataArray(1),
initDataArray(1),
initDataArray(1)
];
var gyroHelpers = initGraphHelpers('#gyro', samples_gyro_i, [-2000, 2000]);
var accelHelpers = initGraphHelpers('#accel', samples_accel_i, [-2, 2]);
var magHelpers = initGraphHelpers('#mag', samples_mag_i, [-1, 1]);
var baroHelpers = initGraphHelpers('#baro', samples_baro_i);
var sonarHelpers = initGraphHelpers('#sonar', samples_sonar_i);
var debugHelpers = [
initGraphHelpers('#debug1', samples_debug_i),
initGraphHelpers('#debug2', samples_debug_i),
initGraphHelpers('#debug3', samples_debug_i),
initGraphHelpers('#debug4', samples_debug_i)
];
var raw_data_text_ements = {
x: [],
y: [],
z: []
};
$('.plot_control .x, .plot_control .y, .plot_control .z').each(function () {
var el = $(this);
if (el.hasClass('x')) {
raw_data_text_ements.x.push(el);
} else if (el.hasClass('y')) {
raw_data_text_ements.y.push(el);
} else {
raw_data_text_ements.z.push(el);
}
});
// set refresh speeds according to configuration saved in storage
chrome.storage.local.get('sensor_settings', function (result) {
if (result.sensor_settings) {
$('.tab-sensors select[name="gyro_refresh_rate"]').val(result.sensor_settings.rates.gyro);
$('.tab-sensors select[name="gyro_scale"]').val(result.sensor_settings.scales.gyro);
$('.tab-sensors select[name="accel_refresh_rate"]').val(result.sensor_settings.rates.accel);
$('.tab-sensors select[name="accel_scale"]').val(result.sensor_settings.scales.accel);
$('.tab-sensors select[name="mag_refresh_rate"]').val(result.sensor_settings.rates.mag);
$('.tab-sensors select[name="mag_scale"]').val(result.sensor_settings.scales.mag);
$('.tab-sensors select[name="baro_refresh_rate"]').val(result.sensor_settings.rates.baro);
$('.tab-sensors select[name="sonar_refresh_rate"]').val(result.sensor_settings.rates.sonar);
$('.tab-sensors select[name="debug_refresh_rate"]').val(result.sensor_settings.rates.debug);
// start polling data by triggering refresh rate change event
$('.tab-sensors .rate select:first').change();
} else {
// start polling immediatly (as there is no configuration saved in the storage)
$('.tab-sensors .rate select:first').change();
}
});
$('.tab-sensors .rate select, .tab-sensors .scale select').change(function () {
// if any of the select fields change value, all of the select values are grabbed
// and timers are re-initialized with the new settings
var rates = {
'gyro': parseInt($('.tab-sensors select[name="gyro_refresh_rate"]').val(), 10),
'accel': parseInt($('.tab-sensors select[name="accel_refresh_rate"]').val(), 10),
'mag': parseInt($('.tab-sensors select[name="mag_refresh_rate"]').val(), 10),
'baro': parseInt($('.tab-sensors select[name="baro_refresh_rate"]').val(), 10),
'sonar': parseInt($('.tab-sensors select[name="sonar_refresh_rate"]').val(), 10),
'debug': parseInt($('.tab-sensors select[name="debug_refresh_rate"]').val(), 10)
};
var scales = {
'gyro': parseFloat($('.tab-sensors select[name="gyro_scale"]').val()),
'accel': parseFloat($('.tab-sensors select[name="accel_scale"]').val()),
'mag': parseFloat($('.tab-sensors select[name="mag_scale"]').val())
};
// handling of "data pulling" is a little bit funky here, as MSP_RAW_IMU contains values for gyro/accel/mag but not baro
// this means that setting a slower refresh rate on any of the attributes would have no effect
// what we will do instead is = determinate the fastest refresh rate for those 3 attributes, use that as a "polling rate"
// and use the "slower" refresh rates only for re-drawing the graphs (to save resources/computing power)
var fastest = d3.min([rates.gyro, rates.accel, rates.mag]);
// store current/latest refresh rates in the storage
chrome.storage.local.set({'sensor_settings': {'rates': rates, 'scales': scales}});
// re-initialize domains with new scales
gyroHelpers = initGraphHelpers('#gyro', samples_gyro_i, [-scales.gyro, scales.gyro]);
accelHelpers = initGraphHelpers('#accel', samples_accel_i, [-scales.accel, scales.accel]);
magHelpers = initGraphHelpers('#mag', samples_mag_i, [-scales.mag, scales.mag]);
// fetch currently enabled plots
var checkboxes = [];
$('.tab-sensors .info .checkboxes input').each(function () {
checkboxes.push($(this).prop('checked'));
});
// timer initialization
GUI.interval_kill_all(['status_pull']);
// data pulling timers
if (checkboxes[0] || checkboxes[1] || checkboxes[2]) {
GUI.interval_add('IMU_pull', function imu_data_pull() {
MSP.send_message(MSPCodes.MSP_RAW_IMU, false, false, update_imu_graphs);
}, fastest, true);
}
if (checkboxes[3]) {
GUI.interval_add('altitude_pull', function altitude_data_pull() {
MSP.send_message(MSPCodes.MSP_ALTITUDE, false, false, update_altitude_graph);
}, rates.baro, true);
}
if (checkboxes[4]) {
GUI.interval_add('sonar_pull', function sonar_data_pull() {
MSP.send_message(MSPCodes.MSP_SONAR, false, false, update_sonar_graphs);
}, rates.sonar, true);
}
if (checkboxes[5]) {
GUI.interval_add('debug_pull', function debug_data_pull() {
MSP.send_message(MSPCodes.MSP_DEBUG, false, false, update_debug_graphs);
}, rates.debug, true);
}
function update_imu_graphs() {
if (checkboxes[0]) {
updateGraphHelperSize(gyroHelpers);
samples_gyro_i = addSampleToData(gyro_data, samples_gyro_i, SENSOR_DATA.gyroscope);
drawGraph(gyroHelpers, gyro_data, samples_gyro_i);
raw_data_text_ements.x[0].text(SENSOR_DATA.gyroscope[0].toFixed(2));
raw_data_text_ements.y[0].text(SENSOR_DATA.gyroscope[1].toFixed(2));
raw_data_text_ements.z[0].text(SENSOR_DATA.gyroscope[2].toFixed(2));
}
if (checkboxes[1]) {
updateGraphHelperSize(accelHelpers);
samples_accel_i = addSampleToData(accel_data, samples_accel_i, SENSOR_DATA.accelerometer);
drawGraph(accelHelpers, accel_data, samples_accel_i);
raw_data_text_ements.x[1].text(SENSOR_DATA.accelerometer[0].toFixed(2));
raw_data_text_ements.y[1].text(SENSOR_DATA.accelerometer[1].toFixed(2));
raw_data_text_ements.z[1].text(SENSOR_DATA.accelerometer[2].toFixed(2));
}
if (checkboxes[2]) {
updateGraphHelperSize(magHelpers);
samples_mag_i = addSampleToData(mag_data, samples_mag_i, SENSOR_DATA.magnetometer);
drawGraph(magHelpers, mag_data, samples_mag_i);
raw_data_text_ements.x[2].text(SENSOR_DATA.magnetometer[0].toFixed(2));
raw_data_text_ements.y[2].text(SENSOR_DATA.magnetometer[1].toFixed(2));
raw_data_text_ements.z[2].text(SENSOR_DATA.magnetometer[2].toFixed(2));
}
}
function update_altitude_graph() {
updateGraphHelperSize(baroHelpers);
samples_baro_i = addSampleToData(baro_data, samples_baro_i, [SENSOR_DATA.altitude]);
drawGraph(baroHelpers, baro_data, samples_baro_i);
raw_data_text_ements.x[3].text(SENSOR_DATA.altitude.toFixed(2));
}
function update_sonar_graphs() {
updateGraphHelperSize(sonarHelpers);
samples_sonar_i = addSampleToData(sonar_data, samples_sonar_i, [SENSOR_DATA.sonar]);
drawGraph(sonarHelpers, sonar_data, samples_sonar_i);
raw_data_text_ements.x[4].text(SENSOR_DATA.sonar.toFixed(2));
}
function update_debug_graphs() {
for (var i = 0; i < 4; i++) {
updateGraphHelperSize(debugHelpers[i]);
addSampleToData(debug_data[i], samples_debug_i, [SENSOR_DATA.debug[i]]);
drawGraph(debugHelpers[i], debug_data[i], samples_debug_i);
raw_data_text_ements.x[5 + i].text(SENSOR_DATA.debug[i]);
}
samples_debug_i++;
}
});
// status data pulled via separate timer with static speed
GUI.interval_add('status_pull', function status_pull() {
MSP.send_message(MSPCodes.MSP_STATUS);
}, 250, true);
GUI.content_ready(callback);
});
};
TABS.sensors.cleanup = function (callback) {
serial.emptyOutputBuffer();
if (callback) callback();
};

185
src/js/tabs/servos.js Executable file
View file

@ -0,0 +1,185 @@
'use strict';
TABS.servos = {};
TABS.servos.initialize = function (callback) {
var self = this;
if (GUI.active_tab != 'servos') {
GUI.active_tab = 'servos';
}
function get_servo_configurations() {
MSP.send_message(MSPCodes.MSP_SERVO_CONFIGURATIONS, false, false, get_servo_mix_rules);
}
function get_servo_mix_rules() {
MSP.send_message(MSPCodes.MSP_SERVO_MIX_RULES, false, false, get_rc_data);
}
function get_rc_data() {
MSP.send_message(MSPCodes.MSP_RC, false, false, get_boxnames_data);
}
function get_boxnames_data() {
MSP.send_message(MSPCodes.MSP_BOXNAMES, false, false, load_html);
}
function load_html() {
$('#content').load("./tabs/servos.html", process_html);
}
get_servo_configurations();
function update_ui() {
if (semver.lt(CONFIG.apiVersion, "1.12.0") || SERVO_CONFIG.length == 0) {
$(".tab-servos").removeClass("supported");
return;
}
$(".tab-servos").addClass("supported");
var servoCheckbox = '';
var servoHeader = '';
for (var i = 0; i < RC.active_channels-4; i++) {
servoHeader = servoHeader + '\
<th >A' + (i+1) + '</th>\
';
}
servoHeader = servoHeader + '<th style="width: 110px" i18n="servosDirectionAndRate"></th>';
for (var i = 0; i < RC.active_channels; i++) {
servoCheckbox = servoCheckbox + '\
<td class="channel"><input type="checkbox"/></td>\
';
}
$('div.tab-servos table.fields tr.main').append(servoHeader);
function process_servos(name, alternate, obj) {
$('div.supported_wrapper').show();
$('div.tab-servos table.fields').append('\
<tr> \
<td style="text-align: center">' + name + '</td>\
<td class="middle"><input type="number" min="500" max="2500" value="' + SERVO_CONFIG[obj].middle + '" /></td>\
<td class="min"><input type="number" min="500" max="2500" value="' + SERVO_CONFIG[obj].min +'" /></td>\
<td class="max"><input type="number" min="500" max="2500" value="' + SERVO_CONFIG[obj].max +'" /></td>\
' + servoCheckbox + '\
<td class="direction">\
</td>\
</tr> \
');
if (SERVO_CONFIG[obj].indexOfChannelToForward >= 0) {
$('div.tab-servos table.fields tr:last td.channel input').eq(SERVO_CONFIG[obj].indexOfChannelToForward).prop('checked', true);
}
// adding select box and generating options
$('div.tab-servos table.fields tr:last td.direction').append('\
<select class="rate" name="rate"></select>\
');
var select = $('div.tab-servos table.fields tr:last td.direction select');
for (var i = 100; i > -101; i--) {
select.append('<option value="' + i + '">Rate: ' + i + '%</option>');
}
// select current rate
select.val(SERVO_CONFIG[obj].rate);
$('div.tab-servos table.fields tr:last').data('info', {'obj': obj});
// UI hooks
// only one checkbox for indicating a channel to forward can be selected at a time, perhaps a radio group would be best here.
$('div.tab-servos table.fields tr:last td.channel input').click(function () {
if($(this).is(':checked')) {
$(this).parent().parent().find('.channel input').not($(this)).prop('checked', false);
}
});
}
function servos_update(save_configuration_to_eeprom) {
$('div.tab-servos table.fields tr:not(".main")').each(function () {
var info = $(this).data('info');
var selection = $('.channel input', this);
var channelIndex = parseInt(selection.index(selection.filter(':checked')));
if (channelIndex == -1) {
channelIndex = undefined;
}
SERVO_CONFIG[info.obj].indexOfChannelToForward = channelIndex;
SERVO_CONFIG[info.obj].middle = parseInt($('.middle input', this).val());
SERVO_CONFIG[info.obj].min = parseInt($('.min input', this).val());
SERVO_CONFIG[info.obj].max = parseInt($('.max input', this).val());
var val = parseInt($('.direction select', this).val());
SERVO_CONFIG[info.obj].rate = val;
});
//
// send data to FC
//
mspHelper.sendServoConfigurations(send_servo_mixer_rules);
function send_servo_mixer_rules() {
mspHelper.sendServoConfigurations(save_to_eeprom);
}
function save_to_eeprom() {
if (save_configuration_to_eeprom) {
MSP.send_message(MSPCodes.MSP_EEPROM_WRITE, false, false, function () {
GUI.log(chrome.i18n.getMessage('servosEepromSave'));
});
}
}
}
// drop previous table
$('div.tab-servos table.fields tr:not(:first)').remove();
for (var servoIndex = 0; servoIndex < 8; servoIndex++) {
process_servos('Servo ' + servoIndex, '', servoIndex, false);
}
// UI hooks for dynamically generated elements
$('table.directions select, table.directions input, table.fields select, table.fields input').change(function () {
if ($('div.live input').is(':checked')) {
// apply small delay as there seems to be some funky update business going wrong
GUI.timeout_add('servos_update', servos_update, 10);
}
});
$('a.update').click(function () {
servos_update(true);
});
}
function process_html() {
update_ui();
// translate to user-selected language
localize();
// status data pulled via separate timer with static speed
GUI.interval_add('status_pull', function () {
MSP.send_message(MSPCodes.MSP_STATUS);
}, 250, true);
GUI.content_ready(callback);
}
};
TABS.servos.cleanup = function (callback) {
if (callback) callback();
};

270
src/js/tabs/setup.js Executable file
View file

@ -0,0 +1,270 @@
'use strict';
TABS.setup = {
yaw_fix: 0.0
};
TABS.setup.initialize = function (callback) {
var self = this;
if (GUI.active_tab != 'setup') {
GUI.active_tab = 'setup';
}
function load_status() {
MSP.send_message(MSPCodes.MSP_STATUS, false, false, load_mixer_config);
}
function load_mixer_config() {
MSP.send_message(MSPCodes.MSP_MIXER_CONFIG, false, false, load_html);
}
function load_html() {
$('#content').load("./tabs/setup.html", process_html);
}
MSP.send_message(MSPCodes.MSP_ACC_TRIM, false, false, load_status);
function process_html() {
// translate to user-selected language
localize();
if (semver.lt(CONFIG.apiVersion, CONFIGURATOR.backupRestoreMinApiVersionAccepted)) {
$('#content .backup').addClass('disabled');
$('#content .restore').addClass('disabled');
GUI.log(chrome.i18n.getMessage('initialSetupBackupAndRestoreApiVersion', [CONFIG.apiVersion, CONFIGURATOR.backupRestoreMinApiVersionAccepted]));
}
// initialize 3D Model
self.initModel();
// set roll in interactive block
$('span.roll').text(chrome.i18n.getMessage('initialSetupAttitude', [0]));
// set pitch in interactive block
$('span.pitch').text(chrome.i18n.getMessage('initialSetupAttitude', [0]));
// set heading in interactive block
$('span.heading').text(chrome.i18n.getMessage('initialSetupAttitude', [0]));
// check if we have accelerometer and magnetometer
if (!have_sensor(CONFIG.activeSensors, 'acc')) {
$('a.calibrateAccel').addClass('disabled');
$('default_btn').addClass('disabled');
}
if (!have_sensor(CONFIG.activeSensors, 'mag')) {
$('a.calibrateMag').addClass('disabled');
$('default_btn').addClass('disabled');
}
self.initializeInstruments();
$('#arming-disable-flag-row').attr('title', chrome.i18n.getMessage('initialSetupArmingDisableFlagsTooltip'));
// UI Hooks
$('a.calibrateAccel').click(function () {
var self = $(this);
if (!self.hasClass('calibrating')) {
self.addClass('calibrating');
// During this period MCU won't be able to process any serial commands because its locked in a for/while loop
// until this operation finishes, sending more commands through data_poll() will result in serial buffer overflow
GUI.interval_pause('setup_data_pull');
MSP.send_message(MSPCodes.MSP_ACC_CALIBRATION, false, false, function () {
GUI.log(chrome.i18n.getMessage('initialSetupAccelCalibStarted'));
$('#accel_calib_running').show();
$('#accel_calib_rest').hide();
});
GUI.timeout_add('button_reset', function () {
GUI.interval_resume('setup_data_pull');
GUI.log(chrome.i18n.getMessage('initialSetupAccelCalibEnded'));
self.removeClass('calibrating');
$('#accel_calib_running').hide();
$('#accel_calib_rest').show();
}, 2000);
}
});
$('a.calibrateMag').click(function () {
var self = $(this);
if (!self.hasClass('calibrating') && !self.hasClass('disabled')) {
self.addClass('calibrating');
MSP.send_message(MSPCodes.MSP_MAG_CALIBRATION, false, false, function () {
GUI.log(chrome.i18n.getMessage('initialSetupMagCalibStarted'));
$('#mag_calib_running').show();
$('#mag_calib_rest').hide();
});
GUI.timeout_add('button_reset', function () {
GUI.log(chrome.i18n.getMessage('initialSetupMagCalibEnded'));
self.removeClass('calibrating');
$('#mag_calib_running').hide();
$('#mag_calib_rest').show();
}, 30000);
}
});
var dialogConfirmReset = $('.dialogConfirmReset')[0];
$('a.resetSettings').click(function () {
dialogConfirmReset.showModal();
});
$('.dialogConfirmReset-cancelbtn').click(function() {
dialogConfirmReset.close();
});
$('.dialogConfirmReset-confirmbtn').click(function() {
dialogConfirmReset.close();
MSP.send_message(MSPCodes.MSP_RESET_CONF, false, false, function () {
GUI.log(chrome.i18n.getMessage('initialSetupSettingsRestored'));
GUI.tab_switch_cleanup(function () {
TABS.setup.initialize();
});
});
});
// display current yaw fix value (important during tab re-initialization)
$('div#interactive_block > a.reset').text(chrome.i18n.getMessage('initialSetupButtonResetZaxisValue', [self.yaw_fix]));
// reset yaw button hook
$('div#interactive_block > a.reset').click(function () {
self.yaw_fix = SENSOR_DATA.kinematics[2] * - 1.0;
$(this).text(chrome.i18n.getMessage('initialSetupButtonResetZaxisValue', [self.yaw_fix]));
console.log('YAW reset to 0 deg, fix: ' + self.yaw_fix + ' deg');
});
$('#content .backup').click(function () {
if ($(this).hasClass('disabled')) {
return;
}
configuration_backup(function () {
GUI.log(chrome.i18n.getMessage('initialSetupBackupSuccess'));
});
});
$('#content .restore').click(function () {
if ($(this).hasClass('disabled')) {
return;
}
configuration_restore(function () {
// get latest settings
TABS.setup.initialize();
GUI.log(chrome.i18n.getMessage('initialSetupRestoreSuccess'));
});
});
// cached elements
var bat_voltage_e = $('.bat-voltage'),
bat_mah_drawn_e = $('.bat-mah-drawn'),
bat_mah_drawing_e = $('.bat-mah-drawing'),
rssi_e = $('.rssi'),
arming_disable_flags_e = $('.arming-disable-flags'),
gpsFix_e = $('.gpsFix'),
gpsSats_e = $('.gpsSats'),
gpsLat_e = $('.gpsLat'),
gpsLon_e = $('.gpsLon'),
roll_e = $('dd.roll'),
pitch_e = $('dd.pitch'),
heading_e = $('dd.heading');
if (semver.lt(CONFIG.apiVersion, "1.36.0")) {
arming_disable_flags_e.hide();
}
function get_slow_data() {
MSP.send_message(MSPCodes.MSP_STATUS, false, false, function() {
var armingString = '';
if (CONFIG.armingDisableFlags == 0) {
armingString = chrome.i18n.getMessage('initialSetupArmingAllowed');
} else {
var flagIndicies = [];
for (var i = 0; i < 32; i++) {
if (CONFIG.armingDisableFlags & (1 << i)) {
flagIndicies.push(i + 1);
}
}
armingString = flagIndicies;
}
arming_disable_flags_e.text(armingString);
});
MSP.send_message(MSPCodes.MSP_ANALOG, false, false, function () {
bat_voltage_e.text(chrome.i18n.getMessage('initialSetupBatteryValue', [ANALOG.voltage]));
bat_mah_drawn_e.text(chrome.i18n.getMessage('initialSetupBatteryMahValue', [ANALOG.mAhdrawn]));
bat_mah_drawing_e.text(chrome.i18n.getMessage('initialSetupBatteryAValue', [ANALOG.amperage.toFixed(2)]));
rssi_e.text(chrome.i18n.getMessage('initialSetupRSSIValue', [((ANALOG.rssi / 1023) * 100).toFixed(0)]));
});
if (have_sensor(CONFIG.activeSensors, 'gps')) {
MSP.send_message(MSPCodes.MSP_RAW_GPS, false, false, function () {
gpsFix_e.html((GPS_DATA.fix) ? chrome.i18n.getMessage('gpsFixTrue') : chrome.i18n.getMessage('gpsFixFalse'));
gpsSats_e.text(GPS_DATA.numSat);
gpsLat_e.text((GPS_DATA.lat / 10000000).toFixed(4) + ' deg');
gpsLon_e.text((GPS_DATA.lon / 10000000).toFixed(4) + ' deg');
});
}
}
function get_fast_data() {
MSP.send_message(MSPCodes.MSP_ATTITUDE, false, false, function () {
roll_e.text(chrome.i18n.getMessage('initialSetupAttitude', [SENSOR_DATA.kinematics[0]]));
pitch_e.text(chrome.i18n.getMessage('initialSetupAttitude', [SENSOR_DATA.kinematics[1]]));
heading_e.text(chrome.i18n.getMessage('initialSetupAttitude', [SENSOR_DATA.kinematics[2]]));
self.renderModel();
self.updateInstruments();
});
}
GUI.interval_add('setup_data_pull_fast', get_fast_data, 33, true); // 30 fps
GUI.interval_add('setup_data_pull_slow', get_slow_data, 250, true); // 4 fps
GUI.content_ready(callback);
}
};
TABS.setup.initializeInstruments = function() {
var options = {size:90, showBox : false, img_directory: 'images/flightindicators/'};
var attitude = $.flightIndicator('#attitude', 'attitude', options);
var heading = $.flightIndicator('#heading', 'heading', options);
this.updateInstruments = function() {
attitude.setRoll(SENSOR_DATA.kinematics[0]);
attitude.setPitch(SENSOR_DATA.kinematics[1]);
heading.setHeading(SENSOR_DATA.kinematics[2]);
};
};
TABS.setup.initModel = function () {
this.model = new Model($('.model-and-info #canvas_wrapper'), $('.model-and-info #canvas'));
$(window).on('resize', $.proxy(this.model.resize, this.model));
};
TABS.setup.renderModel = function () {
var x = (SENSOR_DATA.kinematics[1] * -1.0) * 0.017453292519943295,
y = ((SENSOR_DATA.kinematics[2] * -1.0) - this.yaw_fix) * 0.017453292519943295,
z = (SENSOR_DATA.kinematics[0] * -1.0) * 0.017453292519943295;
this.model.rotateTo(x, y, z);
};
TABS.setup.cleanup = function (callback) {
if (this.model) {
$(window).off('resize', $.proxy(this.model.resize, this.model));
}
if (callback) callback();
};

71
src/js/tabs/setup_osd.js Normal file
View file

@ -0,0 +1,71 @@
'use strict';
TABS.setup_osd = {
};
TABS.setup_osd.initialize = function (callback) {
var self = this;
if (GUI.active_tab != 'setup_osd') {
GUI.active_tab = 'setup_osd';
// Disabled on merge into betaflight-configurator
//googleAnalytics.sendAppView('Setup OSD');
}
function load_status() {
MSP.send_message(MSPCodes.MSP_STATUS, false, false, load_html);
}
function load_html() {
$('#content').load("./tabs/setup_osd.html", process_html);
}
load_status();
function process_html() {
$('.tab-setup-osd .info').hide(); // requires an MSP update
var osdVideoModes = [
'AUTO',
'NTSC',
'PAL'
];
// translate to user-selected language
localize();
$('a.resetSettings').click(function () {
MSP.send_message(MSPCodes.MSP_RESET_CONF, false, false, function () {
GUI.log(chrome.i18n.getMessage('initialSetupSettingsRestored'));
GUI.tab_switch_cleanup(function () {
TABS.setup_osd.initialize();
});
});
});
function get_slow_data() {
/* FIXME requires MSP update
MSP.send_message(MSPCodes.MSP_OSD_VIDEO_STATUS, false, false, function () {
var element;
element = $('.video-mode');
var osdVideoMode = osdVideoModes[OSD_VIDEO_STATE.video_mode];
element.text(osdVideoMode);
element = $('.camera-connected');
element.text(OSD_VIDEO_STATE.camera_connected ? chrome.i18n.getMessage('osdSetupCameraConnectedValueYes') : chrome.i18n.getMessage('osdSetupCameraConnectedValueNo'));
});
*/
}
GUI.interval_add('setup_data_pull_slow', get_slow_data, 250, true); // 4 fps
GUI.content_ready(callback);
}
};
TABS.setup_osd.cleanup = function (callback) {
if (callback) callback();
};

346
src/js/tabs/transponder.js Normal file
View file

@ -0,0 +1,346 @@
'use strict';
TABS.transponder = {
available: false
};
TABS.transponder.initialize = function(callback, scrollPosition) {
let _persistentInputValues = {};
let dataTypes = {
NONE: 0,
TEXT: 1,
LIST: 2,
};
// CONFIGURATION HERE FOR ADD NEW TRANSPONDER
let transponderConfigurations = {
0: {
dataType: dataTypes.NONE // empty
}, //NONE
1: {
dataType: dataTypes.TEXT //<input type="text">
}, //ilap
2: {
dataType: dataTypes.LIST, // <select>...</select>
dataOptions: {
'ID 1': 'E00370FC0FFE07E0FF',
'ID 2': '007C003EF800FC0FFE',
'ID 3': 'F8811FF8811FFFC7FF',
'ID 4': '007C003EF81F800FFE',
'ID 5': 'F00FFF00FFF00FF0FF',
'ID 6': '007CF0C1071F7C00F0',
'ID 7': 'E003F03F00FF03F0C1',
'ID 8': '00FC0FFE071F3E00FE',
'ID 9': 'E083BFF00F9E38C0FF',
}
}, //arcitimer
3: {
dataType: dataTypes.LIST, // <select>...</select>
dataOptions: {
'0':'00',
'1':'01',
'2':'02',
'3':'03',
'4':'04',
'5':'05',
'6':'06',
'7':'07',
'8':'08',
'9':'09',
'10':'0A',
'11':'0B',
'12':'0C',
'13':'0D',
'14':'0E',
'15':'0F',
'16':'10',
'17':'11',
'18':'12',
'19':'13',
'20':'14',
'21':'15',
'22':'16',
'23':'17',
'24':'18',
'25':'19',
'26':'1A',
'27':'1B',
'28':'1C',
'29':'1D',
'30':'1E',
'31':'1F',
'32':'20',
'33':'21',
'34':'22',
'35':'23',
'36':'24',
'37':'25',
'38':'26',
'39':'27',
'40':'28',
'41':'29',
'42':'2A',
'43':'2B',
'44':'2C',
'45':'2D',
'46':'2E',
'47':'2F',
'48':'30',
'49':'31',
'50':'32',
'51':'33',
'52':'34',
'53':'35',
'54':'36',
'55':'37',
'56':'38',
'57':'39',
'58':'3A',
'59':'3B',
'60':'3C',
'61':'3D',
'62':'3E',
'63':'3F',
}
}, //ERLT
};
/////////////////////////////////////////////
if ( GUI.active_tab != 'transponder' ) {
GUI.active_tab = 'transponder';
// Disabled on merge into betaflight-configurator
//googleAnalytics.sendAppView('Transponder');
}
// transponder supported added in MSP API Version 1.16.0
if ( CONFIG ) {
TABS.transponder.available = semver.gte(CONFIG.apiVersion, "1.16.0");
}
//////////////
if ( !TABS.transponder.available ) {
load_html();
return;
}
function load_html() {
$('#content').load("./tabs/transponder.html", process_html);
}
//HELPERS
// Convert a hex string to a byte array
function hexToBytes(hex) {
var bytes = [];
for ( let c = 0; c < hex.length; c += 2 ) {
bytes.push(~parseInt(hex.substr(c, 2), 16));
}
return bytes;
}
function pad(n, width) {
n = n + '';
return n.length >= width ? n : new Array(width - n.length + 1).join('0') + n;
}
// Convert a byte array to a hex string
function bytesToHex(bytes) {
var hex = [];
for ( let i = 0; i < bytes.length; i++ ) {
hex.push(pad(((~bytes[i]) & 0xFF).toString(16), 2));
}
return hex.join("").toUpperCase();
}
/////////////
function fillByTransponderProviders(transponderProviders, transponderProviderID, toggleTransponderType) {
let transponderTypeSelect = $('#transponder_type_select');
transponderTypeSelect.attr('data-defaultValue', transponderProviderID);
transponderTypeSelect.off('change').change(toggleTransponderType);
transponderTypeSelect.html('');
//build radio buttons
if (transponderProviders.length > 1) {
transponderTypeSelect.append(
$('<option>').attr('value', 0).html(chrome.i18n.getMessage("transponderType0")) // NONE
);
}
for ( let transponderProvidersKey in transponderProviders ) {
let transponderProvider = transponderProviders[transponderProvidersKey];
if ( transponderProvider.hasOwnProperty('id') ) {
transponderTypeSelect.append(
$('<option>').attr('value', transponderProvider.id).html(chrome.i18n.getMessage("transponderType" + transponderProvider.id))
);
}
}
transponderTypeSelect.val(transponderProviderID);
}
function buildDataBlockForTransponderProviders(transponderProvider, data, clearValue) {
var clearValue = clearValue || false;
$('#transponderConfiguration').html('');
$('#transponderConfiguration').hide();
$('#transponderHelpBox').hide();
if ( !transponderProvider ) {
return;
}
let template = $('#transponder-configuration-template').clone();
template.find('.spacer_box_title').html(chrome.i18n.getMessage("transponderData" + transponderProvider.id));
template.find('.dataHelp').html(chrome.i18n.getMessage("transponderDataHelp" + transponderProvider.id));
if ( chrome.i18n.getMessage("transponderHelp" + transponderProvider.id).length ) {
$('#transponderHelp').html(chrome.i18n.getMessage("transponderHelp" + transponderProvider.id));
$('#transponderHelpBox').show();
}
let transponderConfiguration = transponderConfigurations[transponderProvider.id];
let dataInput = null;
switch ( transponderConfiguration.dataType ) {
case dataTypes.TEXT:
dataInput = $('<input>').attr('type', 'text').attr('maxlength', parseInt(transponderProvider.dataLength) * 2);
if ( !clearValue ) {
dataInput.val(data);
} else {
dataInput.val(_persistentInputValues[transponderProvider.id] || '');
}
break;
case dataTypes.LIST:
dataInput = $('<select>');
for ( let dataOptionsKey in transponderConfiguration.dataOptions ) {
let dataOptions = transponderConfiguration.dataOptions[dataOptionsKey];
dataInput.append($('<option>').val(dataOptions).html(dataOptionsKey));
}
if ( dataInput.find("option[value='" + data + "']").length > 0 && !clearValue ) {
dataInput.val(data);
} else {
dataInput.val(_persistentInputValues[transponderProvider.id] || '');
}
break;
default:
return;
}
if ( !clearValue ) {
_persistentInputValues[transponderProvider.id] = data;
}
let changedInputValue = function() {
let dataString = $(this).val();
let hexRegExp = new RegExp('[0-9a-fA-F]{' + (transponderProvider.dataLength * 2) + '}', 'gi');
if ( !dataString.match(hexRegExp) ) {
TRANSPONDER.data = [];
} else {
TRANSPONDER.data = hexToBytes(dataString);
}
_persistentInputValues[transponderProvider.id] = dataString;
};
dataInput.change(changedInputValue).keyup(changedInputValue);
template.find('.input_block').html(dataInput);
$('#transponder-configuration').html(template.show());
}
/**
* this function is called from select click scope
*/
function toggleTransponderType() {
TRANSPONDER.provider = $(this).val();
let defaultProvider = $(this).attr('data-defaultValue');
if ( defaultProvider == $(this).val() ) {
$('.save_reboot').hide();
$('.save_no_reboot').show();
} else {
$('.save_no_reboot').hide();
$('.save_reboot').show();
}
let clearValue = true;
buildDataBlockForTransponderProviders(TRANSPONDER.providers.find(function(provider) {
return provider.id == TRANSPONDER.provider;
}), bytesToHex(TRANSPONDER.data), clearValue);
}
MSP.send_message(MSPCodes.MSP_TRANSPONDER_CONFIG, false, false, load_html);
function process_html() {
$(".tab-transponder").toggleClass("transponder-supported", TABS.transponder.available && TRANSPONDER.supported);
localize();
if ( TABS.transponder.available && TRANSPONDER.providers.length > 0 ) {
fillByTransponderProviders(TRANSPONDER.providers, TRANSPONDER.provider, toggleTransponderType);
buildDataBlockForTransponderProviders(TRANSPONDER.providers.find(function(provider) {
return provider.id == TRANSPONDER.provider;
}), bytesToHex(TRANSPONDER.data));
$('a.save').click(function() {
let _this = this;
function save_transponder_data() {
MSP.send_message(MSPCodes.MSP_SET_TRANSPONDER_CONFIG, mspHelper.crunch(MSPCodes.MSP_SET_TRANSPONDER_CONFIG), false, save_to_eeprom);
}
function save_to_eeprom() {
MSP.send_message(MSPCodes.MSP_EEPROM_WRITE, false, false, function() {
GUI.log(chrome.i18n.getMessage('transponderEepromSaved'));
if ( $(_this).hasClass('reboot') ) {
GUI.tab_switch_cleanup(function() {
MSP.send_message(MSPCodes.MSP_SET_REBOOT, false, false, reinitialize);
});
}
});
}
if (TRANSPONDER.provider !== "0" && TRANSPONDER.data.length !== TRANSPONDER.providers.find(function(provider) {
return provider.id == TRANSPONDER.provider;
}).dataLength ) {
GUI.log(chrome.i18n.getMessage('transponderDataInvalid'));
} else {
save_transponder_data();
}
});
}
function reinitialize() {
GUI.log(chrome.i18n.getMessage('deviceRebooting'));
if ( BOARD.find_board_definition(CONFIG.boardIdentifier).vcp ) {
$('a.connect').click();
GUI.timeout_add('start_connection', function start_connection() {
$('a.connect').click();
}, 2500);
} else {
GUI.timeout_add('waiting_for_bootup', function waiting_for_bootup() {
MSP.send_message(MSPCodes.MSP_IDENT, false, false, function() {
GUI.log(chrome.i18n.getMessage('deviceReady'));
TABS.configuration.initialize(false, $('#content').scrollTop());
});
}, 1500);
}
}
GUI.content_ready(callback);
}
};
TABS.transponder.cleanup = function(callback) {
if ( callback ) callback();
};