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:
parent
24c81375a4
commit
fd8e47706d
141 changed files with 248 additions and 263 deletions
122
src/js/eventPage.js
Executable file
122
src/js/eventPage.js
Executable 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;
|
||||
}
|
31
src/js/libraries/bluebird.min.js
vendored
31
src/js/libraries/bluebird.min.js
vendored
File diff suppressed because one or more lines are too long
5
src/js/libraries/d3.min.js
vendored
5
src/js/libraries/d3.min.js
vendored
File diff suppressed because one or more lines are too long
|
@ -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);
|
||||
}
|
31
src/js/libraries/inflection.min.js
vendored
31
src/js/libraries/inflection.min.js
vendored
|
@ -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;}));
|
|
@ -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;
|
||||
}
|
2
src/js/libraries/jbox/jBox.min.js
vendored
2
src/js/libraries/jbox/jBox.min.js
vendored
File diff suppressed because one or more lines are too long
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
4
src/js/libraries/jquery-2.1.4.min.js
vendored
4
src/js/libraries/jquery-2.1.4.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
13
src/js/libraries/jquery-ui-1.11.2.min.js
vendored
13
src/js/libraries/jquery-ui-1.11.2.min.js
vendored
File diff suppressed because one or more lines are too long
13
src/js/libraries/jquery-ui-1.11.4.min.js
vendored
13
src/js/libraries/jquery-ui-1.11.4.min.js
vendored
File diff suppressed because one or more lines are too long
|
@ -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);
|
|
@ -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 ));
|
|
@ -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
4
src/js/libraries/jquery.nouislider.min.css
vendored
4
src/js/libraries/jquery.nouislider.min.css
vendored
|
@ -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}
|
|
@ -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
|
@ -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
|
@ -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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
};
|
869
src/js/libraries/three/three.min.js
vendored
869
src/js/libraries/three/three.min.js
vendored
File diff suppressed because one or more lines are too long
606
src/js/main.js
Normal file
606
src/js/main.js
Normal 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
285
src/js/tabs/adjustments.js
Normal 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
326
src/js/tabs/auxiliary.js
Normal 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
281
src/js/tabs/cli.js
Normal 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 += '<';
|
||||
break;
|
||||
case 62:
|
||||
text += '>';
|
||||
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
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
349
src/js/tabs/failsafe.js
Normal 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
512
src/js/tabs/firmware_flasher.js
Executable 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
158
src/js/tabs/gps.js
Normal 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
20
src/js/tabs/help.js
Normal 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
42
src/js/tabs/landing.js
Normal 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
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
322
src/js/tabs/logging.js
Normal 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
76
src/js/tabs/map.js
Normal 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
634
src/js/tabs/motors.js
Normal 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();
|
||||
};
|
509
src/js/tabs/onboard_logging.js
Normal file
509
src/js/tabs/onboard_logging.js
Normal 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
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
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
349
src/js/tabs/ports.js
Normal 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
343
src/js/tabs/power.js
Normal 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
512
src/js/tabs/receiver.js
Normal 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
185
src/js/tabs/receiver_msp.js
Normal 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
470
src/js/tabs/sensors.js
Normal 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
185
src/js/tabs/servos.js
Executable 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
270
src/js/tabs/setup.js
Executable 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
71
src/js/tabs/setup_osd.js
Normal 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
346
src/js/tabs/transponder.js
Normal 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();
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue