<commit_before>import Multiselect from 'vue-multiselect';
import { library, dom } from '@fortawesome/fontawesome-svg-core';
import { fab } from '@fortawesome/free-brands-svg-icons';
import { far } from '@fortawesome/free-regular-svg-icons';
import { fas } from '@fortawesome/free-solid-svg-icons';
import VModal from 'vue-js-modal';
import Axios from 'axios';
import VueRouter from 'vue-router';

import App from './App.vue';
import MainView from './components/MainView.vue';
import ContextDetail from './components/ContextDetail.vue';
import Login from './components/Login.vue';
// Tools
import Bibliography from './components/BibliographyTable.vue';
import BibliographyItemModal from './components/BibliographyItemModal.vue';
// Settings
import Users from './components/Users.vue';
import Roles from './components/Roles.vue';
import DataModel from './components/DataModel.vue';
import DataModelDetailView from './components/DataModelDetailView.vue';
import Preferences from './components/Preferences.vue';
import UserPreferences from './components/UserPreferences.vue';

import VueUploadComponent from 'vue-upload-component';
import moment from 'moment';
import VCalendar from 'v-calendar';
import VeeValidate from 'vee-validate';
import Notifications from 'vue-notification';
import SpacialistPluginSystem from './plugin.js';
import VTooltip from 'v-tooltip';

library.add(fas, far, fab);
dom.watch(); // search for  tags to replace with <svg>

/**
 * First we will load all of this project's JavaScript dependencies which
 * includes Vue and other libraries. It is a great starting point when
 * building robust, powerful web applications using Vue and Laravel.
 */

require('popper.js');
require('bootstrap');
window.Vue = require('vue');
window._ = require('lodash');
$ = jQuery  = window.$ = window.jQuery = require('jquery');
require('./globals.js');

Axios.defaults.baseURL = '/api/v1';

/**
 * Next, we will create a fresh Vue application instance and attach it to
 * the page. Then, you may begin adding components to this application
 * or customize the JavaScript scaffolding to fit your unique needs.
 */
Vue.prototype.$http = Axios;
Vue.axios = Axios;
Vue.use(VueRouter);

Vue.use(SpacialistPluginSystem);

Vue.use(VModal);
Vue.use(VeeValidate);
Vue.use(Notifications);
Vue.use(VCalendar, {
    firstDayOfWeek: 2,
    popoverVisibility: 'focus'
});
Vue.use(VTooltip, {
    popover: {
        defaultBaseClass: 'popover',
        defaultInnerClass: 'popover-body',
        defaultArrowClass: 'arrow'
    }
});

const router = new VueRouter({
    scrollBehavior(to, from, savedPosition) {
        return {
            x: 0,
            y: 0
        };
    },
    routes: [
        // deprecated pre-0.6 routes
        {
            path: '/s',
            redirect: { name: 'home' },
            children: [
                {
                    path: 'context/:id',
                    redirect: to => {
                        return {
                            name: 'contextdetail',
                            params: {
                                id: to.params.id
                            }
                        }
                    },
                    children: [{
                        path: 'sources/:aid',
                        redirect: to => {
                            return {
                                // TODO not contextdetail
                                name: 'contextdetail',
                                params: {
                                    id: to.params.id,
                                    aid: to.params.aid,
                                }
                            }
                        }
                    }]
                },
                {
                    path: 'f/:id',
                    redirect: to => {
                        return {
                            name: 'file',
                            params: {
                                id: to.params.id
                            }
                        }
                    }
                },
                {
                    path: 'user',
                    redirect: { name: 'users' },
                    // TODO user edit route (redirect to users or add it)
                },
                {
                    path: 'role',
                    redirect: { name: 'roles' },
                    // TODO role edit route (redirect to roles or add it)
                },
                {
                    path: 'editor/data-model',
                    redirect: { name: 'dme' },
                    children: [{
                        path: 'contexttype/:id',
                        redirect: to => {
                            return {
                                name: 'dmdetail',
                                params: {
                                    id: to.params.id
                                }
                            }
                        }
                    }]
                },
                {
                    path: 'preferences/:id',
                    redirect: to => {
                        return {
                            name: 'userpreferences',
                            params: {
                                id: to.params.id
                            }
                        }
                    }
                }
            ]
        },
        {
            path: '/',
            name: 'home',
            component: MainView,
            children: [
                {
                    path: 'e/:id',
                    name: 'contextdetail',
                    component: ContextDetail
                }
            ],
            meta: {
                auth: true
            }
        },
        // Tools
        {
            path: '/bibliography',
            name: 'bibliography',
            component: Bibliography,
            children: [
                {
                    path: 'edit/:id',
                    name: 'bibedit',
                    component: BibliographyItemModal
                },
                {
                    path: 'new',
                    name: 'bibnew',
                    component: BibliographyItemModal
                }
            ],
            meta: {
                auth: true
            }
        },
        {
            path: '/login',
            name: 'login',
            component: Login,
            meta: {
                auth: false
            }
        },
        // Settings
        {
            path: '/mg/users',
            name: 'users',
            component: Users,
            meta: {
                auth: true
            }
        },
        {
            path: '/mg/roles',
            name: 'roles',
            component: Roles,
            meta: {
                auth: true
            }
        },
        {
            path: '/editor/dm',
            name: 'dme',
            component: DataModel,
            children: [
                {
                    path: 'et/:id',
                    name: 'dmdetail',
                    component: DataModelDetailView
                }
            ],
            meta: {
                auth: true
            }
        },
        {
            path: '/preferences',
            name: 'preferences',
            component: Preferences,
            meta: {
                auth: true
            }
        },
        {
            path: '/preferences/u/:id',
            name: 'userpreferences',
            component: UserPreferences,
            meta: {
                auth: true
            }
        },
    ]
});

Vue.router = router;
App.router = Vue.router;

Axios.interceptors.response.use(response => {
    return response;
}, error => {
    if(error.response.status == 401) {
        router.push({name: 'login'});
    }
    return Promise.reject(error);
});

Vue.use(require('@websanova/vue-auth'), {
   auth: require('@websanova/vue-auth/drivers/auth/bearer.js'),
   http: require('@websanova/vue-auth/drivers/http/axios.1.x.js'),
   router: require('@websanova/vue-auth/drivers/router/vue-router.2.x.js'),
});

// Imported Components
Vue.component('multiselect', Multiselect);
Vue.component('file-upload', VueUploadComponent);

// Extended Components
Vue.component('global-search', require('./components/GlobalSearch.vue'));
Vue.component('context-search', require('./components/ContextSearch.vue'));
Vue.component('label-search', require('./components/LabelSearch.vue'));
Vue.component('csv-table', require('./components/CsvTable.vue'));

// Reusable Components
Vue.component('attributes', require('./components/AttributeList.vue'));
Vue.component('context-tree', require('./components/ContextTree.vue'));
Vue.component('context-types', require('./components/ContextTypeList.vue'));
Vue.component('ol-map', require('./components/OlMap.vue'));

// Page Components
Vue.component('entity-reference-modal', require('./components/EntityReferenceModal.vue'));
Vue.component('discard-changes-modal', require('./components/DiscardChangesModal.vue'));
Vue.component('about-dialog', require('./components/About.vue'));
Vue.component('error-modal', require('./components/Error.vue'));

Vue.component('data-analysis', require('./components/plugins/DataAnalysis.vue'));

// Filter
Vue.filter('date', function(value, format) {
    if(!format) format = 'DD.MM.YYYY hh:mm';
    if(value) {
        return moment.unix(Number(value)).utc().format(format);
    }
});
Vue.filter('bytes', function(value, precision) {
    if(!value) return value;
    precision = precision || 2;

    let units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
    let bytes = parseFloat(value);

    let unitIndex;
    if(!isFinite(value) || isNaN(bytes)) {
        unitIndex = 0;
    } else {
        unitIndex = Math.floor(Math.log(bytes) / Math.log(1024));
    }

    let unit = units[unitIndex];
    let sizeInUnit = bytes / Math.pow(1024, unitIndex);
    return sizeInUnit.toFixed(precision) +  ' ' + unit;
});
Vue.filter('bibtexify', function(value, type) {
    let rendered = "
";
    if(type) {
        rendered += "@"+type+" {";
        for(let k in value) {
            if(value[k] == null || value[k] == '') continue;
            rendered += "
";
            rendered += "    " + k + " = \"" + value[k] + "\"";
        }
        rendered += "
";
        rendered += "}";
    }
    rendered += "
";
    return rendered;
});

const app = new Vue({
    el: '#app',
    router: router,
    render: app => app(App),
    beforeMount: function() {
        Axios.get('pre').then(response =>  {
            this.preferences = response.data.preferences;
            this.concepts = response.data.concepts;
            this.contextTypes = response.data.contextTypes;
            const extensions = this.preferences['prefs.load-extensions'];
            for(let k in extensions) {
                if(!extensions[k] || (k != 'map' && k != 'files')) {
                    console.log("Skipping plugin " + k);
                    continue;
                }
                let name = k;
                let nameExt = name + '.js';
                System.import('./plugins/' + nameExt).then(function(data) {
                    Vue.use(data.default);
                });
            }
            this.$getSpacialistPlugins('plugins');
        });
    },
    data: {
        selectedContext: {},
        onSelectContext: function(selection) {
            app.$data.selectedContext = Object.assign({}, selection);
        },
        preferences: {},
        concepts: {},
        contextTypes: {},
        plugins: {}
    }
});
<commit_msg>do proper logout<commit_after>import Multiselect from 'vue-multiselect';
import { library, dom } from '@fortawesome/fontawesome-svg-core';
import { fab } from '@fortawesome/free-brands-svg-icons';
import { far } from '@fortawesome/free-regular-svg-icons';
import { fas } from '@fortawesome/free-solid-svg-icons';
import VModal from 'vue-js-modal';
import Axios from 'axios';
import VueRouter from 'vue-router';

import App from './App.vue';
import MainView from './components/MainView.vue';
import ContextDetail from './components/ContextDetail.vue';
import Login from './components/Login.vue';
// Tools
import Bibliography from './components/BibliographyTable.vue';
import BibliographyItemModal from './components/BibliographyItemModal.vue';
// Settings
import Users from './components/Users.vue';
import Roles from './components/Roles.vue';
import DataModel from './components/DataModel.vue';
import DataModelDetailView from './components/DataModelDetailView.vue';
import Preferences from './components/Preferences.vue';
import UserPreferences from './components/UserPreferences.vue';

import VueUploadComponent from 'vue-upload-component';
import moment from 'moment';
import VCalendar from 'v-calendar';
import VeeValidate from 'vee-validate';
import Notifications from 'vue-notification';
import SpacialistPluginSystem from './plugin.js';
import VTooltip from 'v-tooltip';

library.add(fas, far, fab);
dom.watch(); // search for  tags to replace with <svg>

/**
 * First we will load all of this project's JavaScript dependencies which
 * includes Vue and other libraries. It is a great starting point when
 * building robust, powerful web applications using Vue and Laravel.
 */

require('popper.js');
require('bootstrap');
window.Vue = require('vue');
window._ = require('lodash');
$ = jQuery  = window.$ = window.jQuery = require('jquery');
require('./globals.js');

Axios.defaults.baseURL = '/api/v1';

/**
 * Next, we will create a fresh Vue application instance and attach it to
 * the page. Then, you may begin adding components to this application
 * or customize the JavaScript scaffolding to fit your unique needs.
 */
Vue.prototype.$http = Axios;
Vue.axios = Axios;
Vue.use(VueRouter);

Vue.use(SpacialistPluginSystem);

Vue.use(VModal);
Vue.use(VeeValidate);
Vue.use(Notifications);
Vue.use(VCalendar, {
    firstDayOfWeek: 2,
    popoverVisibility: 'focus'
});
Vue.use(VTooltip, {
    popover: {
        defaultBaseClass: 'popover',
        defaultInnerClass: 'popover-body',
        defaultArrowClass: 'arrow'
    }
});

const router = new VueRouter({
    scrollBehavior(to, from, savedPosition) {
        return {
            x: 0,
            y: 0
        };
    },
    routes: [
        // deprecated pre-0.6 routes
        {
            path: '/s',
            redirect: { name: 'home' },
            children: [
                {
                    path: 'context/:id',
                    redirect: to => {
                        return {
                            name: 'contextdetail',
                            params: {
                                id: to.params.id
                            }
                        }
                    },
                    children: [{
                        path: 'sources/:aid',
                        redirect: to => {
                            return {
                                // TODO not contextdetail
                                name: 'contextdetail',
                                params: {
                                    id: to.params.id,
                                    aid: to.params.aid,
                                }
                            }
                        }
                    }]
                },
                {
                    path: 'f/:id',
                    redirect: to => {
                        return {
                            name: 'file',
                            params: {
                                id: to.params.id
                            }
                        }
                    }
                },
                {
                    path: 'user',
                    redirect: { name: 'users' },
                    // TODO user edit route (redirect to users or add it)
                },
                {
                    path: 'role',
                    redirect: { name: 'roles' },
                    // TODO role edit route (redirect to roles or add it)
                },
                {
                    path: 'editor/data-model',
                    redirect: { name: 'dme' },
                    children: [{
                        path: 'contexttype/:id',
                        redirect: to => {
                            return {
                                name: 'dmdetail',
                                params: {
                                    id: to.params.id
                                }
                            }
                        }
                    }]
                },
                {
                    path: 'preferences/:id',
                    redirect: to => {
                        return {
                            name: 'userpreferences',
                            params: {
                                id: to.params.id
                            }
                        }
                    }
                }
            ]
        },
        {
            path: '/',
            name: 'home',
            component: MainView,
            children: [
                {
                    path: 'e/:id',
                    name: 'contextdetail',
                    component: ContextDetail
                }
            ],
            meta: {
                auth: true
            }
        },
        // Tools
        {
            path: '/bibliography',
            name: 'bibliography',
            component: Bibliography,
            children: [
                {
                    path: 'edit/:id',
                    name: 'bibedit',
                    component: BibliographyItemModal
                },
                {
                    path: 'new',
                    name: 'bibnew',
                    component: BibliographyItemModal
                }
            ],
            meta: {
                auth: true
            }
        },
        {
            path: '/login',
            name: 'login',
            component: Login,
            meta: {
                auth: false
            }
        },
        // Settings
        {
            path: '/mg/users',
            name: 'users',
            component: Users,
            meta: {
                auth: true
            }
        },
        {
            path: '/mg/roles',
            name: 'roles',
            component: Roles,
            meta: {
                auth: true
            }
        },
        {
            path: '/editor/dm',
            name: 'dme',
            component: DataModel,
            children: [
                {
                    path: 'et/:id',
                    name: 'dmdetail',
                    component: DataModelDetailView
                }
            ],
            meta: {
                auth: true
            }
        },
        {
            path: '/preferences',
            name: 'preferences',
            component: Preferences,
            meta: {
                auth: true
            }
        },
        {
            path: '/preferences/u/:id',
            name: 'userpreferences',
            component: UserPreferences,
            meta: {
                auth: true
            }
        },
    ]
});

Vue.router = router;
App.router = Vue.router;

Axios.interceptors.response.use(response => {
    return response;
}, error => {
    if(error.response.status == 401) {
        Vue.auth.logout({
            redirect: {
                name: 'login'
            }
        });
    }
    return Promise.reject(error);
});

Vue.use(require('@websanova/vue-auth'), {
   auth: require('@websanova/vue-auth/drivers/auth/bearer.js'),
   http: require('@websanova/vue-auth/drivers/http/axios.1.x.js'),
   router: require('@websanova/vue-auth/drivers/router/vue-router.2.x.js'),
   forbiddenRedirect: {
       name: 'login'
   },
   notFoundRedirect: {
       name: 'login'
   },
});

// Imported Components
Vue.component('multiselect', Multiselect);
Vue.component('file-upload', VueUploadComponent);

// Extended Components
Vue.component('global-search', require('./components/GlobalSearch.vue'));
Vue.component('context-search', require('./components/ContextSearch.vue'));
Vue.component('label-search', require('./components/LabelSearch.vue'));
Vue.component('csv-table', require('./components/CsvTable.vue'));

// Reusable Components
Vue.component('attributes', require('./components/AttributeList.vue'));
Vue.component('context-tree', require('./components/ContextTree.vue'));
Vue.component('context-types', require('./components/ContextTypeList.vue'));
Vue.component('ol-map', require('./components/OlMap.vue'));

// Page Components
Vue.component('entity-reference-modal', require('./components/EntityReferenceModal.vue'));
Vue.component('discard-changes-modal', require('./components/DiscardChangesModal.vue'));
Vue.component('about-dialog', require('./components/About.vue'));
Vue.component('error-modal', require('./components/Error.vue'));

Vue.component('data-analysis', require('./components/plugins/DataAnalysis.vue'));

// Filter
Vue.filter('date', function(value, format) {
    if(!format) format = 'DD.MM.YYYY hh:mm';
    if(value) {
        return moment.unix(Number(value)).utc().format(format);
    }
});
Vue.filter('bytes', function(value, precision) {
    if(!value) return value;
    precision = precision || 2;

    let units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
    let bytes = parseFloat(value);

    let unitIndex;
    if(!isFinite(value) || isNaN(bytes)) {
        unitIndex = 0;
    } else {
        unitIndex = Math.floor(Math.log(bytes) / Math.log(1024));
    }

    let unit = units[unitIndex];
    let sizeInUnit = bytes / Math.pow(1024, unitIndex);
    return sizeInUnit.toFixed(precision) +  ' ' + unit;
});
Vue.filter('bibtexify', function(value, type) {
    let rendered = "
";
    if(type) {
        rendered += "@"+type+" {";
        for(let k in value) {
            if(value[k] == null || value[k] == '') continue;
            rendered += "
";
            rendered += "    " + k + " = \"" + value[k] + "\"";
        }
        rendered += "
";
        rendered += "}";
    }
    rendered += "
";
    return rendered;
});

const app = new Vue({
    el: '#app',
    router: router,
    render: app => app(App),
    beforeMount: function() {
        Axios.get('pre').then(response =>  {
            this.preferences = response.data.preferences;
            this.concepts = response.data.concepts;
            this.contextTypes = response.data.contextTypes;
            const extensions = this.preferences['prefs.load-extensions'];
            for(let k in extensions) {
                if(!extensions[k] || (k != 'map' && k != 'files')) {
                    console.log("Skipping plugin " + k);
                    continue;
                }
                let name = k;
                let nameExt = name + '.js';
                System.import('./plugins/' + nameExt).then(function(data) {
                    Vue.use(data.default);
                });
            }
            this.$getSpacialistPlugins('plugins');
        });
    },
    data: {
        selectedContext: {},
        onSelectContext: function(selection) {
            app.$data.selectedContext = Object.assign({}, selection);
        },
        preferences: {},
        concepts: {},
        contextTypes: {},
        plugins: {}
    }
});
<|endoftext|>


<commit_before><commit_msg>manage data api route<commit_after><|endoftext|>


<commit_before>
var translations = {
  'de-freifunk': {
    'tr-list-files': 'Liste aller Dateien',
    'tr-loading': 'Loading...',
    'tr-show-table': 'Tabelle anzeigen',
    'tr-select-firmware': 'Wähle deine Firmware',
    'tr-introduction': 'Erst mit der Freifunk-Firmware wird dein Router zu einem Teil des Freifunk-Netzes. Sie ist fertig vorkonfiguriert, um Kontakt mit benachbarten Freifunk-Knoten aufzunehmen, ein offenes und anonymes Freifunk-WLAN auszusenden und dich dabei vor der Störerhaftung zu schützen.',
    'tr-introduction-additional': 'Bitte verwende das Erstinstallationsimage, wenn Du deinen Router gerade erst gekauft hast und sich darauf noch keine Freifunk-/OpenWRT-Firmware befindet. Ist bereits eine Freifunk- oder OpenWRT-Firmware installiert, verwende bitte die mit Upgrade bezeichnete Version.',
    'tr-different-versions': 'Diese Freifunk-Firmware bieten wir in verschiedenen Versionen an:',
    'tr-experimental-warning': 'Vorsicht! Unsere experimentelle Firmware wurde nicht getestet und kann dein Gerät jederzeit und unangekündigt in einen Zustand versetzen, in dem du nur mit einem Lötkolben und dem Öffnen des Gehäuses weiter kommst. Verwende diese Version nur, wenn Du Dir darüber im Klaren bist!',
    'tr-back-to-selection': 'Zurück zum Auswahlmenü',
    'tr-list-files': 'Liste aller Dateien',
    'tr-firmware': 'Firmware',
    'tr-recommendation': 'Empfehlung',
    'tr-stable-desciption': 'Gut getestet, zuverlässig und stabil.',
    'tr-beta-desciption': 'Vorabtests neuer Stable-Kandidaten.',
    'tr-experimental-desciption': 'Ungetestet, automatisch generiert.',
    'tr-available-versions': 'Für das gewählte Routermodell sind folgende Versionen vorhanden:',
    'tr-selection-consequences': 'Die Auswahl der passenden Version entscheidet über die Stablität des Routers und den potentiell anfallenden Wartungsaufwand.',
    'tr-manufacturer': 'Hersteller',
    'tr-model': 'Modell',
    'tr-first-installation': 'Erstinstallation',
    'tr-upgrade': 'Upgrade',
    'tr-generic-error': 'Da ist was schiefgelaufen. Frage doch bitte einmal im Chat nach.',
    'tr-select-manufacturer': '-- Hersteller wählen --',
    'tr-select-model': '-- Modell wählen --',
    'tr-select-revision': '-- Hardwarerevision wählen --',
    'tr-factory': 'Erstinstallation',
    'tr-sysupgrade': 'Upgrade',
    'tr-rootfs': 'Root-Image',
    'tr-kernel': 'Kernel-Image',
    'tr-download-experimental': 'Download für Experimentierfreudige'
  },
  'en': {
    'tr-list-files': 'List all files',
    'tr-loading': 'Loading...',
    'tr-show-table': 'Show table',
    'tr-select-firmware': 'Select your image',
    'tr-introduction': 'The firmware puts a Linux based operating system on your device. It allows you to install additional software.',
    'tr-introduction-additional': 'If you have just bought the device, please choose the first installation image. If you have already installed LEDE/OpenWRT, use the upgrade images.',
    'tr-different-versions': 'This firmware is available in different versions:',
    'tr-experimental-warning': 'Attention! The following images were not tested und might cause your device to put it in state that might need soldering. Please use this version only if you know what you do!',
    'tr-back-to-selection': 'Back to selection',
    'tr-list-files': 'List all files',
    'tr-firmware': 'Firmware',
    'tr-recommendation': 'Recommendation',
    'tr-stable-desciption': 'Well tested, reliable and stable.',
    'tr-beta-desciption': 'Testing candidates for the next stable release.',
    'tr-experimental-desciption': 'Untested, build automatically.',
    'tr-available-versions': 'For the selected router model, the following router versions are available:',
    'tr-selection-consequences': 'The selection of the model decides how stable the version will be and decides the potential maintenance requirements.',
    'tr-manufacturer': 'Manufacturer',
    'tr-model': 'Model',
    'tr-first-installation': 'First installation',
    'tr-upgrade': 'Upgrade',
    'tr-select-manufacturer': '-- Select manufacturer --',
    'tr-select-model': '-- Select model --',
    'tr-select-revision': '-- Select revision --',
    'tr-factory': 'First installation',
    'tr-sysupgrade': 'Upgrade',
    'tr-rootfs': 'Root-Image',
    'tr-kernel': 'Kernel-Image',
    'tr-download-experimental': 'Download for testers'
  }
}
<commit_msg>fix translations<commit_after>
var translations = {
  'de-freifunk': {
    'tr-introduction': 'Erst mit der Freifunk-Firmware wird dein Router zu einem Teil des Freifunk-Netzes. Sie ist fertig vorkonfiguriert, um Kontakt mit benachbarten Freifunk-Knoten aufzunehmen, ein offenes und anonymes Freifunk-WLAN auszusenden und dich dabei vor der Störerhaftung zu schützen.',
    'tr-introduction-additional': 'Bitte verwende das Erstinstallationsimage, wenn Du deinen Router gerade erst gekauft hast und sich darauf noch keine Freifunk-/OpenWRT-Firmware befindet. Ist bereits eine Freifunk- oder OpenWRT-Firmware installiert, verwende bitte die mit Upgrade bezeichnete Version.',
    'tr-different-versions': 'Diese Freifunk-Firmware bieten wir in verschiedenen Versionen an:',
  },
  'de': {
    'tr-list-files': 'Liste aller Dateien',
    'tr-loading': 'Loading...',
    'tr-show-table': 'Tabelle anzeigen',
    'tr-select-firmware': 'Wähle deine Firmware',
    'tr-introduction': 'Die Firmware installierd ein Linux-basiertes Betriebsystem auf den Router. Darauf can verschiedene Software installiert werden.',
    'tr-introduction-additional': 'Bitte verwende das Erstinstallationsimage, wenn Du deinen Router gerade erst gekauft hast und sich darauf noch keine LEDE-/OpenWRT-Firmware befindet. Ist bereits eine LEDE- oder OpenWRT-Firmware installiert, verwende bitte die mit Upgrade bezeichnete Version.',
    'tr-different-versions': 'Diese Firmware bieten wir in verschiedenen Versionen an:',
    'tr-experimental-warning': 'Vorsicht! Unsere experimentelle Firmware wurde nicht getestet und kann dein Gerät jederzeit und unangekündigt in einen Zustand versetzen, in dem du nur mit einem Lötkolben und dem Öffnen des Gehäuses weiter kommst. Verwende diese Version nur, wenn Du Dir darüber im Klaren bist!',
    'tr-back-to-selection': 'Zurück zum Auswahlmenü',
    'tr-list-files': 'Liste aller Dateien',
    'tr-firmware': 'Firmware',
    'tr-recommendation': 'Empfehlung',
    'tr-stable-desciption': 'Gut getestet, zuverlässig und stabil.',
    'tr-beta-desciption': 'Vorabtests neuer Stable-Kandidaten.',
    'tr-experimental-desciption': 'Ungetestet, automatisch generiert.',
    'tr-available-versions': 'Für das gewählte Routermodell sind folgende Versionen vorhanden:',
    'tr-selection-consequences': 'Die Auswahl der passenden Version entscheidet über die Stablität des Routers und den potentiell anfallenden Wartungsaufwand.',
    'tr-manufacturer': 'Hersteller',
    'tr-model': 'Modell',
    'tr-first-installation': 'Erstinstallation',
    'tr-upgrade': 'Upgrade',
    'tr-generic-error': 'Da ist was schiefgelaufen. Frage doch bitte einmal im Chat nach.',
    'tr-select-manufacturer': '-- Hersteller wählen --',
    'tr-select-model': '-- Modell wählen --',
    'tr-select-revision': '-- Hardwarerevision wählen --',
    'tr-factory': 'Erstinstallation',
    'tr-sysupgrade': 'Upgrade',
    'tr-rootfs': 'Root-Image',
    'tr-kernel': 'Kernel-Image',
    'tr-download-experimental': 'Download für Experimentierfreudige',
    'tr-all': 'alle'
  },
  'en': {
    'tr-list-files': 'List all files',
    'tr-loading': 'Loading...',
    'tr-show-table': 'Show table',
    'tr-select-firmware': 'Select your image',
    'tr-introduction': 'The firmware puts a Linux based operating system on your device. It allows you to install additional software.',
    'tr-introduction-additional': 'If you have just bought the device, please choose the first installation image. If you have already installed LEDE/OpenWRT, use the upgrade images.',
    'tr-different-versions': 'This firmware is available in different versions:',
    'tr-experimental-warning': 'Attention! The following images were not tested und might cause your device to put it in state that might need soldering. Please use this version only if you know what you do!',
    'tr-back-to-selection': 'Back to selection',
    'tr-list-files': 'List all files',
    'tr-firmware': 'Firmware',
    'tr-recommendation': 'Recommendation',
    'tr-stable-desciption': 'Well tested, reliable and stable.',
    'tr-beta-desciption': 'Testing candidates for the next stable release.',
    'tr-experimental-desciption': 'Untested, build automatically.',
    'tr-available-versions': 'For the selected router model, the following router versions are available:',
    'tr-selection-consequences': 'The selection of the model decides how stable the version will be and decides the potential maintenance requirements.',
    'tr-manufacturer': 'Manufacturer',
    'tr-model': 'Model',
    'tr-first-installation': 'First installation',
    'tr-upgrade': 'Upgrade',
    'tr-select-manufacturer': '-- Select manufacturer --',
    'tr-select-model': '-- Select model --',
    'tr-select-revision': '-- Select revision --',
    'tr-factory': 'First installation',
    'tr-sysupgrade': 'Upgrade',
    'tr-rootfs': 'Root-Image',
    'tr-kernel': 'Kernel-Image',
    'tr-download-experimental': 'Download for testers',
    'tr-all': 'all'
  }
}

// Complement translations based on other translations
translations['de-freifunk'] = Object.assign({}, translations['de-freifunk'], translations['de']);
<|endoftext|>


<commit_before>/* See license.txt for terms of usage */

"use strict";

module.metadata = {
  "stability": "experimental"
};

const { Cc, Ci, Cu } = require("chrome");
const { Trace, TraceError } = require("../../core/trace.js").get(module.id);
const { target } = require("../../target.js");
const { Http } = require("../../core/http.js");
const { Dom } = require("../../core/dom.js");
const { getInnerId } = require("sdk/window/utils");

const Events = require("sdk/system/events.js");
const tabUtils = require("sdk/tabs/utils");
const base64 = require("sdk/base64");

const { DebuggerServer } = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
const { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
const protocol = devtools["require"]("devtools/server/protocol");
const { method, RetVal, ActorClass, Actor } = protocol;

// TODO support FirePHP
const acceptableLoggerHeaders = ["X-ChromeLogger-Data"];

/**
 * A method decorator that ensures the actor is in the expected state before
 * proceeding. If the actor is not in the expected state, the decorated method
 * returns a rejected promise.
 *
 * @param String expectedState
 *        The expected state.
 *
 * @param Function method
 *        The actor method to proceed with when the actor is in the expected
 *        state.
 *
 * @returns Function
 *          The decorated method.
 */
function expectState(expectedState, method) {
  return function(...args) {
    if (this.state !== expectedState) {
      let msg = "Wrong State: Expected '" + expectedState + "', but current "
        + "state is '" + this.state + "'";
      return Promise.reject(new Error(msg));
    }

    return method.apply(this, args);
  };
}

const actorTypeName = "firebugLoggerActor";

/**
 * @actor The actor is responsible for detecting server side logs
 * within HTTP headers and sending them to the client.
 */
var LoggerActor = ActorClass(
/** @lends LoggerActor */
{
  typeName: actorTypeName,

  // Initialization

  initialize: function(conn, parent) {
    Actor.prototype.initialize.call(this, conn);

    Trace.sysout("loggerActor.initialize;", this);

    this.parent = parent;
    this.state = "detached";
    this.onExamineResponse = this.onExamineResponse.bind(this);

    Events.on("http-on-examine-response", this.onExamineResponse);
  },

  destroy: function() {
    Trace.sysout("loggerActor.destroy;", arguments);

    Events.off("http-on-examine-response", this.onExamineResponse);

    if (this.state === "attached") {
      this.detach();
    }

    Actor.prototype.destroy.call(this);
  },

  /**
   * Attach to this actor.
   */
  attach: method(expectState("detached", function() {
    Trace.sysout("loggerActor.attach;", arguments);

    this.state = "attached";
  }), {
    request: {},
    response: {
      type: "attached"
    }
  }),

  /**
   * Detach from this actor.
   */
  detach: method(expectState("attached", function() {
    Trace.sysout("loggerActor.detach;", arguments);

    this.state = "detached";
  }), {
    request: {},
    response: {
      type: "detached"
    }
  }),

  // HTTP Observer
  onExamineResponse: function(event) {
    let { subject } = event;
    let httpChannel = subject.QueryInterface(Ci.nsIHttpChannel);
    let tab = getTabFromHttpChannel(subject);

    if (!tab) {
      return;
    }

    // Filter out responses coming from different tabs.
    // xxxHonza: is there a better way how to get the current tab ID?
    let linkedBrowser = this.parent._tabbrowser.mCurrentTab.linkedBrowser;
    if (tab.linkedBrowser != linkedBrowser) {
      FBTrace.sysout("loggerActor.onExamineResponse; different tab " +
        tab.linkedBrowser + ", " + linkedBrowser);
      return;
    }

    let parsedMessages = [];

    httpChannel.visitResponseHeaders((header, value) => {
      if (acceptableLoggerHeaders.indexOf(header) !== -1) {
        let parsedMessage = this.parse(header, value);
        parsedMessages.push(parsedMessage);
      }
    });

    if (!parsedMessages.length) {
      return;
    }

    // better variable names for parsedMessages, parsedMessage and msg
    // (probably a little piece of refactoring)
    for (let parsedMessage of parsedMessages) {
      for (let msg of parsedMessage) {
        this.sendMessage(msg, tab);
      }
    }
  },

  // TODO parse should invoke the parse method of a dedicated module.
  // Example:
  parse: function(header, value) {
    let data = JSON.parse(base64.decode(value));
    let parsedMessage = [];
    let columnMap = this.getColumnMap(data);

    for (let row of data.rows) {
      let backtrace = row[columnMap.get("backtrace")];
      let label = row[columnMap.get("label")];
      let rawLogs = row[columnMap.get("log")];
      let type = row[columnMap.get("type")] || "log";

      // new version without label
      let newVersion = false;
      if (data.columns.indexOf("label") === -1) {
        newVersion = true;
      }

      // if this is the old version do some converting
      if (!newVersion) {
        let showLabel = label && typeof label === "string";

        rawLogs = [rawLogs];

        if (showLabel) {
          rawLogs.unshift(label);
        }
      }

      // xxxHonza: can we simplify the url and line info extraction?
      let result = backtrace.match(/\s*(\d+)\:(\d+)$/);
      let location;
      if (result.length == 3) {
        location = {
          url: backtrace.slice(0, -result[0].length),
          line: result[1]
        };
      }

      parsedMessage.push({
        logs: rawLogs,
        location: location,
        type: type
      });
    }

    return parsedMessage;
  },

  getColumnMap: function(data) {
    let columnMap = new Map();
    let columnName;

    for (let key in data.columns) {
      columnName = data.columns[key];
      columnMap.set(columnName, key);
    }

    return columnMap;
  },

  sendMessage: function(msg, tab) {
    Trace.sysout("!!! loggerActor.sendMessage; raw: " + msg.logs.join(", "), msg);

    let formatted = format(msg);
    Trace.sysout("loggerActor.sendMessage; formatted:", formatted);

    // xxxHonza: do we need this chrome logger prop?
    for (let log of formatted.logs) {
      if (typeof log == "object") {
        delete log.___class_name;
      }
    }

    let packet = {
      from: this.actorID,
      type: msg.type,
      message: {
        category: "server",
        innerID: getInnerId(this.parent.window),
        level: msg.type,
        filename: msg.location.url,
        lineNumber: msg.location.line,
        columnNumber: 0,
        private: false,
        timeStamp: Date.now(),
        arguments: formatted.logs,
        styles: formatted.styles,
      }
    };

    // Send raw data to the client {@LoggerFront}
    this.conn.send(packet);

    // Log into the console using standard API now.
    // xxxHonza: custom logging should be implemented
    // by {@LoggerFront} object.
    let consoleEvent = clone(packet.message);
    consoleEvent.wrappedJSObject = consoleEvent;

    Services.obs.notifyObservers(consoleEvent,
      "console-api-log-event", null);
  },
});

// Helpers

function clone(obj = {}) {
  return JSON.parse(JSON.stringify(obj));
}

/**
 * Parse printf-like specifiers ("%f", "%d", ...) and
 * format the logs according to them.
 */
function format(msg) {
  if (!msg.logs || !msg.logs[0])
    return;

  msg.styles = [];

  let firstLog = msg.logs.shift();
  let specifierIndex = -1;
  let specifiers = [];
  let splitLog = [];
  let splitLogRegExp = /(.*?)(%[oOcsdif]|$)/g;
  let splitLogRegExpRes;
  while (splitLogRegExpRes = splitLogRegExp.exec(firstLog)) {
    let [_, log, specifier] = splitLogRegExpRes;
    // We can add an empty string if there is a specifier after (which
    // means we haven't reached the end of the string)
    if (log || specifier)
      splitLog.push(log);
    // Break now if there is no specifier anymore
    // (means that we have reached the end of the string)
    if (!specifier)
      break;
    specifiers.push(specifier);
  }

  let rebuildLogArray = [""];
  let concatWithLastLog = (string) => {
    let lastStringIndex = rebuildLogArray.length - 1;
    return rebuildLogArray[lastStringIndex] += string;
  };
  splitLog.forEach((string, index) => {
    concatWithLastLog(string);
    if (specifiers.length === 0)
      return;
    let argument = msg.logs.shift();
    switch (specifiers[index]) {
      case "%i":
      case "%d":
        // Parse into integer.
        argument |= 0;
        concatWithLastLog(argument);
        break;
      case "%f":
        // Parse into float.
        argument = +argument;
        concatWithLastLog(argument);
        break;
      case "%o":
      case "%O":
        // Add the object and initialize a new String to concatene.
        rebuildLogArray.push(argument);
        if (index < splitLog.length - 1)
          rebuildLogArray.push("");
        break;
      case "%s":
        argument += "";
        concatWithLastLog(argument);
        break;
      case "%c":
        for (let j = msg.styles.length; j < rebuildLogArray.length - 1; j++)
          msg.styles.push(null);
        msg.styles.push(argument);
        // Go to next iteration directly.
        return;
      default:
        // Should never happen.
        return;
    }
  });

  msg.logs = rebuildLogArray.concat(msg.logs);

  return msg;
}

function getTabFromHttpChannel(httpChannel) {
  let topFrame = Http.getTopFrameElementForRequest(httpChannel);
  if (!topFrame) {
    return;
  }

  // In case of in-process debugging (no e10s) the result topFrame
  // represents the content window.
  let winType = topFrame.Window;
  if (typeof winType != "undefined" && topFrame instanceof winType) {
    return tabUtils.getTabForContentWindow(topFrame);
  }

  // ... otherwise the topFrame represents the content window parent frame.
  let notificationBox = Dom.getAncestorByTagName(topFrame, "notificationbox");
  if (!notificationBox) {
    Trace.sysout("loggerActor.getTabFromHttpChannel; " +
      "notificationbox not found");
    return;
  }

  return notificationBox.ownerDocument.querySelector(
    "#tabbrowser-tabs [linkedpanel='" + notificationBox.id + "']");
}

// Note: Future work for special rendering.
/*DebuggerServer.ObjectActorPreviewers.Object.unshift(
  function ChromeLoggerRemoteObject({obj, threadActor}, aGrip) {
    if (!obj.__class_name)
      return false;
    aGrip.preview = {
      kind: "ChromeLoggerRemoteObject",
      text: threadActor.createValueGrip(`[Remote ${obj.___class_name}]`)
    };
    return true;
  }
); */

// Registration

// xxxHonza: remote device debugging requires dynamic actor installation.
// See also:
// * https://bugzilla.mozilla.org/show_bug.cgi?id=977443
// * https://bugzilla.mozilla.org/show_bug.cgi?id=980481
target.on("initialize", Firebug => {
  DebuggerServer.registerModule(module.uri);
});

exports.register = function(handle) {
  handle.addTabActor(LoggerActor, actorTypeName);
};

exports.unregister = function(handle) {
  handle.removeTabActor(LoggerActor, actorTypeName);
};

// Exports from this module
exports.LoggerActor = LoggerActor;
<commit_msg>Issue #23: Fix issues with color formatting + comments (more to come)<commit_after>/* See license.txt for terms of usage */

"use strict";

module.metadata = {
  "stability": "experimental"
};

const { Cc, Ci, Cu } = require("chrome");
const { Trace, TraceError } = require("../../core/trace.js").get(module.id);
const { target } = require("../../target.js");
const { Http } = require("../../core/http.js");
const { Dom } = require("../../core/dom.js");
const { getInnerId } = require("sdk/window/utils");

const Events = require("sdk/system/events.js");
const tabUtils = require("sdk/tabs/utils");
const base64 = require("sdk/base64");

const { DebuggerServer } = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
const { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
const protocol = devtools["require"]("devtools/server/protocol");
const { method, RetVal, ActorClass, Actor } = protocol;

// TODO support FirePHP
const acceptableLoggerHeaders = ["X-ChromeLogger-Data"];

/**
 * A method decorator that ensures the actor is in the expected state before
 * proceeding. If the actor is not in the expected state, the decorated method
 * returns a rejected promise.
 *
 * @param String expectedState
 *        The expected state.
 *
 * @param Function method
 *        The actor method to proceed with when the actor is in the expected
 *        state.
 *
 * @returns Function
 *          The decorated method.
 */
function expectState(expectedState, method) {
  return function(...args) {
    if (this.state !== expectedState) {
      let msg = "Wrong State: Expected '" + expectedState + "', but current "
        + "state is '" + this.state + "'";
      return Promise.reject(new Error(msg));
    }

    return method.apply(this, args);
  };
}

const actorTypeName = "firebugLoggerActor";

/**
 * @actor The actor is responsible for detecting server side logs
 * within HTTP headers and sending them to the client.
 */
var LoggerActor = ActorClass(
/** @lends LoggerActor */
{
  typeName: actorTypeName,

  // Initialization

  initialize: function(conn, parent) {
    Actor.prototype.initialize.call(this, conn);

    Trace.sysout("loggerActor.initialize;", this);

    this.parent = parent;
    this.state = "detached";
    this.onExamineResponse = this.onExamineResponse.bind(this);

    Events.on("http-on-examine-response", this.onExamineResponse);
  },

  destroy: function() {
    Trace.sysout("loggerActor.destroy;", arguments);

    Events.off("http-on-examine-response", this.onExamineResponse);

    if (this.state === "attached") {
      this.detach();
    }

    Actor.prototype.destroy.call(this);
  },

  /**
   * Attach to this actor.
   */
  attach: method(expectState("detached", function() {
    Trace.sysout("loggerActor.attach;", arguments);

    this.state = "attached";
  }), {
    request: {},
    response: {
      type: "attached"
    }
  }),

  /**
   * Detach from this actor.
   */
  detach: method(expectState("attached", function() {
    Trace.sysout("loggerActor.detach;", arguments);

    this.state = "detached";
  }), {
    request: {},
    response: {
      type: "detached"
    }
  }),

  // HTTP Observer
  onExamineResponse: function(event) {
    let { subject } = event;
    let httpChannel = subject.QueryInterface(Ci.nsIHttpChannel);
    let tab = getTabFromHttpChannel(subject);

    if (!tab) {
      return;
    }

    // Filter out responses coming from different tabs.
    // xxxHonza: is there a better way how to get the current tab ID?
    let linkedBrowser = this.parent._tabbrowser.mCurrentTab.linkedBrowser;
    if (tab.linkedBrowser != linkedBrowser) {
      FBTrace.sysout("loggerActor.onExamineResponse; different tab " +
        tab.linkedBrowser + ", " + linkedBrowser);
      return;
    }

    let parsedMessages = [];

    httpChannel.visitResponseHeaders((header, value) => {
      if (acceptableLoggerHeaders.indexOf(header) !== -1) {
        let parsedMessage = this.parse(header, value);
        parsedMessages.push(parsedMessage);
      }
    });

    if (!parsedMessages.length) {
      return;
    }

    // better variable names for parsedMessages, parsedMessage and msg
    // (probably a little piece of refactoring)
    for (let parsedMessage of parsedMessages) {
      for (let msg of parsedMessage) {
        this.sendMessage(msg, tab);
      }
    }
  },

  // TODO parse should invoke the parse method of a dedicated module.
  // Example:
  parse: function(header, value) {
    let data = JSON.parse(base64.decode(value));
    let parsedMessage = [];
    let columnMap = this.getColumnMap(data);

    for (let row of data.rows) {
      let backtrace = row[columnMap.get("backtrace")];
      let label = row[columnMap.get("label")];
      let rawLogs = row[columnMap.get("log")];
      let type = row[columnMap.get("type")] || "log";

      // new version without label
      let newVersion = false;
      if (data.columns.indexOf("label") === -1) {
        newVersion = true;
      }

      // if this is the old version do some converting
      if (!newVersion) {
        let showLabel = label && typeof label === "string";

        rawLogs = [rawLogs];

        if (showLabel) {
          rawLogs.unshift(label);
        }
      }

      // xxxHonza: can we simplify the url and line info extraction?
      let result = backtrace.match(/\s*(\d+)\:(\d+)$/);
      let location;
      if (result.length == 3) {
        location = {
          url: backtrace.slice(0, -result[0].length),
          line: result[1]
        };
      }

      parsedMessage.push({
        logs: rawLogs,
        location: location,
        type: type
      });
    }

    return parsedMessage;
  },

  getColumnMap: function(data) {
    let columnMap = new Map();
    let columnName;

    for (let key in data.columns) {
      columnName = data.columns[key];
      columnMap.set(columnName, key);
    }

    return columnMap;
  },

  sendMessage: function(msg, tab) {
    Trace.sysout("!!! loggerActor.sendMessage; raw: " + msg.logs.join(", "), msg);

    let formatted = format(msg);
    Trace.sysout("loggerActor.sendMessage; formatted:", formatted);

    // xxxHonza: do we need this chrome logger prop?
    for (let log of formatted.logs) {
      if (typeof log == "object") {
        delete log.___class_name;
      }
    }

    let packet = {
      from: this.actorID,
      type: msg.type,
      message: {
        category: "server",
        innerID: getInnerId(this.parent.window),
        level: msg.type,
        filename: msg.location.url,
        lineNumber: msg.location.line,
        columnNumber: 0,
        private: false,
        timeStamp: Date.now(),
        arguments: formatted.logs,
        styles: formatted.styles,
      }
    };

    // Send raw data to the client {@LoggerFront}
    this.conn.send(packet);

    // Log into the console using standard API now.
    // xxxHonza: custom logging should be implemented
    // by {@LoggerFront} object.
    let consoleEvent = clone(packet.message);
    consoleEvent.wrappedJSObject = consoleEvent;

    Services.obs.notifyObservers(consoleEvent,
      "console-api-log-event", null);
  },
});

// Helpers

function clone(obj = {}) {
  return JSON.parse(JSON.stringify(obj));
}

/**
 * Parse printf-like specifiers ("%f", "%d", ...) and
 * format the logs according to them.
 */
function format(msg) {
  if (!msg.logs || !msg.logs[0])
    return;

  // Initialize the styles array (used for the "%c" specifier).
  msg.styles = [];

  // Remove and get the first log (in which the specifiers are).
  let firstString = msg.logs.shift();
  // Contains all the strings split by the specifiers
  // (i.e. "a %f b" => ["a ", " b"]).
  let splitLog = [];
  // All the specifiers present in the first string.
  let specifiers = [];
  let specifierIndex = -1;
  let splitLogRegExp = /(.*?)(%[oOcsdif]|$)/g;
  let splitLogRegExpRes;
  // Get the strings before the specifiers (or the last chunk before the end
  // of the string).
  while (splitLogRegExpRes = splitLogRegExp.exec(firstString)) {
    let [_, log, specifier] = splitLogRegExpRes;
    // We can add an empty string if there is a specifier after (which
    // means we haven't reached the end of the string). This empty string is
    // necessary when rebuilding the logs after the formatting (we should ensure
    // to alternate a log + a specifier to replace to make this loop work).
    //
    // Example: "%ctest" => first iteration: log = "", specifier = "%c".
    //                   => second iteration: log = "test", specifier = "".
    if (log || specifier)
      splitLog.push(log);
    // Break now if there is no specifier anymore
    // (means that we have reached the end of the string).
    if (!specifier)
      break;
    specifiers.push(specifier);
  }

  // This array represents the string of the log, in which the specifiers
  // are replaced. It alternates strings and objects (%o;%O).
  let rebuiltLogArray = [];
  let concatString = "";
  let pushConcatString = () => {
    if (concatString)
      rebuiltLogArray.push(concatString);
    concatString = "";
  };
  // Merge the split first string and the values associated to the specifiers.
  splitLog.forEach((string, index) => {
    // Concatene the string in any case.
    concatString += string;
    if (specifiers.length === 0)
      return;
    let argument = msg.logs.shift();
    switch (specifiers[index]) {
      case "%i":
      case "%d":
        // Parse into integer.
        argument |= 0;
        concatString += argument;
        break;
      case "%f":
        // Parse into float.
        argument = +argument;
        concatString += argument;
        break;
      case "%o":
      case "%O":
        // Push the concatened string and reintialize concatString.
        pushConcatString();
        // Push the object.
        rebuiltLogArray.push(argument);
        break;
      case "%s":
        concatString += argument;
        break;
      case "%c":
        pushConcatString();
        for (let j = msg.styles.length; j < rebuiltLogArray.length; j++)
          msg.styles.push(null);
        msg.styles.push(argument);
        // Go to next iteration directly.
        return;
      default:
        // Should never happen.
        return;
    }
  });

  if (concatString)
    rebuiltLogArray.push(concatString);

  // Prepend the items of the rebuilt log array of the first string
  // to the message logs.
  msg.logs = rebuiltLogArray.concat(msg.logs);

  return msg;
}

function getTabFromHttpChannel(httpChannel) {
  let topFrame = Http.getTopFrameElementForRequest(httpChannel);
  if (!topFrame) {
    return;
  }

  // In case of in-process debugging (no e10s) the result topFrame
  // represents the content window.
  let winType = topFrame.Window;
  if (typeof winType != "undefined" && topFrame instanceof winType) {
    return tabUtils.getTabForContentWindow(topFrame);
  }

  // ... otherwise the topFrame represents the content window parent frame.
  let notificationBox = Dom.getAncestorByTagName(topFrame, "notificationbox");
  if (!notificationBox) {
    Trace.sysout("loggerActor.getTabFromHttpChannel; " +
      "notificationbox not found");
    return;
  }

  return notificationBox.ownerDocument.querySelector(
    "#tabbrowser-tabs [linkedpanel='" + notificationBox.id + "']");
}

// Note: Future work for special rendering.
/*DebuggerServer.ObjectActorPreviewers.Object.unshift(
  function ChromeLoggerRemoteObject({obj, threadActor}, aGrip) {
    if (!obj.__class_name)
      return false;
    aGrip.preview = {
      kind: "ChromeLoggerRemoteObject",
      text: threadActor.createValueGrip(`[Remote ${obj.___class_name}]`)
    };
    return true;
  }
); */

// Registration

// xxxHonza: remote device debugging requires dynamic actor installation.
// See also:
// * https://bugzilla.mozilla.org/show_bug.cgi?id=977443
// * https://bugzilla.mozilla.org/show_bug.cgi?id=980481
target.on("initialize", Firebug => {
  DebuggerServer.registerModule(module.uri);
});

exports.register = function(handle) {
  handle.addTabActor(LoggerActor, actorTypeName);
};

exports.unregister = function(handle) {
  handle.removeTabActor(LoggerActor, actorTypeName);
};

// Exports from this module
exports.LoggerActor = LoggerActor;
<|endoftext|>


<commit_before>function initializeDetector(name, headline){
    var headWrapper = document.createElement('div')
    ,   title = document.createElement('h1')
    ,   drawTarget = document.createElement('div')
    ,   plotControlWrap = document.createElement('form')
    ,   plotControlTitle = document.createElement('h3')
    ,   plotControlMinLabel = document.createElement('label')
    ,   plotControlMaxLabel = document.createElement('label')
    ,   plotControlMin = document.createElement('input')
    ,   plotControlMax = document.createElement('input')
    ,   plotScale = document.createElement('select')
    ,   plotScaleLin = document.createElement('option')
    ,   plotScaleLog = document.createElement('option')
    ,   deckWrap = document.createElement('div')
    ,   plotDeck
    ,   plotCard
    ,   xString
    ,   deckNavigator, deckOption
    //image has aspect ratio 3:2 and tries to be 80% of the window width, but not more than 80% of the window height
    ,   width = this.offsetWidth
    ,   height = 2*width/3
    ,   i, subdetectorNav, subdetectorNavLabel

    this.name = name;
    //declare default views and units if none pre-defined
    if(!this.views)
        this.views = ['HV', 'Threshold', 'Rate'];
    if(!this.units)
        this.units = ['V', 'ADC Units', 'Hz'];

    //set up data store for detectors
    if(!window.currentData)
        window.currentData = {};
    for(i=0; i<this.views.length; i++){
        window.currentData[this.views[i]] = {};
    }

    //////////////////////
    //Build DOM
    //////////////////////
    //the DOM layout for a detector view is roughly:

    //-----------------------------------
    // h1 Title
    //-----------------------------------
    // input[radio] HV / Thresh / Rate
    //-----------------------------------
    // x-deck for detector views; each
    // card contains a Kinetic drawing
    // context
    //
    //-----------------------------------
    // x-deck navigation if required
    //-----------------------------------
    // form plot control widget
    //-----------------------------------

    headWrapper.setAttribute('id', this.id+'titleWrapper');
    headWrapper.setAttribute('class', 'subdetectorHeadlineWrap')
    this.appendChild(headWrapper);
    //top nav title
    title.setAttribute('id', this.id+'title');
    title.setAttribute('class', 'subdetectorTitle');
    document.getElementById(this.id+'titleWrapper').appendChild(title);
    document.getElementById(this.id+'title').innerHTML = headline;
    //state nav radio
    for(i=0; i<this.views.length; i++){
        subdetectorNav = document.createElement('input')
        subdetectorNav.setAttribute('id', this.id+'goto'+this.views[i]);
        subdetectorNav.setAttribute('class', 'subdetectorNavRadio');
        subdetectorNav.setAttribute('type', 'radio');
        subdetectorNav.setAttribute('name', this.id+'Nav');
        subdetectorNav.setAttribute('value', this.views[i]);
        subdetectorNav.onchange = this.trackView.bind(this);
        if(i==2) subdetectorNav.setAttribute('checked', true); //default to rate view
        document.getElementById(this.id+'titleWrapper').appendChild(subdetectorNav);
        subdetectorNavLabel = document.createElement('label');
        subdetectorNavLabel.setAttribute('id', this.id+'goto'+this.views[i]+'Label');
        subdetectorNavLabel.setAttribute('class', 'subdetectorNavLabel');
        subdetectorNavLabel.setAttribute('for', this.id+'goto'+this.views[i]);
        document.getElementById(this.id+'titleWrapper').appendChild(subdetectorNavLabel);
        document.getElementById(this.id+'goto'+this.views[i]+'Label').innerHTML = this.views[i];
    }
    //plot deck wrapper:
    deckWrap.setAttribute('id', this.id+'DeckWrap');
    this.appendChild(deckWrap);

    //declaring x-tags from within other x-tags needs special treatment via innerHTML; must build HTML string and set it.
    xString = '<x-deck id="' + this.id + 'Deck" selected-index=0 style="height:'+height+'">';
    for(i=0; i<this.viewNames.length; i++){
        xString += '<x-card id="' + this.id+this.viewNames[i] + 'Card"></x-card>';
    }
    xString += '</x-deck>'
    deckWrap.innerHTML = xString;

    //plot buffers
    for(i=0; i<this.viewNames.length; i++){
        //divs to hold kinetic contexts
        drawTarget = document.createElement('div');
        drawTarget.setAttribute('id', this.id+this.viewNames[i]+'Draw');
        document.getElementById(this.id+this.viewNames[i] + 'Card').appendChild(drawTarget);
    }

    //plot control widget
    plotControlWrap.setAttribute('id', this.id+'PlotControl');
    plotControlWrap.setAttribute('class', 'plotControlWidget');
    this.appendChild(plotControlWrap);
    document.getElementById(this.id+'PlotControl').onchange = this.updatePlotParameters.bind(this);

    plotControlTitle.setAttribute('id', this.id+'PlotControlTitle');
    plotControlWrap.appendChild(plotControlTitle);
    document.getElementById(this.id + 'PlotControlTitle').innerHTML = 'Plot Control'

    //x-deck navigation
    if(this.viewNames.length > 1){
        deckNavigator = document.createElement('select');
        deckNavigator.id = this.id + 'viewSelect';
        for(i=0; i<this.viewNames.length; i++){
            deckOption = document.createElement('option');
            deckOption.innerHTML = this.viewNames[i];
            deckOption.value = i;
            deckNavigator.appendChild(deckOption);
        }
        deckNavigator.onchange = function(){
            var viewVal = selected(this.id+'viewSelect');

            document.getElementById(this.id+'Deck').shuffleTo(viewVal);
            this.displayIndex = viewVal;
            this.update();  //repaint right away
        }.bind(this)
        plotControlWrap.appendChild(deckNavigator);
    }

    plotControlMinLabel.setAttribute('id', this.id+'PlotControlMinLabel');
    plotControlWrap.appendChild(plotControlMinLabel)
    document.getElementById(this.id+'PlotControlMinLabel').innerHTML = 'Min: ';
    plotControlMin.setAttribute('id', this.id + 'PlotControlMin');
    plotControlMin.setAttribute('type', 'number');
    plotControlMin.setAttribute('step', 'any');
    plotControlWrap.appendChild(plotControlMin);

    plotControlMaxLabel.setAttribute('id', this.id+'PlotControlMaxLabel');
    plotControlWrap.appendChild(plotControlMaxLabel)
    document.getElementById(this.id+'PlotControlMaxLabel').innerHTML = 'Max: ';
    plotControlMax.setAttribute('id', this.id + 'PlotControlMax');
    plotControlMax.setAttribute('type', 'number');
    plotControlMax.setAttribute('step', 'any');
    plotControlWrap.appendChild(plotControlMax);

    plotScale.setAttribute('id', this.id+'PlotControlScale');
    plotControlWrap.appendChild(plotScale);

    plotScaleLin.setAttribute('id', this.id+'PlotScaleLin');
    plotScaleLin.setAttribute('value', 'lin');
    plotScale.appendChild(plotScaleLin);
    document.getElementById(this.id+'PlotScaleLin').innerHTML = 'Linear';

    plotScaleLog.setAttribute('id', this.id+'PlotScaleLog');
    plotScaleLog.setAttribute('value', 'log');
    plotScale.appendChild(plotScaleLog);
    document.getElementById(this.id+'PlotScaleLog').innerHTML = 'Log';

    ///////////////////////
    //State variables
    ///////////////////////
    this.currentView = 'Rate';
    this.currentUnit = 'Hz';
    this.displayIndex = 0;  //always start on the first card, guarnateed to exist.
    this.HVcrates = 0;
    detectHVcrates(this.MIDAS, this);
    //cells
    this.cells = {};
    this.summaryDepth = 0;

    ////////////////////////////
    //Drawing parameters
    ////////////////////////////
    this.frameLineWidth = 2;
    this.frameColor = '#000000';//'#999999';
    this.width = width;
    this.height = height;

    ///////////////////////////
    //Scale Parameters
    ///////////////////////////
    this.scale = 'ROOT Rainbow';
    this.min = {};
    this.max = {};
    this.scaleType = {};
    for(i=0; i<this.views.length; i++){
        this.min[this.views[i]] = canHas(localStorage.getItem(name+this.views[i]+'min'), 0);
        this.max[this.views[i]] = canHas(localStorage.getItem(name+this.views[i]+'max'), 3000);
        this.scaleType[this.views[i]] = canHas(localStorage.getItem(name+this.views[i]+'scaleType'), 'lin');
    }

    //if anything was in local storage, communicate this to the UI:
    plotControlMin.value = this.min[this.currentView];
    plotControlMax.value = this.max[this.currentView];
    plotScale.value = this.scaleType[this.currentView];

    ///////////////////////////
    //Tooltip state
    ///////////////////////////
    this.lastTTindex = -1;

    ////////////////////////////
    //Kinetic.js setup
    ////////////////////////////

    //indices for these arrays correspond to the x-card index on display
    this.stage = [];
    this.mainLayer = [];
    this.scaleLayer = [];
    this.tooltipLayer = [];
    this.TTbkg = [];
    this.text = [];

    for(i=0; i<this.viewNames.length; i++){

        //point kinetic at the div and set up the staging and layers:
        this.stage[i] = new Kinetic.Stage({
            container: this.id+this.viewNames[i]+'Draw',
            width: width,
            height: height
        });
        this.mainLayer[i] = new Kinetic.Layer();       //main rendering layer
        this.scaleLayer[i] = new Kinetic.Layer();      //layer for scales / legends
        this.tooltipLayer[i] = new Kinetic.Layer();    //layer for tooltip info

        //tooltip background:
        this.TTbkg[i] = new Kinetic.Rect({
            x:-1000,
            y:-1000,
            width:100,
            height:100,
            fill:'rgba(0,0,0,0.8)',
            stroke: 'rgba(0,0,0,0)',
            listening: false
        });
        this.tooltipLayer[i].add(this.TTbkg[i]);

        //tooltip text:
        this.text[i] = new Kinetic.Text({
            x: -1000,
            y: -1000,
            fontFamily: 'Arial',
            fontSize: 16,
            text: '',
            lineHeight: 1.2,
            fill: '#EEEEEE',
            listening: false
        });
        this.tooltipLayer[i].add(this.text[i]);
    }

    this.errorPattern = new Image();
    this.errorPattern.onload = function(){
        var key;

        for(key in this.cells){
            this.cells[key].setAttr('fillPatternImage', this.errorPattern);
        }

        this.mainLayer[this.displayIndex].draw();
    }.bind(this)
    this.errorPattern.src = 'static/img/static.gif'

    //let repopulate know that the detector would like to be updated every loop:
    if(!window.refreshTargets)
        window.refreshTargets = [];
    window.refreshTargets[window.refreshTargets.length] = this;

}

//stick the ODB equipment directory into its local slot:
function fetchODBEquipment(returnObj){
    if(!window.currentData.ODB)
        window.currentData.ODB = {};
    window.currentData.ODB.Equipment = returnObj;
}

//callback for fetching from the scalar service:
function parseRate(data){
    var key, subkey;

    if(!window.currentData.Rate)
        window.currentData.Rate = {};

    for(key in data){
        if (data.hasOwnProperty(key)) {
            for(subkey in data[key]){
                if(data[key].hasOwnProperty(subkey)){
                    window.currentData.Rate[subkey.toUpperCase().slice(0,10)] = data[key][subkey].TRIGREQ;
                }
            }
        }
    }
}

//similar function for the threshold service:
function parseThreshold(data){
    var key;
    if(!window.currentData.Threshold)
        window.currentData.Threshold = {};

    if(data['parameters']['thresholds']){
        for(key in data['parameters']['thresholds']){
            window.currentData.Threshold[key.toUpperCase().slice(0,10)] = data['parameters']['thresholds'][key];
        }
    }
}

//function to make a reasonable decision on how many decimal places to show, whether to to use
//sci. notation on an axis tick mark, where <min> and <max> are the axis minimum and maximum,
//<nTicks> is the number of tickmarks on the axis, and we are returning the label for the <n>th
//tick mark
function generateTickLabel(min, max, nTicks, n){
    var range = max - min,
        smallestPrecision = range / (nTicks-1),
        tickValue = min + (max-min)/(nTicks-1)*n;

    //tickmark needs to be labeled to enough precision to show the difference between subsequent ticks:
    smallestPrecision = Math.floor(Math.log(smallestPrecision) / Math.log(10));

    if(smallestPrecision < 0)
        return tickValue.toFixed(-smallestPrecision)

    tickValue = Math.floor(tickValue/Math.pow(10, smallestPrecision)) * Math.pow(10, smallestPrecision);
    return tickValue+'';

}

//determine how many HV frontends are in the ODB.  Frontends must be named HV-0, HV-1, HV-2....
function detectHVcrates(MIDAS, obj){
    var xmlhttp = new XMLHttpRequest();

    //once this is all dealt with, refresh the display immediately
    xmlhttp.onreadystatechange = function(target){
        var data, nCrates = 0;

        if (this.readyState != 4) return;

        data = JSON.parse(this.responseText.slice(this.responseText.indexOf('{'), this.responseText.lastIndexOf('}')+1 ) )

        while(data['HV-'+nCrates])
            nCrates++

        target.HVcrates = nCrates;
    }.bind(xmlhttp, obj)
    //fire async
    xmlhttp.open('GET', MIDAS + 'cmd=jcopy&odb=/Equipment&encoding=json-nokeys');
    xmlhttp.send();
}<commit_msg>routing HV to detectors<commit_after>function initializeDetector(name, headline){
    var headWrapper = document.createElement('div')
    ,   title = document.createElement('h1')
    ,   drawTarget = document.createElement('div')
    ,   plotControlWrap = document.createElement('form')
    ,   plotControlTitle = document.createElement('h3')
    ,   plotControlMinLabel = document.createElement('label')
    ,   plotControlMaxLabel = document.createElement('label')
    ,   plotControlMin = document.createElement('input')
    ,   plotControlMax = document.createElement('input')
    ,   plotScale = document.createElement('select')
    ,   plotScaleLin = document.createElement('option')
    ,   plotScaleLog = document.createElement('option')
    ,   deckWrap = document.createElement('div')
    ,   plotDeck
    ,   plotCard
    ,   xString
    ,   deckNavigator, deckOption
    //image has aspect ratio 3:2 and tries to be 80% of the window width, but not more than 80% of the window height
    ,   width = this.offsetWidth
    ,   height = 2*width/3
    ,   i, subdetectorNav, subdetectorNavLabel

    this.name = name;
    //declare default views and units if none pre-defined
    if(!this.views)
        this.views = ['HV', 'Threshold', 'Rate'];
    if(!this.units)
        this.units = ['V', 'ADC Units', 'Hz'];

    //set up data store for detectors
    if(!window.currentData)
        window.currentData = {};
    for(i=0; i<this.views.length; i++){
        window.currentData[this.views[i]] = {};
    }

    //////////////////////
    //Build DOM
    //////////////////////
    //the DOM layout for a detector view is roughly:

    //-----------------------------------
    // h1 Title
    //-----------------------------------
    // input[radio] HV / Thresh / Rate
    //-----------------------------------
    // x-deck for detector views; each
    // card contains a Kinetic drawing
    // context
    //
    //-----------------------------------
    // x-deck navigation if required
    //-----------------------------------
    // form plot control widget
    //-----------------------------------

    headWrapper.setAttribute('id', this.id+'titleWrapper');
    headWrapper.setAttribute('class', 'subdetectorHeadlineWrap')
    this.appendChild(headWrapper);
    //top nav title
    title.setAttribute('id', this.id+'title');
    title.setAttribute('class', 'subdetectorTitle');
    document.getElementById(this.id+'titleWrapper').appendChild(title);
    document.getElementById(this.id+'title').innerHTML = headline;
    //state nav radio
    for(i=0; i<this.views.length; i++){
        subdetectorNav = document.createElement('input')
        subdetectorNav.setAttribute('id', this.id+'goto'+this.views[i]);
        subdetectorNav.setAttribute('class', 'subdetectorNavRadio');
        subdetectorNav.setAttribute('type', 'radio');
        subdetectorNav.setAttribute('name', this.id+'Nav');
        subdetectorNav.setAttribute('value', this.views[i]);
        subdetectorNav.onchange = this.trackView.bind(this);
        if(i==2) subdetectorNav.setAttribute('checked', true); //default to rate view
        document.getElementById(this.id+'titleWrapper').appendChild(subdetectorNav);
        subdetectorNavLabel = document.createElement('label');
        subdetectorNavLabel.setAttribute('id', this.id+'goto'+this.views[i]+'Label');
        subdetectorNavLabel.setAttribute('class', 'subdetectorNavLabel');
        subdetectorNavLabel.setAttribute('for', this.id+'goto'+this.views[i]);
        document.getElementById(this.id+'titleWrapper').appendChild(subdetectorNavLabel);
        document.getElementById(this.id+'goto'+this.views[i]+'Label').innerHTML = this.views[i];
    }
    //plot deck wrapper:
    deckWrap.setAttribute('id', this.id+'DeckWrap');
    this.appendChild(deckWrap);

    //declaring x-tags from within other x-tags needs special treatment via innerHTML; must build HTML string and set it.
    xString = '<x-deck id="' + this.id + 'Deck" selected-index=0 style="height:'+height+'">';
    for(i=0; i<this.viewNames.length; i++){
        xString += '<x-card id="' + this.id+this.viewNames[i] + 'Card"></x-card>';
    }
    xString += '</x-deck>'
    deckWrap.innerHTML = xString;

    //plot buffers
    for(i=0; i<this.viewNames.length; i++){
        //divs to hold kinetic contexts
        drawTarget = document.createElement('div');
        drawTarget.setAttribute('id', this.id+this.viewNames[i]+'Draw');
        document.getElementById(this.id+this.viewNames[i] + 'Card').appendChild(drawTarget);
    }

    //plot control widget
    plotControlWrap.setAttribute('id', this.id+'PlotControl');
    plotControlWrap.setAttribute('class', 'plotControlWidget');
    this.appendChild(plotControlWrap);
    document.getElementById(this.id+'PlotControl').onchange = this.updatePlotParameters.bind(this);

    plotControlTitle.setAttribute('id', this.id+'PlotControlTitle');
    plotControlWrap.appendChild(plotControlTitle);
    document.getElementById(this.id + 'PlotControlTitle').innerHTML = 'Plot Control'

    //x-deck navigation
    if(this.viewNames.length > 1){
        deckNavigator = document.createElement('select');
        deckNavigator.id = this.id + 'viewSelect';
        for(i=0; i<this.viewNames.length; i++){
            deckOption = document.createElement('option');
            deckOption.innerHTML = this.viewNames[i];
            deckOption.value = i;
            deckNavigator.appendChild(deckOption);
        }
        deckNavigator.onchange = function(){
            var viewVal = selected(this.id+'viewSelect');

            document.getElementById(this.id+'Deck').shuffleTo(viewVal);
            this.displayIndex = viewVal;
            this.update();  //repaint right away
        }.bind(this)
        plotControlWrap.appendChild(deckNavigator);
    }

    plotControlMinLabel.setAttribute('id', this.id+'PlotControlMinLabel');
    plotControlWrap.appendChild(plotControlMinLabel)
    document.getElementById(this.id+'PlotControlMinLabel').innerHTML = 'Min: ';
    plotControlMin.setAttribute('id', this.id + 'PlotControlMin');
    plotControlMin.setAttribute('type', 'number');
    plotControlMin.setAttribute('step', 'any');
    plotControlWrap.appendChild(plotControlMin);

    plotControlMaxLabel.setAttribute('id', this.id+'PlotControlMaxLabel');
    plotControlWrap.appendChild(plotControlMaxLabel)
    document.getElementById(this.id+'PlotControlMaxLabel').innerHTML = 'Max: ';
    plotControlMax.setAttribute('id', this.id + 'PlotControlMax');
    plotControlMax.setAttribute('type', 'number');
    plotControlMax.setAttribute('step', 'any');
    plotControlWrap.appendChild(plotControlMax);

    plotScale.setAttribute('id', this.id+'PlotControlScale');
    plotControlWrap.appendChild(plotScale);

    plotScaleLin.setAttribute('id', this.id+'PlotScaleLin');
    plotScaleLin.setAttribute('value', 'lin');
    plotScale.appendChild(plotScaleLin);
    document.getElementById(this.id+'PlotScaleLin').innerHTML = 'Linear';

    plotScaleLog.setAttribute('id', this.id+'PlotScaleLog');
    plotScaleLog.setAttribute('value', 'log');
    plotScale.appendChild(plotScaleLog);
    document.getElementById(this.id+'PlotScaleLog').innerHTML = 'Log';

    ///////////////////////
    //State variables
    ///////////////////////
    this.currentView = 'Rate';
    this.currentUnit = 'Hz';
    this.displayIndex = 0;  //always start on the first card, guarnateed to exist.
    this.HVcrates = 0;
    detectHVcrates(this.MIDAS, this);
    //cells
    this.cells = {};
    this.summaryDepth = 0;

    ////////////////////////////
    //Drawing parameters
    ////////////////////////////
    this.frameLineWidth = 2;
    this.frameColor = '#000000';//'#999999';
    this.width = width;
    this.height = height;

    ///////////////////////////
    //Scale Parameters
    ///////////////////////////
    this.scale = 'ROOT Rainbow';
    this.min = {};
    this.max = {};
    this.scaleType = {};
    for(i=0; i<this.views.length; i++){
        this.min[this.views[i]] = canHas(localStorage.getItem(name+this.views[i]+'min'), 0);
        this.max[this.views[i]] = canHas(localStorage.getItem(name+this.views[i]+'max'), 3000);
        this.scaleType[this.views[i]] = canHas(localStorage.getItem(name+this.views[i]+'scaleType'), 'lin');
    }

    //if anything was in local storage, communicate this to the UI:
    plotControlMin.value = this.min[this.currentView];
    plotControlMax.value = this.max[this.currentView];
    plotScale.value = this.scaleType[this.currentView];

    ///////////////////////////
    //Tooltip state
    ///////////////////////////
    this.lastTTindex = -1;

    ////////////////////////////
    //Kinetic.js setup
    ////////////////////////////

    //indices for these arrays correspond to the x-card index on display
    this.stage = [];
    this.mainLayer = [];
    this.scaleLayer = [];
    this.tooltipLayer = [];
    this.TTbkg = [];
    this.text = [];

    for(i=0; i<this.viewNames.length; i++){

        //point kinetic at the div and set up the staging and layers:
        this.stage[i] = new Kinetic.Stage({
            container: this.id+this.viewNames[i]+'Draw',
            width: width,
            height: height
        });
        this.mainLayer[i] = new Kinetic.Layer();       //main rendering layer
        this.scaleLayer[i] = new Kinetic.Layer();      //layer for scales / legends
        this.tooltipLayer[i] = new Kinetic.Layer();    //layer for tooltip info

        //tooltip background:
        this.TTbkg[i] = new Kinetic.Rect({
            x:-1000,
            y:-1000,
            width:100,
            height:100,
            fill:'rgba(0,0,0,0.8)',
            stroke: 'rgba(0,0,0,0)',
            listening: false
        });
        this.tooltipLayer[i].add(this.TTbkg[i]);

        //tooltip text:
        this.text[i] = new Kinetic.Text({
            x: -1000,
            y: -1000,
            fontFamily: 'Arial',
            fontSize: 16,
            text: '',
            lineHeight: 1.2,
            fill: '#EEEEEE',
            listening: false
        });
        this.tooltipLayer[i].add(this.text[i]);
    }

    this.errorPattern = new Image();
    this.errorPattern.onload = function(){
        var key;

        for(key in this.cells){
            this.cells[key].setAttr('fillPatternImage', this.errorPattern);
        }

        this.mainLayer[this.displayIndex].draw();
    }.bind(this)
    this.errorPattern.src = 'static/img/static.gif'

    //let repopulate know that the detector would like to be updated every loop:
    if(!window.refreshTargets)
        window.refreshTargets = [];
    window.refreshTargets[window.refreshTargets.length] = this;

}

//stick the ODB equipment directory into its local slot:
function fetchODBEquipment(returnObj){
    if(!window.currentData.ODB)
        window.currentData.ODB = {};
    window.currentData.ODB.Equipment = returnObj;
}

//callback for fetching from the scalar service:
function parseRate(data){
    var key, subkey;

    if(!window.currentData.Rate)
        window.currentData.Rate = {};

    for(key in data){
        if (data.hasOwnProperty(key)) {
            for(subkey in data[key]){
                if(data[key].hasOwnProperty(subkey)){
                    window.currentData.Rate[subkey.toUpperCase().slice(0,10)] = data[key][subkey].TRIGREQ;
                }
            }
        }
    }
}

//similar function for the threshold service:
function parseThreshold(data){
    var key;
    if(!window.currentData.Threshold)
        window.currentData.Threshold = {};

    if(data['parameters']['thresholds']){
        for(key in data['parameters']['thresholds']){
            window.currentData.Threshold[key.toUpperCase().slice(0,10)] = data['parameters']['thresholds'][key];
        }
    }
}

//function to make a reasonable decision on how many decimal places to show, whether to to use
//sci. notation on an axis tick mark, where <min> and <max> are the axis minimum and maximum,
//<nTicks> is the number of tickmarks on the axis, and we are returning the label for the <n>th
//tick mark
function generateTickLabel(min, max, nTicks, n){
    var range = max - min,
        smallestPrecision = range / (nTicks-1),
        tickValue = min + (max-min)/(nTicks-1)*n;

    //tickmark needs to be labeled to enough precision to show the difference between subsequent ticks:
    smallestPrecision = Math.floor(Math.log(smallestPrecision) / Math.log(10));

    if(smallestPrecision < 0)
        return tickValue.toFixed(-smallestPrecision)

    tickValue = Math.floor(tickValue/Math.pow(10, smallestPrecision)) * Math.pow(10, smallestPrecision);
    return tickValue+'';

}

//determine how many HV frontends are in the ODB.  Frontends must be named HV-0, HV-1, HV-2....
function detectHVcrates(MIDAS, obj){
    var xmlhttp = new XMLHttpRequest();

    //once this is all dealt with, refresh the display immediately
    xmlhttp.onreadystatechange = function(target){
        var data, nCrates = 0;

        if (this.readyState != 4) return;

        data = JSON.parse(this.responseText.slice(this.responseText.indexOf('{'), this.responseText.lastIndexOf('}')+1 ) )

        while(data['HV-'+nCrates])
            nCrates++

        target.HVcrates = nCrates;
    }.bind(xmlhttp, obj)
    //fire async
    xmlhttp.open('GET', MIDAS + '/?cmd=jcopy&odb=/Equipment&encoding=json-nokeys');
    xmlhttp.send();
}<|endoftext|>