Measurement plugin and JS API working
|
@ -1,5 +1,5 @@
|
|||
import logging, os, sys
|
||||
from abc import ABC, abstractmethod
|
||||
from abc import ABC
|
||||
|
||||
logger = logging.getLogger('app.logger')
|
||||
|
||||
|
@ -7,7 +7,6 @@ class PluginBase(ABC):
|
|||
def __init__(self):
|
||||
self.name = self.get_module_name().split(".")[-2]
|
||||
|
||||
@abstractmethod
|
||||
def register(self):
|
||||
pass
|
||||
|
||||
|
@ -27,6 +26,9 @@ class PluginBase(ABC):
|
|||
def get_include_js_urls(self):
|
||||
return ["/plugins/{}/{}".format(self.get_name(), js_file) for js_file in self.include_js_files()]
|
||||
|
||||
def get_include_css_urls(self):
|
||||
return ["/plugins/{}/{}".format(self.get_name(), css_file) for css_file in self.include_css_files()]
|
||||
|
||||
def has_public_path(self):
|
||||
return os.path.isdir(self.get_path("public"))
|
||||
|
||||
|
@ -38,5 +40,13 @@ class PluginBase(ABC):
|
|||
"""
|
||||
return []
|
||||
|
||||
def include_css_files(self):
|
||||
"""
|
||||
Should be overriden by plugins to communicate
|
||||
which CSS files should be included in the WebODM interface
|
||||
All paths are relative to a plugin's /public folder.
|
||||
"""
|
||||
return []
|
||||
|
||||
def __str__(self):
|
||||
return "[{}]".format(self.get_module_name())
|
|
@ -9,6 +9,9 @@ ul#side-menu.nav a,
|
|||
{
|
||||
color: theme("primary");
|
||||
}
|
||||
.theme-border-primary{
|
||||
border-color: theme("primary");
|
||||
}
|
||||
.tooltip{
|
||||
.tooltip-inner{
|
||||
background-color: theme("primary");
|
||||
|
@ -162,6 +165,9 @@ footer,
|
|||
.popover-title{
|
||||
border-bottom-color: theme("border");
|
||||
}
|
||||
.theme-border{
|
||||
border-color: theme("border");
|
||||
}
|
||||
|
||||
/* Highlight */
|
||||
.task-list-item:nth-child(odd),
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
import { EventEmitter } from 'fbemitter';
|
||||
import Utils from './Utils';
|
||||
|
||||
const { assert } = Utils;
|
||||
|
||||
if (!window.PluginsAPI){
|
||||
const events = new EventEmitter();
|
||||
|
||||
window.PluginsAPI = {
|
||||
Map: {
|
||||
AddPanel: (callback) => {
|
||||
events.addListener('Map::Loaded', callback);
|
||||
},
|
||||
|
||||
Loaded: (params) => {
|
||||
assert(params.map !== undefined);
|
||||
events.emit('Map::Loaded', params);
|
||||
}
|
||||
},
|
||||
|
||||
events
|
||||
};
|
||||
}
|
||||
|
||||
export default window.PluginsAPI;
|
||||
|
|
@ -73,6 +73,13 @@ export default {
|
|||
}
|
||||
throw message; // Fallback
|
||||
}
|
||||
},
|
||||
|
||||
getCurrentScriptDir: function(){
|
||||
let scripts= document.getElementsByTagName('script');
|
||||
let path= scripts[scripts.length-1].src.split('?')[0]; // remove any ?query
|
||||
let mydir= path.split('/').slice(0, -1).join('/')+'/'; // remove last filename part of path
|
||||
return mydir;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
import { EventEmitter } from 'fbemitter';
|
||||
import ApiFactory from './ApiFactory';
|
||||
import Map from './Map';
|
||||
import $ from 'jquery';
|
||||
import SystemJS from 'SystemJS';
|
||||
|
||||
if (!window.PluginsAPI){
|
||||
const events = new EventEmitter();
|
||||
const factory = new ApiFactory(events);
|
||||
|
||||
SystemJS.config({
|
||||
baseURL: '/plugins',
|
||||
map: {
|
||||
css: '/static/app/js/vendor/css.js'
|
||||
},
|
||||
meta: {
|
||||
'*.css': { loader: 'css' }
|
||||
}
|
||||
});
|
||||
|
||||
window.PluginsAPI = {
|
||||
Map: factory.create(Map),
|
||||
|
||||
SystemJS,
|
||||
events
|
||||
};
|
||||
}
|
||||
|
||||
export default window.PluginsAPI;
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
import SystemJS from 'SystemJS';
|
||||
|
||||
export default class ApiFactory{
|
||||
// @param events {EventEmitter}
|
||||
constructor(events){
|
||||
this.events = events;
|
||||
}
|
||||
|
||||
// @param api {Object}
|
||||
create(api){
|
||||
|
||||
// Adds two functions to obj
|
||||
// - eventName
|
||||
// - triggerEventName
|
||||
// We could just use events, but methods
|
||||
// are more robust as we can detect more easily if
|
||||
// things break
|
||||
const addEndpoint = (obj, eventName, preTrigger = () => {}) => {
|
||||
obj[eventName] = (callbackOrDeps, callbackOrUndef) => {
|
||||
if (Array.isArray(callbackOrDeps)){
|
||||
// Deps
|
||||
// Load dependencies, then raise event as usual
|
||||
// by appending the dependencies to the argument list
|
||||
this.events.addListener(`${api.namespace}::${eventName}`, (...args) => {
|
||||
Promise.all(callbackOrDeps.map(dep => SystemJS.import(dep)))
|
||||
.then((...deps) => {
|
||||
callbackOrUndef(...(Array.from(args).concat(...deps)));
|
||||
});
|
||||
});
|
||||
}else{
|
||||
// Callback
|
||||
this.events.addListener(`${api.namespace}::${eventName}`, callbackOrDeps);
|
||||
}
|
||||
}
|
||||
|
||||
const triggerEventName = "trigger" + eventName[0].toUpperCase() + eventName.slice(1);
|
||||
|
||||
obj[triggerEventName] = (...args) => {
|
||||
preTrigger(...args);
|
||||
this.events.emit(`${api.namespace}::${eventName}`, ...args);
|
||||
};
|
||||
}
|
||||
|
||||
const obj = {};
|
||||
api.endpoints.forEach(endpoint => {
|
||||
if (!Array.isArray(endpoint)) endpoint = [endpoint];
|
||||
addEndpoint(obj, ...endpoint);
|
||||
});
|
||||
return obj;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
import Utils from '../Utils';
|
||||
|
||||
const { assert } = Utils;
|
||||
|
||||
const leafletPreCheck = (options) => {
|
||||
assert(options.map !== undefined);
|
||||
};
|
||||
|
||||
export default {
|
||||
namespace: "Map",
|
||||
|
||||
endpoints: [
|
||||
["willAddControls", leafletPreCheck],
|
||||
["didAddControls", leafletPreCheck]
|
||||
]
|
||||
};
|
||||
|
|
@ -3,8 +3,6 @@ import '../css/Map.scss';
|
|||
import 'leaflet/dist/leaflet.css';
|
||||
import Leaflet from 'leaflet';
|
||||
import async from 'async';
|
||||
import 'leaflet-measure/dist/leaflet-measure.css';
|
||||
import 'leaflet-measure/dist/leaflet-measure';
|
||||
import '../vendor/leaflet/L.Control.MousePosition.css';
|
||||
import '../vendor/leaflet/L.Control.MousePosition';
|
||||
import '../vendor/leaflet/Leaflet.Autolayers/css/leaflet.auto-layers.css';
|
||||
|
@ -15,7 +13,7 @@ import SwitchModeButton from './SwitchModeButton';
|
|||
import ShareButton from './ShareButton';
|
||||
import AssetDownloads from '../classes/AssetDownloads';
|
||||
import PropTypes from 'prop-types';
|
||||
import PluginsAPI from '../classes/PluginsAPI';
|
||||
import PluginsAPI from '../classes/plugins/API';
|
||||
|
||||
class Map extends React.Component {
|
||||
static defaultProps = {
|
||||
|
@ -173,16 +171,22 @@ class Map extends React.Component {
|
|||
|
||||
this.map = Leaflet.map(this.container, {
|
||||
scrollWheelZoom: true,
|
||||
positionControl: true
|
||||
positionControl: true,
|
||||
zoomControl: false
|
||||
});
|
||||
|
||||
const measureControl = Leaflet.control.measure({
|
||||
primaryLengthUnit: 'meters',
|
||||
secondaryLengthUnit: 'feet',
|
||||
primaryAreaUnit: 'sqmeters',
|
||||
secondaryAreaUnit: 'acres'
|
||||
PluginsAPI.Map.triggerWillAddControls({
|
||||
map: this.map
|
||||
});
|
||||
measureControl.addTo(this.map);
|
||||
|
||||
Leaflet.control.scale({
|
||||
maxWidth: 250,
|
||||
}).addTo(this.map);
|
||||
|
||||
//add zoom control with your options
|
||||
Leaflet.control.zoom({
|
||||
position:'bottomleft'
|
||||
}).addTo(this.map);
|
||||
|
||||
if (showBackground) {
|
||||
this.basemaps = {
|
||||
|
@ -215,10 +219,6 @@ class Map extends React.Component {
|
|||
}).addTo(this.map);
|
||||
|
||||
this.map.fitWorld();
|
||||
|
||||
Leaflet.control.scale({
|
||||
maxWidth: 250,
|
||||
}).addTo(this.map);
|
||||
this.map.attributionControl.setPrefix("");
|
||||
|
||||
this.loadImageryLayers(true).then(() => {
|
||||
|
@ -239,7 +239,7 @@ class Map extends React.Component {
|
|||
// PluginsAPI.events.addListener('Map::AddPanel', (e) => {
|
||||
// console.log("Received response: " + e);
|
||||
// });
|
||||
PluginsAPI.Map.Loaded({
|
||||
PluginsAPI.Map.triggerDidAddControls({
|
||||
map: this.map
|
||||
});
|
||||
}
|
||||
|
@ -275,6 +275,7 @@ class Map extends React.Component {
|
|||
return (
|
||||
<div style={{height: "100%"}} className="map">
|
||||
<ErrorMessage bind={[this, 'error']} />
|
||||
|
||||
<div
|
||||
style={{height: "100%"}}
|
||||
ref={(domNode) => (this.container = domNode)}
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
.map{
|
||||
position: relative;
|
||||
|
||||
.leaflet-popup-content{
|
||||
.title{
|
||||
font-weight: bold;
|
||||
|
@ -24,7 +26,7 @@
|
|||
|
||||
.shareButton{
|
||||
z-index: 2000;
|
||||
bottom: -11px;
|
||||
bottom: 11px;
|
||||
right: 38px;
|
||||
}
|
||||
}
|
|
@ -3,7 +3,7 @@
|
|||
display: block;
|
||||
|
||||
&.top{
|
||||
top: -32px;
|
||||
top: -54px;
|
||||
}
|
||||
&.bottom{
|
||||
top: 32px;
|
||||
|
|
|
@ -2,6 +2,6 @@
|
|||
border-width: 1px;
|
||||
position: absolute;
|
||||
z-index: 2000;
|
||||
bottom: -22px;
|
||||
bottom: 22px;
|
||||
right: 12px;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import '../css/main.scss';
|
||||
import './django/csrf';
|
||||
import ReactDOM from 'react-dom';
|
||||
import PluginsAPI from './classes/PluginsAPI';
|
||||
import PluginsAPI from './classes/plugins/API';
|
||||
|
||||
// Main is always executed first in the page
|
||||
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
var waitSeconds = 100;
|
||||
|
||||
var head = document.getElementsByTagName('head')[0];
|
||||
|
||||
var isWebkit = !!window.navigator.userAgent.match(/AppleWebKit\/([^ ;]*)/);
|
||||
var webkitLoadCheck = function(link, callback) {
|
||||
setTimeout(function() {
|
||||
for (var i = 0; i < document.styleSheets.length; i++) {
|
||||
var sheet = document.styleSheets[i];
|
||||
if (sheet.href == link.href)
|
||||
return callback();
|
||||
}
|
||||
webkitLoadCheck(link, callback);
|
||||
}, 10);
|
||||
};
|
||||
|
||||
var cssIsReloadable = function cssIsReloadable(links) {
|
||||
// Css loaded on the page initially should be skipped by the first
|
||||
// systemjs load, and marked for reload
|
||||
var reloadable = true;
|
||||
forEach(links, function(link) {
|
||||
if(!link.hasAttribute('data-systemjs-css')) {
|
||||
reloadable = false;
|
||||
link.setAttribute('data-systemjs-css', '');
|
||||
}
|
||||
});
|
||||
return reloadable;
|
||||
}
|
||||
|
||||
var findExistingCSS = function findExistingCSS(url){
|
||||
// Search for existing link to reload
|
||||
var links = head.getElementsByTagName('link')
|
||||
return filter(links, function(link){ return link.href === url; });
|
||||
}
|
||||
|
||||
var noop = function() {};
|
||||
|
||||
var loadCSS = function(url, existingLinks) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
var timeout = setTimeout(function() {
|
||||
reject('Unable to load CSS');
|
||||
}, waitSeconds * 1000);
|
||||
var _callback = function(error) {
|
||||
clearTimeout(timeout);
|
||||
link.onload = link.onerror = noop;
|
||||
setTimeout(function() {
|
||||
if (error)
|
||||
reject(error);
|
||||
else
|
||||
resolve('');
|
||||
}, 7);
|
||||
};
|
||||
var link = document.createElement('link');
|
||||
link.type = 'text/css';
|
||||
link.rel = 'stylesheet';
|
||||
link.href = url;
|
||||
link.setAttribute('data-systemjs-css', '');
|
||||
if (!isWebkit) {
|
||||
link.onload = function() {
|
||||
_callback();
|
||||
}
|
||||
} else {
|
||||
webkitLoadCheck(link, _callback);
|
||||
}
|
||||
link.onerror = function(event) {
|
||||
_callback(event.error || new Error('Error loading CSS file.'));
|
||||
};
|
||||
if (existingLinks.length)
|
||||
head.insertBefore(link, existingLinks[0]);
|
||||
else
|
||||
head.appendChild(link);
|
||||
})
|
||||
// Remove the old link regardless of loading outcome
|
||||
.then(function(result){
|
||||
forEach(existingLinks, function(link){link.parentElement.removeChild(link);})
|
||||
return result;
|
||||
}, function(err){
|
||||
forEach(existingLinks, function(link){link.parentElement.removeChild(link);})
|
||||
throw err;
|
||||
})
|
||||
};
|
||||
|
||||
exports.fetch = function(load) {
|
||||
// dont reload styles loaded in the head
|
||||
var links = findExistingCSS(load.address);
|
||||
if(!cssIsReloadable(links))
|
||||
return '';
|
||||
return loadCSS(load.address, links);
|
||||
};
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Base CSS Plugin Class
|
||||
*/
|
||||
|
||||
function CSSPluginBase(compileCSS) {
|
||||
this.compileCSS = compileCSS;
|
||||
|
||||
this.translate = function(load, opts) {
|
||||
var loader = this;
|
||||
if (loader.builder && loader.buildCSS === false) {
|
||||
load.metadata.build = false;
|
||||
return;
|
||||
}
|
||||
|
||||
var path = this._nodeRequire && this._nodeRequire('path');
|
||||
|
||||
return Promise.resolve(compileCSS.call(loader, load.source, load.address, load.metadata.loaderOptions || {}))
|
||||
.then(function(result) {
|
||||
load.metadata.style = result.css;
|
||||
load.metadata.styleSourceMap = result.map;
|
||||
if (result.moduleFormat)
|
||||
load.metadata.format = result.moduleFormat;
|
||||
return result.moduleSource || '';
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
var isWin = typeof process != 'undefined' && process.platform.match(/^win/);
|
||||
function toFileURL(path) {
|
||||
return 'file://' + (isWin ? '/' : '') + path.replace(/\\/g, '/');
|
||||
}
|
||||
|
||||
var builderPromise;
|
||||
function getBuilder(loader) {
|
||||
if (builderPromise)
|
||||
return builderPromise;
|
||||
return builderPromise = loader['import']('./css-plugin-base-builder.js', module.id);
|
||||
}
|
||||
|
||||
CSSPluginBase.prototype.bundle = function(loads, compileOpts, outputOpts) {
|
||||
var loader = this;
|
||||
return getBuilder(loader)
|
||||
.then(function(builder) {
|
||||
return builder.bundle.call(loader, loads, compileOpts, outputOpts);
|
||||
});
|
||||
};
|
||||
|
||||
CSSPluginBase.prototype.listAssets = function(loads, opts) {
|
||||
var loader = this;
|
||||
return getBuilder(loader)
|
||||
.then(function(builder) {
|
||||
return builder.listAssets.call(loader, loads, opts);
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
* <style> injection browser plugin
|
||||
*/
|
||||
// NB hot reloading support here
|
||||
CSSPluginBase.prototype.instantiate = function(load) {
|
||||
if (this.builder || typeof document === 'undefined')
|
||||
return;
|
||||
|
||||
var style = document.createElement('style');
|
||||
style.type = 'text/css';
|
||||
style.innerHTML = load.metadata.style;
|
||||
document.head.appendChild(style);
|
||||
};
|
||||
|
||||
module.exports = CSSPluginBase;
|
|
@ -0,0 +1,162 @@
|
|||
if (typeof window !== 'undefined') {
|
||||
var waitSeconds = 100;
|
||||
|
||||
var head = document.getElementsByTagName('head')[0];
|
||||
|
||||
var isWebkit = !!window.navigator.userAgent.match(/AppleWebKit\/([^ ;]*)/);
|
||||
var webkitLoadCheck = function(link, callback) {
|
||||
setTimeout(function() {
|
||||
for (var i = 0; i < document.styleSheets.length; i++) {
|
||||
var sheet = document.styleSheets[i];
|
||||
if (sheet.href == link.href)
|
||||
return callback();
|
||||
}
|
||||
webkitLoadCheck(link, callback);
|
||||
}, 10);
|
||||
};
|
||||
|
||||
var cssIsReloadable = function cssIsReloadable(links) {
|
||||
// Css loaded on the page initially should be skipped by the first
|
||||
// systemjs load, and marked for reload
|
||||
var reloadable = true;
|
||||
forEach(links, function(link) {
|
||||
if(!link.hasAttribute('data-systemjs-css')) {
|
||||
reloadable = false;
|
||||
link.setAttribute('data-systemjs-css', '');
|
||||
}
|
||||
});
|
||||
return reloadable;
|
||||
}
|
||||
|
||||
var findExistingCSS = function findExistingCSS(url){
|
||||
// Search for existing link to reload
|
||||
var links = head.getElementsByTagName('link')
|
||||
return filter(links, function(link){ return link.href === url; });
|
||||
}
|
||||
|
||||
var noop = function() {};
|
||||
|
||||
var loadCSS = function(url, existingLinks) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
var timeout = setTimeout(function() {
|
||||
reject('Unable to load CSS');
|
||||
}, waitSeconds * 1000);
|
||||
var _callback = function(error) {
|
||||
clearTimeout(timeout);
|
||||
link.onload = link.onerror = noop;
|
||||
setTimeout(function() {
|
||||
if (error)
|
||||
reject(error);
|
||||
else
|
||||
resolve('');
|
||||
}, 7);
|
||||
};
|
||||
var link = document.createElement('link');
|
||||
link.type = 'text/css';
|
||||
link.rel = 'stylesheet';
|
||||
link.href = url;
|
||||
link.setAttribute('data-systemjs-css', '');
|
||||
if (!isWebkit) {
|
||||
link.onload = function() {
|
||||
_callback();
|
||||
}
|
||||
} else {
|
||||
webkitLoadCheck(link, _callback);
|
||||
}
|
||||
link.onerror = function(event) {
|
||||
_callback(event.error || new Error('Error loading CSS file.'));
|
||||
};
|
||||
if (existingLinks.length)
|
||||
head.insertBefore(link, existingLinks[0]);
|
||||
else
|
||||
head.appendChild(link);
|
||||
})
|
||||
// Remove the old link regardless of loading outcome
|
||||
.then(function(result){
|
||||
forEach(existingLinks, function(link){link.parentElement.removeChild(link);})
|
||||
return result;
|
||||
}, function(err){
|
||||
forEach(existingLinks, function(link){link.parentElement.removeChild(link);})
|
||||
throw err;
|
||||
})
|
||||
};
|
||||
|
||||
exports.fetch = function(load) {
|
||||
// dont reload styles loaded in the head
|
||||
var links = findExistingCSS(load.address);
|
||||
if(!cssIsReloadable(links))
|
||||
return '';
|
||||
return loadCSS(load.address, links);
|
||||
};
|
||||
}
|
||||
else {
|
||||
var builderPromise;
|
||||
function getBuilder(loader) {
|
||||
if (builderPromise)
|
||||
return builderPromise;
|
||||
|
||||
return builderPromise = System['import']('./css-plugin-base.js', module.id)
|
||||
.then(function(CSSPluginBase) {
|
||||
return new CSSPluginBase(function compile(source, address) {
|
||||
return {
|
||||
css: source,
|
||||
map: null,
|
||||
moduleSource: null,
|
||||
moduleFormat: null
|
||||
};
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
exports.cssPlugin = true;
|
||||
exports.fetch = function(load, fetch) {
|
||||
if (!this.builder)
|
||||
return '';
|
||||
return fetch(load);
|
||||
};
|
||||
exports.translate = function(load, opts) {
|
||||
if (!this.builder)
|
||||
return '';
|
||||
var loader = this;
|
||||
return getBuilder(loader).then(function(builder) {
|
||||
return builder.translate.call(loader, load, opts);
|
||||
});
|
||||
};
|
||||
exports.instantiate = function(load, opts) {
|
||||
if (!this.builder)
|
||||
return;
|
||||
var loader = this;
|
||||
return getBuilder(loader).then(function(builder) {
|
||||
return builder.instantiate.call(loader, load, opts);
|
||||
});
|
||||
};
|
||||
exports.bundle = function(loads, compileOpts, outputOpts) {
|
||||
var loader = this;
|
||||
return getBuilder(loader).then(function(builder) {
|
||||
return builder.bundle.call(loader, loads, compileOpts, outputOpts);
|
||||
});
|
||||
};
|
||||
exports.listAssets = function(loads, opts) {
|
||||
var loader = this;
|
||||
return getBuilder(loader).then(function(builder) {
|
||||
return builder.listAssets.call(loader, loads, opts);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
// Because IE8?
|
||||
function filter(arrayLike, func) {
|
||||
var arr = []
|
||||
forEach(arrayLike, function(item){
|
||||
if(func(item))
|
||||
arr.push(item);
|
||||
});
|
||||
return arr;
|
||||
}
|
||||
|
||||
// Because IE8?
|
||||
function forEach(arrayLike, func){
|
||||
for (var i = 0; i < arrayLike.length; i++) {
|
||||
func(arrayLike[i])
|
||||
}
|
||||
}
|
|
@ -0,0 +1,329 @@
|
|||
const env = {};
|
||||
(function (environment) {
|
||||
|
||||
/**
|
||||
* List of existings modules
|
||||
* @type {Object}
|
||||
*/
|
||||
var modules = {};
|
||||
|
||||
/**
|
||||
* Array of waiting modules
|
||||
* @type {Array}
|
||||
*/
|
||||
var waitingModules = [];
|
||||
|
||||
/**
|
||||
* Count created script for control
|
||||
* @type {Number}
|
||||
*/
|
||||
var scriptCounter = 1;
|
||||
|
||||
/**
|
||||
* Base element check for IE 6-8
|
||||
* @type {Node}
|
||||
*/
|
||||
var baseElement = document.getElementsByTagName('base')[0];
|
||||
|
||||
/**
|
||||
* Head element
|
||||
* @type {Node}
|
||||
*/
|
||||
var head = document.getElementsByTagName('head')[0];
|
||||
|
||||
/**
|
||||
* @param {String} name the name of the module
|
||||
* @param {Array} deps dependencies of the module
|
||||
* @param {Function} module module definition
|
||||
* @param {String} dir relative dir path from which to load files
|
||||
*/
|
||||
function Include (name, deps, module, dir) {
|
||||
var self = this;
|
||||
|
||||
if (typeof name !== "string") {
|
||||
module = deps;
|
||||
deps = name;
|
||||
name = null;
|
||||
}
|
||||
|
||||
if (deps.constructor !== [].constructor) {
|
||||
module = deps;
|
||||
deps = [];
|
||||
}
|
||||
|
||||
waitingModules.unshift([name, deps, module]);
|
||||
|
||||
/**
|
||||
* Uid for script differentiation
|
||||
* @type {String}
|
||||
*/
|
||||
self.uid = Math.random().toString(36).replace(/[^a-z0-9]+/g, '').substr(0, 10);
|
||||
|
||||
self.checkModuleLoaded();
|
||||
|
||||
if (deps.length) {
|
||||
self.each(deps, self.parseFiles);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Loop trougth an array of element with the given function
|
||||
* @param {Array|NodeList} array array to loop
|
||||
* @param {Function} callback function to execute with each element
|
||||
*/
|
||||
Include.prototype.each = function (array, callback) {
|
||||
var self = this,
|
||||
i;
|
||||
|
||||
for (i = 0; i < array.length; i++) {
|
||||
if (array[i] !== undefined && callback.call(self, array[i], i, array) === false) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get element data id
|
||||
* @param {String} name
|
||||
* @param {Boolean} clean only clean the name
|
||||
* @return {String}
|
||||
*/
|
||||
Include.prototype.getId = function (name, clean) {
|
||||
return (clean ? '' : this.uid + '-') + name.replace(/[^a-z0-9]+/g, '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a module is loaded
|
||||
*/
|
||||
Include.prototype.checkModuleLoaded = function () {
|
||||
var self = this;
|
||||
|
||||
self.each(waitingModules, function (module, i) {
|
||||
var name = module[0],
|
||||
dependencies = module[1],
|
||||
exec = module[2],
|
||||
args = [];
|
||||
|
||||
self.each(dependencies, function (dependencie, n, t) {
|
||||
n = dependencie.push ? dependencie[0] : dependencie;
|
||||
t = document.querySelector('[data-id*="' + self.getId(n, 1) + '"]');
|
||||
|
||||
if (t && t.nodeName == "LINK") {
|
||||
args.push(null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (modules[n] !== undefined) {
|
||||
args.push(modules[n]);
|
||||
}
|
||||
});
|
||||
|
||||
if (dependencies.length === args.length || dependencies.length === 0) {
|
||||
if (name === null && i+1 === waitingModules.length) {
|
||||
waitingModules = [];
|
||||
scriptCounter = 1;
|
||||
}
|
||||
|
||||
exec = typeof exec == 'function' ? exec.apply(this, args) : exec;
|
||||
modules[name] = exec;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* onModuleLoaded
|
||||
* @param {String} name name of the module
|
||||
* @param {Number} index index of the module
|
||||
*/
|
||||
Include.prototype.onModuleLoaded = function (name, index) {
|
||||
var self = this;
|
||||
|
||||
// Is this script add a waiting module ? If not, that's a "normal" script file
|
||||
if (index > waitingModules.length) {
|
||||
scriptCounter--;
|
||||
modules[name] = modules[name] || scriptCounter;
|
||||
} else if (waitingModules[0][0] === null) {
|
||||
waitingModules[0][0] = name;
|
||||
}
|
||||
|
||||
self.checkModuleLoaded();
|
||||
}
|
||||
|
||||
/**
|
||||
* On Load event
|
||||
* @param {Event} event event of the load
|
||||
* @param {Function} caller
|
||||
*/
|
||||
Include.prototype.onLoad = function (event, caller) {
|
||||
var self = this,
|
||||
target = (event.currentTarget || event.srcElement);
|
||||
|
||||
//Check if the script is realy loaded and executed
|
||||
if (event.type !== "load" && target.readyState != "complete") {
|
||||
return;
|
||||
}
|
||||
|
||||
target.setAttribute('data-loaded', true);
|
||||
self.onModuleLoaded(target.getAttribute('data-module'), target.getAttribute('data-count'));
|
||||
|
||||
// Old browser need to use the detachEvent method
|
||||
if (target.attachEvent) {
|
||||
target.detachEvent('onreadystatechange', caller);
|
||||
} else {
|
||||
target.removeEventListener('load', caller);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Watch for css load
|
||||
* @param {Element} elem elem to check loading
|
||||
*/
|
||||
Include.prototype.watchCss = function (elem) {
|
||||
var self = this,
|
||||
sheets = document.styleSheets,
|
||||
i = sheets.length,
|
||||
href = elem.href.split('//').pop();
|
||||
|
||||
// loop on document stylesheets to check if media is loaded
|
||||
while (i--) {
|
||||
if (sheets[i].href.indexOf(href) != -1) {
|
||||
elem.setAttribute('data-loaded', true);
|
||||
self.onModuleLoaded(elem.getAttribute('data-module'), elem.getAttribute('data-count'));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout(function () {
|
||||
self.watchCss.call(self, elem);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach events to an element
|
||||
* @param {Element} elem elem to attach event
|
||||
* @param {Boolean} isJs is elem a script
|
||||
*/
|
||||
Include.prototype.attachEvents = function (elem, isJs) {
|
||||
var self = this,
|
||||
cb = function () {
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
args.push(cb);
|
||||
|
||||
self.onLoad.apply(self, args);
|
||||
};
|
||||
|
||||
|
||||
if (isJs) {
|
||||
if (elem.attachEvent) {
|
||||
elem.attachEvent('onreadystatechange', cb);
|
||||
} else {
|
||||
elem.addEventListener('load', cb, true);
|
||||
}
|
||||
} else {
|
||||
self.watchCss(elem);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a script already load
|
||||
* @param {String} moduleName module to load
|
||||
* @param {String} isJs type of file
|
||||
*/
|
||||
Include.prototype.checkExists = function (moduleName, isJs) {
|
||||
var exists = false;
|
||||
|
||||
this.each(document.getElementsByTagName(isJs ? 'script' : 'link'), function (elem) {
|
||||
if (elem.getAttribute('data-module') && elem.getAttribute('data-module') === moduleName) {
|
||||
exists = elem;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
return exists;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a script element to load asked module
|
||||
* @param {String} moduleName name of the module
|
||||
* @param {String} moduleFile file to include
|
||||
* @param {String} isJs type of file
|
||||
*/
|
||||
Include.prototype.create = function (moduleName, moduleFile, isJs) {
|
||||
var self = this;
|
||||
|
||||
//SetTimeout prevent the element create browser rush
|
||||
setTimeout(function(){
|
||||
var elem = self.checkExists.call(self, moduleName, isJs);
|
||||
|
||||
if (elem) {
|
||||
return;
|
||||
}
|
||||
|
||||
scriptCounter++;
|
||||
|
||||
elem = document.createElement(isJs ? 'script' : 'link');
|
||||
|
||||
if (isJs) {
|
||||
elem.async = true;
|
||||
elem.type = "text/javascript";
|
||||
elem.src = moduleFile;
|
||||
} else {
|
||||
elem.media = "all";
|
||||
elem.href = moduleFile;
|
||||
elem.rel = "stylesheet"
|
||||
}
|
||||
|
||||
elem.setAttribute('data-id', self.getId(moduleName));
|
||||
elem.setAttribute('data-module', moduleName);
|
||||
elem.setAttribute('data-count', scriptCounter);
|
||||
elem.setAttribute('data-loaded', false);
|
||||
|
||||
if (baseElement) {
|
||||
//prevent IE 6-8 bug (script executed before appenchild execution.
|
||||
baseElement.parentNode.insertBefore(elem, baseElement);
|
||||
} else {
|
||||
head.appendChild(elem);
|
||||
}
|
||||
|
||||
self.attachEvents.call(self, elem, isJs);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a file to include
|
||||
* @param {String} file file to parse
|
||||
*/
|
||||
Include.prototype.parseFiles = function (file) {
|
||||
var moduleName = file.push ? file[0] : file,
|
||||
moduleFile = file.push ? file[1] : file,
|
||||
ext;
|
||||
|
||||
//Don't load module already loaded
|
||||
if (modules[moduleName]) {
|
||||
this.checkModuleLoaded();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (moduleFile.indexOf('//') == -1 && !/\.js/.test(moduleFile) && !/^http/.test(moduleFile)) {
|
||||
moduleFile = moduleFile.replace(/\./g, '/');
|
||||
moduleFile = moduleFile + '.js';
|
||||
}
|
||||
|
||||
ext = moduleFile.split('.').pop() == 'js';
|
||||
|
||||
this.create.call(this, moduleName, moduleFile, ext);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {String} name the name of the module
|
||||
* @param {Array} deps dependencies of the module
|
||||
* @param {Function} module module definition
|
||||
*/
|
||||
environment['include'] = environment['require'] = environment['define'] = function (name, deps, module) {
|
||||
return new Include(name, deps, module);
|
||||
};
|
||||
|
||||
})(env);
|
||||
|
||||
export default env;
|
|
@ -18,12 +18,14 @@
|
|||
|
||||
<script src="{% static 'app/js/vendor/modernizr-2.8.3.min.js' %}"></script>
|
||||
<script src="{% static 'app/js/vendor/jquery-1.11.2.min.js' %}"></script>
|
||||
<script src="{% static 'app/js/vendor/system.js' %}"></script>
|
||||
|
||||
{% load render_bundle from webpack_loader %}
|
||||
{% render_bundle 'main' %}
|
||||
|
||||
{% autoescape off %}
|
||||
{% get_plugins_js_includes %}
|
||||
{% get_plugins_css_includes %}
|
||||
{% endautoescape %}
|
||||
|
||||
<title>{{title|default:"Login"}} - {{ SETTINGS.app_name }}</title>
|
||||
|
@ -88,6 +90,7 @@
|
|||
<script src="{% static 'app/js/vendor/metisMenu.min.js' %}"></script>
|
||||
<script>
|
||||
$(function(){
|
||||
return;
|
||||
$('#side-menu').metisMenu();
|
||||
|
||||
$(window).bind("load resize", function() {
|
||||
|
|
|
@ -8,4 +8,10 @@ register = template.Library()
|
|||
def get_plugins_js_includes():
|
||||
# Flatten all urls for all plugins
|
||||
js_urls = list(itertools.chain(*[plugin.get_include_js_urls() for plugin in get_active_plugins()]))
|
||||
return "\n".join(map(lambda url: "<script src='{}'></script>".format(url), js_urls))
|
||||
return "\n".join(map(lambda url: "<script src='{}'></script>".format(url), js_urls))
|
||||
|
||||
@register.simple_tag(takes_context=False)
|
||||
def get_plugins_css_includes():
|
||||
# Flatten all urls for all plugins
|
||||
css_urls = list(itertools.chain(*[plugin.get_include_css_urls() for plugin in get_active_plugins()]))
|
||||
return "\n".join(map(lambda url: "<link href='{}' rel='stylesheet' type='text/css'>".format(url), css_urls))
|
||||
|
|
|
@ -43,7 +43,6 @@
|
|||
"jest": "^21.0.1",
|
||||
"json-loader": "^0.5.4",
|
||||
"leaflet": "^1.0.1",
|
||||
"leaflet-measure": "^2.0.5",
|
||||
"node-sass": "^3.10.1",
|
||||
"object.values": "^1.0.3",
|
||||
"proj4": "^2.4.3",
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
from .plugin import *
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"name": "Area/Length Measurements",
|
||||
"webodmMinVersion": "0.4.2",
|
||||
"description": "A plugin to compute area and length measurements on Leaflet",
|
||||
"version": "0.1.0",
|
||||
"author": "Piero Toffanin",
|
||||
"email": "pt@masseranolabs.com",
|
||||
"repository": "https://github.com/OpenDroneMap/WebODM",
|
||||
"tags": ["area", "length", "measurements"],
|
||||
"homepage": "https://github.com/OpenDroneMap/WebODM",
|
||||
"experimental": false,
|
||||
"deprecated": false
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
from app.plugins import PluginBase
|
||||
|
||||
class Plugin(PluginBase):
|
||||
def include_js_files(self):
|
||||
return ['main.js']
|
Po Szerokość: | Wysokość: | Rozmiar: 397 B |
Po Szerokość: | Wysokość: | Rozmiar: 762 B |
Po Szerokość: | Wysokość: | Rozmiar: 387 B |
Po Szerokość: | Wysokość: | Rozmiar: 692 B |
Po Szerokość: | Wysokość: | Rozmiar: 326 B |
Po Szerokość: | Wysokość: | Rozmiar: 462 B |
Po Szerokość: | Wysokość: | Rozmiar: 192 B |
Po Szerokość: | Wysokość: | Rozmiar: 277 B |
Po Szerokość: | Wysokość: | Rozmiar: 491 B |
Po Szerokość: | Wysokość: | Rozmiar: 1003 B |
Po Szerokość: | Wysokość: | Rozmiar: 279 B |
Po Szerokość: | Wysokość: | Rozmiar: 460 B |
|
@ -0,0 +1 @@
|
|||
.leaflet-control-measure h3,.leaflet-measure-resultpopup h3{margin:0 0 12px 0;padding-bottom:10px;line-height:1em;font-weight:normal;font-size:1.1em;border-bottom:solid 1px #DDD}.leaflet-control-measure p,.leaflet-measure-resultpopup p{margin:10px 0 0;line-height:1em}.leaflet-control-measure p:first-child,.leaflet-measure-resultpopup p:first-child{margin-top:0}.leaflet-control-measure a,.leaflet-measure-resultpopup a{color:#5E66CC;text-decoration:none}.leaflet-control-measure a:hover,.leaflet-measure-resultpopup a:hover{opacity:0.5;text-decoration:none}.leaflet-control-measure .tasks,.leaflet-measure-resultpopup .tasks{margin:12px 0 0;padding:10px 0 0;border-top:solid 1px #DDD;list-style:none;list-style-image:none}.leaflet-control-measure .tasks li,.leaflet-measure-resultpopup .tasks li{display:inline;margin:0 10px 0 0}.leaflet-control-measure .tasks li:last-child,.leaflet-measure-resultpopup .tasks li:last-child{margin-right:0}.leaflet-control-measure .coorddivider,.leaflet-measure-resultpopup .coorddivider{color:#999}.leaflet-control-measure{background:#fff;border-radius:5px;box-shadow:0 1px 5px rgba(0,0,0,0.4)}.leaflet-control-measure .leaflet-control-measure-toggle,.leaflet-control-measure .leaflet-control-measure-toggle:hover{display:block;width:36px;height:36px;background-position:50% 50%;background-repeat:no-repeat;background-image:url(images/rulers.png);border-radius:5px;text-indent:100%;white-space:nowrap;overflow:hidden}.leaflet-retina .leaflet-control-measure .leaflet-control-measure-toggle,.leaflet-retina .leaflet-control-measure .leaflet-control-measure-toggle:hover{background-image:url(images/rulers_@2X.png);background-size:16px 16px}.leaflet-touch .leaflet-control-measure .leaflet-control-measure-toggle,.leaflet-touch .leaflet-control-measure .leaflet-control-measure-toggle:hover{width:44px;height:44px}.leaflet-control-measure .startprompt h3{margin-bottom:10px}.leaflet-control-measure .startprompt .tasks{margin-top:0;padding-top:0;border-top:0}.leaflet-control-measure .leaflet-control-measure-interaction{padding:10px 12px}.leaflet-control-measure .results .group{margin-top:10px;padding-top:10px;border-top:dotted 1px #eaeaea}.leaflet-control-measure .results .group:first-child{margin-top:0;padding-top:0;border-top:0}.leaflet-control-measure .results .heading{margin-right:5px;color:#999}.leaflet-control-measure a.start{padding-left:18px;background-repeat:no-repeat;background-position:0% 50%;background-image:url(images/start.png)}.leaflet-retina .leaflet-control-measure a.start{background-image:url(images/start_@2X.png);background-size:12px 12px}.leaflet-control-measure a.cancel{padding-left:18px;background-repeat:no-repeat;background-position:0% 50%;background-image:url(images/cancel.png)}.leaflet-retina .leaflet-control-measure a.cancel{background-image:url(images/cancel_@2X.png);background-size:12px 12px}.leaflet-control-measure a.finish{padding-left:18px;background-repeat:no-repeat;background-position:0% 50%;background-image:url(images/check.png)}.leaflet-retina .leaflet-control-measure a.finish{background-image:url(images/check_@2X.png);background-size:12px 12px}.leaflet-measure-resultpopup a.zoomto{padding-left:18px;background-repeat:no-repeat;background-position:0% 50%;background-image:url(images/focus.png)}.leaflet-retina .leaflet-measure-resultpopup a.zoomto{background-image:url(images/focus_@2X.png);background-size:12px 12px}.leaflet-measure-resultpopup a.deletemarkup{padding-left:18px;background-repeat:no-repeat;background-position:0% 50%;background-image:url(images/trash.png)}.leaflet-retina .leaflet-measure-resultpopup a.deletemarkup{background-image:url(images/trash_@2X.png);background-size:11px 12px}
|
|
@ -0,0 +1,11 @@
|
|||
PluginsAPI.Map.willAddControls([
|
||||
'measure/leaflet-measure.css',
|
||||
'measure/leaflet-measure.min.js'
|
||||
], function(options){
|
||||
L.control.measure({
|
||||
primaryLengthUnit: 'meters',
|
||||
secondaryLengthUnit: 'feet',
|
||||
primaryAreaUnit: 'sqmeters',
|
||||
secondaryAreaUnit: 'acres'
|
||||
}).addTo(options.map);
|
||||
});
|
|
@ -1,3 +1,6 @@
|
|||
PluginsAPI.Map.AddPanel(function(params){
|
||||
console.log("GOT: ", params.map);
|
||||
PluginsAPI.Map.willAddControls(function(options){
|
||||
console.log("GOT: ", options);
|
||||
});
|
||||
PluginsAPI.Map.didAddControls(function(options){
|
||||
console.log("GOT2: ", options);
|
||||
});
|
|
@ -72,6 +72,7 @@ module.exports = {
|
|||
externals: {
|
||||
// require("jquery") is external and available
|
||||
// on the global let jQuery
|
||||
"jquery": "jQuery"
|
||||
"jquery": "jQuery",
|
||||
"SystemJS": "SystemJS"
|
||||
}
|
||||
}
|