4537 lines
98 KiB
JavaScript
4537 lines
98 KiB
JavaScript
class _TypeIcons {
|
|
constructor(){
|
|
this.kv = {}
|
|
}
|
|
get(n, defaultvalue){
|
|
return this.kv.get(n, defaultvalue);
|
|
}
|
|
register(n, icon){
|
|
this.kv[n] = icon;
|
|
}
|
|
}
|
|
|
|
TypeIcons = new _TypeIcons();
|
|
|
|
/**
|
|
* Current Script Path
|
|
*
|
|
* Get the dir path to the currently executing script file
|
|
* which is always the last one in the scripts array with
|
|
* an [src] attr
|
|
*/
|
|
var currentScriptPath = function () {
|
|
var currentScript;
|
|
if (document.currentScript){
|
|
currentScript = document.currentScript.src;
|
|
} else {
|
|
console.log('has not currentScriot');
|
|
var scripts = document.querySelectorAll( 'script[src]' );
|
|
if (scripts.length < 1){
|
|
return null;
|
|
}
|
|
currentScript = scripts[ scripts.length - 1 ].src;
|
|
}
|
|
var currentScriptChunks = currentScript.split( '/' );
|
|
var currentScriptFile = currentScriptChunks[ currentScriptChunks.length - 1 ];
|
|
return currentScript.replace( currentScriptFile, '' );
|
|
}
|
|
|
|
var bricks_path = currentScriptPath();
|
|
|
|
var bricks_resource = function(name){
|
|
return bricks_path + name;
|
|
}
|
|
|
|
/**
|
|
* Finds all elements in the entire page matching `selector`, even if they are in shadowRoots.
|
|
* Just like `querySelectorAll`, but automatically expand on all child `shadowRoot` elements.
|
|
* @see https://stackoverflow.com/a/71692555/2228771
|
|
*/
|
|
function querySelectorAllShadows(selector, el = document.body) {
|
|
// recurse on childShadows
|
|
const childShadows = Array.from(el.querySelectorAll('*')).
|
|
map(el => el.shadowRoot).filter(Boolean);
|
|
|
|
console.log('[querySelectorAllShadows]', selector, el, `(${childShadows.length} shadowRoots)`);
|
|
|
|
const childResults = childShadows.map(child => querySelectorAllShadows(selector, child));
|
|
|
|
// fuse all results into singular, flat array
|
|
const result = Array.from(el.querySelectorAll(selector));
|
|
return result.concat(childResults).flat();
|
|
}
|
|
|
|
var schedule_once = function(f, t){
|
|
/* f: function
|
|
t:time in second unit
|
|
*/
|
|
t = t * 1000
|
|
window.setTimeout(f, t);
|
|
}
|
|
|
|
var schedule_interval = function(f, t){
|
|
var mf = function(func, t){
|
|
func();
|
|
schedule_once(f, t);
|
|
}
|
|
schedule_once(mf.bind(f,t), t);
|
|
}
|
|
|
|
var debug = function(){
|
|
console.log(...arguments);
|
|
}
|
|
|
|
var import_cache = new Map()
|
|
|
|
var import_css = async function(url){
|
|
if (import_cache.get(url)===1) return;
|
|
var result = await tget(url);
|
|
debug('import_css():tget() return', result);
|
|
var s = document.createElement('style');
|
|
s.setAttribute('type', 'text/javascript');
|
|
s.innerHTML = result;
|
|
document.getElementsByTagName("head")[0].appendChild(s);
|
|
import_cache.set(url, 1);
|
|
}
|
|
|
|
var import_js = async function(url){
|
|
if (import_cache.get(url)===1) return;
|
|
// var result = await tget(url);
|
|
// debug('import_js():tget() return', url, result);
|
|
var s = document.createElement('script');
|
|
s.setAttribute('type', 'text/javascript');
|
|
s.src=url;
|
|
// s.innerHTML = result;
|
|
document.body.appendChild(s);
|
|
import_cache.set(url, 1);
|
|
|
|
}
|
|
|
|
var extend = function(d, s){
|
|
for (var p in s){
|
|
if (! s.hasOwnProperty(p)){
|
|
continue;
|
|
}
|
|
if (d[p] && (typeof(d[p]) == 'object')
|
|
&& (d[p].toString() == '[object Object]') && s[p]){
|
|
extend(d[p], s[p]);
|
|
} else {
|
|
d[p] = s[p];
|
|
}
|
|
}
|
|
return d;
|
|
}
|
|
|
|
var objget = function(obj, key, defval){
|
|
if (obj.hasOwnProperty(key)){
|
|
return obj[key];
|
|
}
|
|
return defval;
|
|
}
|
|
|
|
var obj_fmtstr = function(obj, fmt){
|
|
/* fmt like
|
|
'my name is ${name}, ${age=}'
|
|
'${name:}, ${age=}'
|
|
*/
|
|
var s = fmt;
|
|
s = s.replace(/\${(\w+)([:=]*)}/g, (k, key, op) => {
|
|
if (obj.hasOwnProperty(key)){
|
|
if (op == ''){
|
|
return obj[key];
|
|
} else {
|
|
return key + op + obj[key];
|
|
}
|
|
}
|
|
return ''
|
|
})
|
|
return s;
|
|
}
|
|
|
|
Object.prototype.copy = function(){
|
|
var o = {}
|
|
for ( k in this){
|
|
if (this.hasOwnProperty(k)){
|
|
o[k] = this[k];
|
|
}
|
|
}
|
|
return o;
|
|
}
|
|
Object.prototype.get = function(name, defvalue){
|
|
return objget(this, name, defvalue);
|
|
}
|
|
|
|
Object.prototype.fmtstr = function(fmt){
|
|
return obj_fmtstr(this, fmt);
|
|
}
|
|
|
|
Object.prototype.update = function(obj){
|
|
if (obj){
|
|
extend(this, obj);
|
|
}
|
|
}
|
|
|
|
Object.prototype.updates = function(){
|
|
for (var i=0; i<arguments.length; i++){
|
|
extend(this, arguments[i]);
|
|
}
|
|
}
|
|
|
|
var archorize = function(ele,archor){
|
|
/* archor maybe one of the:
|
|
"tl", "tc", "tr",
|
|
"cl", "cc", "cr",
|
|
"bl", "bc", "br"
|
|
*/
|
|
if (! archor)
|
|
archor = 'cc';
|
|
var v = archor[0];
|
|
var h = archor[1];
|
|
var y = "0%";
|
|
switch(v){
|
|
case 't':
|
|
y = "0%";
|
|
break;
|
|
case 'b':
|
|
y = '100%';
|
|
break;
|
|
case 'c':
|
|
y = '50%';
|
|
break;
|
|
default:
|
|
y = '50%';
|
|
break;
|
|
}
|
|
var x = "0%";
|
|
switch(h){
|
|
case 'l':
|
|
x = "0%";
|
|
break;
|
|
case 'r':
|
|
x = '100%';
|
|
break;
|
|
case 'c':
|
|
x = '50%';
|
|
break;
|
|
default:
|
|
x = '50%';
|
|
break;
|
|
}
|
|
ele.style.top = y;
|
|
ele.style.left = x;
|
|
var o = {
|
|
'x':x,
|
|
'y':y
|
|
}
|
|
var tsf = o.fmtstr('translateY(-${y}) translateX(-${x})');
|
|
console.log('archorize(): tsf=', tsf);
|
|
ele.style.transform = tsf;
|
|
ele.style.position = "absolute";
|
|
}
|
|
|
|
Array.prototype.insert = function ( index, ...items ) {
|
|
this.splice( index, 0, ...items );
|
|
};
|
|
|
|
Array.prototype.remove = function(item){
|
|
var idx = this.indexOf(item);
|
|
if (idx >= 0){
|
|
this.splice(idx, 1);
|
|
}
|
|
return this;
|
|
}
|
|
|
|
var absurl = function(url, widget){
|
|
if (url.startsWith('http://') || url.startsWith('https://')){
|
|
return url;
|
|
}
|
|
var base_uri = widget.baseURI;
|
|
if (url.startsWith('/')){
|
|
base_uri = Body.baseURI;
|
|
url = url.substring(1);
|
|
}
|
|
paths = base_uri.split('/');
|
|
delete paths[paths.length - 1];
|
|
var ret_url = paths.join('/') + url;
|
|
return ret_url;
|
|
}
|
|
|
|
var debug = function(...args){
|
|
console.log(...args);
|
|
}
|
|
|
|
var convert2int = function(s){
|
|
if (typeof(s) == 'number') return s;
|
|
var s1 = s.match(/\d+/);
|
|
return parseInt(s1[0]);
|
|
}
|
|
|
|
function setCookie(name,value,days) {
|
|
var expires = "";
|
|
if (days) {
|
|
var date = new Date();
|
|
date.setTime(date.getTime() + (days*24*60*60*1000));
|
|
expires = "; expires=" + date.toUTCString();
|
|
}
|
|
document.cookie = name + "=" + (value || "") + expires + "; path=/";
|
|
}
|
|
function getCookie(name) {
|
|
var nameEQ = name + "=";
|
|
var ca = document.cookie.split(';');
|
|
for(var i=0;i < ca.length;i++) {
|
|
var c = ca[i];
|
|
while (c.charAt(0)==' ') c = c.substring(1,c.length);
|
|
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
|
|
}
|
|
return null;
|
|
}
|
|
function eraseCookie(name) {
|
|
document.cookie = name +'=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT;';
|
|
}
|
|
|
|
var set_max_height = function(w1, w2){
|
|
var v1 = w1.dom_element.offsetHeight;
|
|
var v2 = w2.dom_element.offsetHeight;
|
|
var v = v1 - v2;
|
|
if (v < 0){
|
|
w1.set_height(w2.dom_element.offsetHeight);
|
|
} else if (v > 0) {
|
|
w2.set_height(w1.dom_element.offsetHeight);
|
|
}
|
|
}
|
|
class I18n {
|
|
constructor(url, default_lang){
|
|
/*
|
|
{
|
|
url:
|
|
method:
|
|
default_lang
|
|
}
|
|
*/
|
|
this.url = opts.url;
|
|
this.default_lang = opts.default_lang||'en';
|
|
this.method = opts.method || 'GET';
|
|
this.lang_msgs = {};
|
|
this.msgs = {};
|
|
}
|
|
_(txt, obj){
|
|
if (this.msgs.hasOwnProperty(txt)){
|
|
itxt = this.msgs[txt];
|
|
}
|
|
if (obj instanceof Object){
|
|
return obj.fmtstr(itxt);
|
|
}
|
|
return txt;
|
|
}
|
|
is_loaded(lang){
|
|
if (this.lang_msgs.get(lang)) return true;
|
|
return false;
|
|
}
|
|
setup_dict(dic, lang){
|
|
this.cur_lang = lang;
|
|
this.lang_msgs.update({lang:dic});
|
|
this.msgs = dic;
|
|
}
|
|
async change_lang(lang){
|
|
if (this.lang_msgs.get(lang)){
|
|
this.msgs = this.lang_msgs.get(lang);
|
|
return;
|
|
}
|
|
|
|
if (! this.url) return;
|
|
|
|
let params = {'lang':lang};
|
|
d = await jcall(desc.url, {
|
|
"method":this.method || 'GET',
|
|
params:params
|
|
});
|
|
this.setup_dict(d, lang);
|
|
}
|
|
}
|
|
|
|
class Factory_ {
|
|
constructor(){
|
|
this.widgets_kv = new Object();
|
|
this.widgets_kv['_t_'] = 1;
|
|
}
|
|
register(name, widget){
|
|
this.widgets_kv[name] = widget;
|
|
}
|
|
get(name){
|
|
if (this.widgets_kv.hasOwnProperty(name)){
|
|
return this.widgets_kv[name];
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
const Factory = new Factory_();
|
|
|
|
|
|
class JsWidget {
|
|
dom_element = null;
|
|
constructor(options){
|
|
if (!options){
|
|
options = {}
|
|
}
|
|
this.baseURI = options.baseURI;
|
|
this.opts = options;
|
|
this.create();
|
|
this.dom_element.bricks_widget = this;
|
|
this.opts_set_style();
|
|
if (this.opts.tooltip){
|
|
this.dom_element.tooltip = this.opts.tooltip;
|
|
}
|
|
this._container = false;
|
|
this.parent = null;
|
|
this.sizable_elements = [];
|
|
if (options.css){
|
|
this.set_css(options.css);
|
|
}
|
|
if (options.csses){
|
|
this.set_csses(options.csses);
|
|
}
|
|
}
|
|
create(){
|
|
this.dom_element = document.createElement('div');
|
|
}
|
|
opts_set_style(){
|
|
var keys = [
|
|
"width",
|
|
"x",
|
|
"y",
|
|
"height",
|
|
"margin",
|
|
"padding",
|
|
"align",
|
|
"textAlign",
|
|
"overflowY",
|
|
"overflowX",
|
|
"overflow",
|
|
"color"
|
|
]
|
|
var mapping_keys = {
|
|
"z-index":"zIndex",
|
|
"overflow-x":"overflowX",
|
|
"opveflow-y":"overflowY",
|
|
"bgcolor":"backgroundColor"
|
|
};
|
|
var mkeys = Object.keys(mapping_keys);
|
|
var style = {};
|
|
var okeys = Object.keys(this.opts);
|
|
for (var k=0; k<okeys.length; k++){
|
|
if (keys.find( i => i ==okeys[k])){
|
|
style[okeys[k]] = this.opts[okeys[k]];
|
|
}
|
|
if (mkeys.find( i => i ==okeys[k])){
|
|
var mk = mapping_keys[okeys[k]];
|
|
style[mk] = this.opts[okeys[k]];
|
|
}
|
|
this[okeys[k]] = this.opts[okeys[k]];
|
|
}
|
|
this.dom_element.style.update(style);
|
|
if (this.opts.css){
|
|
this.set_css(this.opts.css);
|
|
}
|
|
|
|
}
|
|
sizable(){
|
|
bricks_app.text_ref(this);
|
|
}
|
|
change_fontsize(ts){
|
|
ts = convert2int(ts);
|
|
if (! this.specified_fontsize){
|
|
var rate = this.rate || 1;
|
|
ts = ts * rate;
|
|
ts = ts + 'px';
|
|
this.dom_element.style.fontSize = ts;
|
|
for(var i=0;i<this.sizable_elements.length;i++){
|
|
this.sizable_elements[i].style.fontSize = ts;
|
|
}
|
|
}
|
|
}
|
|
set_fontsize(){
|
|
var fontsize;
|
|
if (this.opts.fontsize){
|
|
this.specified_fontsize = true;
|
|
fontsize = this.opts.fontsize;
|
|
} else {
|
|
fontsize = bricks_app.get_textsize(this.ctype);
|
|
}
|
|
fontsize = convert2int(fontsize);
|
|
var rate = this.rate || 1;
|
|
fontsize = rate * fontsize;
|
|
fontsize = fontsize + 'px';
|
|
this.dom_element.style.fontSize = fontsize;
|
|
for(var i=0;i<this.sizable_elements.length;i++){
|
|
this.sizable_elements[i].style.fontSize = fontsize;
|
|
}
|
|
}
|
|
h_center(){
|
|
this.dom_element.style.marginLeft = 'auto';
|
|
this.dom_element.style.marginRight = 'auto';
|
|
}
|
|
h_left(){
|
|
this.dom_element.style.marginLeft = '0px';
|
|
this.dom_element.style.marginRight = 'auto';
|
|
}
|
|
h_right(){
|
|
this.dom_element.style.marginLeft = 'auto';
|
|
this.dom_element.style.marginRight = '0px';
|
|
}
|
|
ht_center(){
|
|
this.dom_element.style.textAlign = 'center';
|
|
}
|
|
ht_left(){
|
|
this.dom_element.style.textAlign = 'left';
|
|
}
|
|
ht_right(){
|
|
this.dom_element.style.textAlign = 'right';
|
|
}
|
|
set_width(width){
|
|
if (typeof(width) == 'number'){
|
|
width = width + 'px';
|
|
}
|
|
this.dom_element.style.width = width;
|
|
}
|
|
set_height(height){
|
|
if (typeof(height) == 'number'){
|
|
height = height + 'px';
|
|
}
|
|
this.dom_element.style.height = height;
|
|
}
|
|
set_style(k, v){
|
|
this.dom_element.style[k] = v;
|
|
}
|
|
set_csses(csses, remove_flg){
|
|
var arr = csses.split(' ');
|
|
arr.forEach(c =>{
|
|
this.set_css(c, remove_flg);
|
|
})
|
|
}
|
|
set_css(css, remove_flg){
|
|
if (!remove_flg){
|
|
this.dom_element.classList.add(css);
|
|
} else {
|
|
this.dom_element.classList.remove(css);
|
|
}
|
|
}
|
|
set_cssObject(cssobj){
|
|
this.dom_element.style.update(cssobj);
|
|
}
|
|
is_container(){
|
|
return this._container;
|
|
}
|
|
_create(tagname){
|
|
return document.createElement(tagname);
|
|
}
|
|
set_id(id){
|
|
this.dom_element.id = id;
|
|
}
|
|
set_baseURI(url){
|
|
this.baseURI = url;
|
|
}
|
|
absurl(url){
|
|
console.log('this.baseURI=', this.baseURI);
|
|
if (this.baseURI){
|
|
return absurl(url, this);
|
|
}
|
|
return url
|
|
}
|
|
show(){
|
|
this.dom_element.style.display = '';
|
|
}
|
|
hide(){
|
|
this.dom_element.style.display = 'none'
|
|
}
|
|
bind(eventname, handler){
|
|
this.dom_element.addEventListener(eventname, handler);
|
|
}
|
|
unbind(eventname, handler){
|
|
this.dom_element.removeEventListener(eventname, handler);
|
|
}
|
|
dispatch(eventname, params){
|
|
var e = new Event(eventname);
|
|
e.params = params;
|
|
this.dom_element.dispatchEvent(e);
|
|
}
|
|
}
|
|
|
|
|
|
class TextBase extends JsWidget {
|
|
/* {
|
|
otext:
|
|
i18n:
|
|
rate:
|
|
halign:
|
|
valign:
|
|
color:
|
|
bgtcolor:
|
|
css
|
|
}
|
|
*/
|
|
constructor(options){
|
|
super(options);
|
|
this.opts = options;
|
|
this.rate = this.opts.rate || 1;
|
|
this.specified_fontsize = false;
|
|
this.set_attrs();
|
|
this.dom_element.style.fontWeight = 'normal';
|
|
this.sizable();
|
|
}
|
|
set_attrs(){
|
|
if (this.opts.hasOwnProperty('text')){
|
|
this.text = this.opts.text;
|
|
}
|
|
if (this.opts.hasOwnProperty('otext')){
|
|
this.otext = this.opts.otext;
|
|
}
|
|
if (this.opts.hasOwnProperty('i18n')){
|
|
this.i18n = this.opts.i18n;
|
|
}
|
|
this._i18n = new I18n();
|
|
if (this.i18n && this.otext) {
|
|
this.text = this._i18n._(this.otext);
|
|
}
|
|
this.dom_element.innerHTML = this.text;
|
|
}
|
|
set_i18n_text(){
|
|
if ( !this.otext){
|
|
return;
|
|
}
|
|
if (! this.i18n){
|
|
return;
|
|
}
|
|
this.text = this._i18n._(this.otext);
|
|
this.dom_element.innerHTML = this.text;
|
|
}
|
|
|
|
}
|
|
|
|
class Text extends TextBase {
|
|
constructor(opts){
|
|
super(opts);
|
|
this.ctype = 'text';
|
|
this.set_fontsize();
|
|
}
|
|
}
|
|
|
|
class Title1 extends TextBase {
|
|
constructor(options){
|
|
super(options);
|
|
this.ctype = 'title1';
|
|
this.set_fontsize();
|
|
this.dom_element.style.fontWeight = 'bold';
|
|
}
|
|
}
|
|
|
|
class Title2 extends TextBase {
|
|
constructor(options){
|
|
super(options);
|
|
this.ctype = 'title2';
|
|
this.set_fontsize();
|
|
this.dom_element.style.fontWeight = 'bold';
|
|
}
|
|
}
|
|
|
|
class Title3 extends TextBase {
|
|
constructor(options){
|
|
super(options);
|
|
this.ctype = 'title3';
|
|
this.set_fontsize();
|
|
this.dom_element.style.fontWeight = 'bold';
|
|
}
|
|
}
|
|
|
|
class Title4 extends TextBase {
|
|
constructor(options){
|
|
super(options);
|
|
this.ctype = 'title4';
|
|
this.set_fontsize();
|
|
this.dom_element.style.fontWeight = 'bold';
|
|
}
|
|
}
|
|
|
|
class Title5 extends TextBase {
|
|
constructor(options){
|
|
super(options);
|
|
this.ctype = 'title5';
|
|
this.set_fontsize();
|
|
this.dom_element.style.fontWeight = 'bold';
|
|
}
|
|
}
|
|
|
|
class Title6 extends TextBase {
|
|
constructor(options){
|
|
super(options);
|
|
this.ctype = 'title6';
|
|
this.set_fontsize();
|
|
this.dom_element.style.fontWeight = 'bold';
|
|
}
|
|
}
|
|
|
|
Factory.register('Text', Text);
|
|
Factory.register('Title1', Title1);
|
|
Factory.register('Title2', Title2);
|
|
Factory.register('Title3', Title3);
|
|
Factory.register('Title4', Title4);
|
|
Factory.register('Title5', Title5);
|
|
Factory.register('Title6', Title6);
|
|
|
|
var tooltip = null;
|
|
|
|
createTooltip = function(){
|
|
tooltip = document.createElement('div');
|
|
tooltip.className = 'tooltip';
|
|
tooltip.style.left = '50%';
|
|
tooltip.style.trabsform = 'translateX(-50%)';
|
|
var mouseoutHandler = (event) => {
|
|
event.target.removeChild(tooltip);
|
|
}
|
|
window.addEventListener('mouseover', event => {
|
|
if (!event.target.tooltop) return true;
|
|
tooltip.textContent = event.target.tooltip;
|
|
event.target.addEventListener(
|
|
'mouseout',
|
|
mouseoutHandler,
|
|
{once:true}
|
|
);
|
|
});
|
|
}
|
|
|
|
let bricks_app = null;
|
|
/*
|
|
all type of bind action's desc has the following attributes:
|
|
actiontype:'bricks',
|
|
wid:
|
|
event:
|
|
target:
|
|
datawidget:
|
|
datascript:
|
|
datamethod:
|
|
datakwargs:
|
|
rtdata:
|
|
conform:
|
|
and each type of binds specified attributes list following
|
|
|
|
urlwidget action:
|
|
mode:,
|
|
options:{
|
|
method:
|
|
params:{},
|
|
url:
|
|
}
|
|
|
|
bricks action:
|
|
mode:,
|
|
options:{
|
|
"widgettype":"gg",
|
|
...
|
|
}
|
|
|
|
method action:
|
|
method:
|
|
params: for methods kwargs
|
|
|
|
|
|
script action:
|
|
script:
|
|
params:
|
|
|
|
registerfunction action:
|
|
rfname:
|
|
params:
|
|
|
|
event action:
|
|
dispatch_event:
|
|
params:
|
|
*/
|
|
|
|
var widgetBuild = async function(desc, widget){
|
|
if (! widget){
|
|
widget = Body;
|
|
}
|
|
const klassname = desc.widgettype;
|
|
var base_url = null;
|
|
if (klassname == 'urlwidget'){
|
|
let url = absurl(desc.options.url, widget);
|
|
base_url = url;
|
|
let method = desc.options.method || 'GET';
|
|
let opts = desc.options.params || {};
|
|
desc = await jcall(url, { "method":method, "params":opts});
|
|
} else {
|
|
base_url = widget.baseURI;
|
|
}
|
|
let klass = Factory.get(desc.widgettype);
|
|
if (! klass){
|
|
console.log('widgetBuild():',desc.widgettype, 'not registered', Factory.widgets_kw);
|
|
return null;
|
|
}
|
|
desc.options.baseURI = base_url;
|
|
let w = new klass(desc.options);
|
|
if (desc.hasOwnProperty('id')){
|
|
w.set_id(desc.id);
|
|
}
|
|
if (desc.hasOwnProperty('subwidgets')){
|
|
for (let i=0; i<desc.subwidgets.length; i++){
|
|
let sdesc = desc.subwidgets[i];
|
|
let sw = await widgetBuild(sdesc, w);
|
|
if ( sw ){
|
|
w.add_widget(sw);
|
|
} else {
|
|
console.log('widgetBuild() error: sdesc=', sdesc);
|
|
}
|
|
}
|
|
}
|
|
if (desc.hasOwnProperty('binds')){
|
|
for (var i=0;i<desc.binds.length; i++){
|
|
buildBind(w, desc.binds[i]);
|
|
}
|
|
}
|
|
return w;
|
|
}
|
|
|
|
var buildBind = function(w, desc){
|
|
var widget = getWidgetById(desc.wid, w);
|
|
if (!widget){
|
|
cnsole.log('desc wid not find', desc);
|
|
return;
|
|
}
|
|
var event = desc.event;
|
|
buildEventBind(w, widget, event, desc);
|
|
}
|
|
|
|
var buildEventBind = function(from_widget, widget, event, desc){
|
|
var handler = universal_handler.bind(null,from_widget, widget, desc);
|
|
if (desc.conform){
|
|
var conform_widget = widgetBuild(desc.conform, widget);
|
|
conform_widget.bind('on_conform', handler);
|
|
handler = conform_widget.open.bind(conform_widget);
|
|
}
|
|
widget.bind(event, handler);
|
|
|
|
}
|
|
|
|
var universal_handler = function(from_widget, widget, desc, event){
|
|
debug('universal_handler() info', 'from_widget=',
|
|
from_widget,
|
|
'widget=', widget,
|
|
'desc=', desc,
|
|
event);
|
|
var f = buildEventHandler(from_widget, desc);
|
|
if (f){
|
|
return f(event);
|
|
}
|
|
debug('universal_handler() error', 'from_widget=',
|
|
from_widget,
|
|
'widget=', widget,
|
|
'desc=', desc,
|
|
evnet);
|
|
}
|
|
var buildEventHandler = function(w, desc){
|
|
var target = getWidgetById(desc.target, w);
|
|
if (! target){
|
|
console.log('target miss desc=', desc, 'w=', w);
|
|
return null
|
|
}
|
|
var rtdata = null;
|
|
if (desc.rtdata) rtdata = desc.rtdata;
|
|
else if (desc.datawidget){
|
|
var data_desc = {
|
|
widget:desc.datawidget,
|
|
method:desc.datamethod,
|
|
params:desc.dataparams,
|
|
script:desc.datascript
|
|
}
|
|
rtdata = getRealtimeData(w, data_desc);
|
|
}
|
|
switch (desc.actiontype){
|
|
case 'urlwidget':
|
|
return buildUrlwidgetHandler(w, target, rtdata, desc);
|
|
break;
|
|
case 'bricks':
|
|
return buildBricksHandler(w, target, rtdata, desc);
|
|
break;
|
|
case 'registerfunction':
|
|
return buildRegisterFunction(w, target, rtdata, desc);
|
|
break;
|
|
case 'method':
|
|
return buildMethodHandler(w, target, rtdata, desc);
|
|
break;
|
|
case 'script':
|
|
var f = buildScriptHandler(w, target, rtdata, desc);
|
|
return f;
|
|
break;
|
|
case 'event':
|
|
return buildDispatchEventHandler(w, target, rtdata, desc);
|
|
break;
|
|
default:
|
|
console.log('invalid actiontype', target, desc);
|
|
break;
|
|
}
|
|
}
|
|
var getRealtimeData = function(w, desc){
|
|
var target = getWidgetById(desc.widget, w);
|
|
var f;
|
|
if (! target){
|
|
console.log('target miss', desc);
|
|
return null
|
|
}
|
|
if (desc.method){
|
|
f = buildMethodHandler(null, target, null, desc)
|
|
return f();
|
|
}
|
|
if (desc.script){
|
|
f = buildScriptHandler(null, target, null, desc)
|
|
return f();
|
|
}
|
|
debug('getRealtimeData():desc=', desc, 'f=', f);
|
|
return null;
|
|
}
|
|
|
|
var buildUrlwidgetHandler = function(w, target, rtdata, desc){
|
|
var f = async function(target, mode, options){
|
|
console.log('target=', target, 'mode=', mode, 'options=', options);
|
|
var w = await widgetBuild(options, w);
|
|
if (!w){
|
|
console.log('options=', options, 'widgetBuild() failed');
|
|
return;
|
|
}
|
|
if (mode == 'replace'){
|
|
target.clear_widgets();
|
|
}
|
|
target.add_widget(w);
|
|
}
|
|
var options = desc.options.copy();
|
|
options.params.update(rtdata);
|
|
var opts = {
|
|
"widgettype":"urlwidget",
|
|
"options":options
|
|
}
|
|
return f.bind(target, target, desc.mode || 'replace', opts);
|
|
}
|
|
var buildBricksHandler = function(w, target, rtdata, desc){
|
|
var f = async function(target, mode, options){
|
|
console.log('target=', target, 'mode=', mode, 'options=', options);
|
|
var w = await widgetBuild(options, w);
|
|
if (!w){
|
|
console.log('options=', options, 'widgetBuild() failed');
|
|
return;
|
|
}
|
|
if (mode == 'replace'){
|
|
target.clear_widgets();
|
|
}
|
|
target.add_widget(w);
|
|
}
|
|
var options = desc.options.copy();
|
|
options.options.update(rtdata);
|
|
return f.bind(target, target, desc.mode || 'replace', options);
|
|
}
|
|
var buildRegisterFunctionHandler = function(w, target, rtdata, desc){
|
|
var f = registerfunctions.get(desc.rfname);
|
|
if( ! f){
|
|
console.log('rfname:', desc.rfname, 'not registed', desc);
|
|
return null;
|
|
}
|
|
var params = {};
|
|
if (desc.params){
|
|
params.update(desc.params);
|
|
}
|
|
if (rtdata){
|
|
params.update(rtdata);
|
|
}
|
|
return f.bind(target, params);
|
|
}
|
|
var buildMethodHandler = function(w, target, rtdata, desc){
|
|
var f = target[desc.method];
|
|
if (! f){
|
|
console.log('method:', desc.method, 'not exists in', target, 'w=', w);
|
|
return null;
|
|
}
|
|
var params = {};
|
|
params.updates(desc.params, rtdata);
|
|
return f.bind(target, params);
|
|
}
|
|
var buildScriptHandler = function(w, target, rtdata, desc){
|
|
var params = {};
|
|
params.updates(desc.params, rtdata);
|
|
var f = new Function('target', 'params', 'event', desc.script);
|
|
// console.log('params=', params, 'buildScriptHandler() ..........');
|
|
return f.bind(target, target, params);
|
|
}
|
|
var buildDispatchEventHandler = function(w, target, rtdata, desc){
|
|
var params = {};
|
|
params.updates(desc.params, rtdata);
|
|
var f = function(target, event_name, params){
|
|
target.dispatch(event_name, params);
|
|
}
|
|
return f.bind(target, params);
|
|
}
|
|
|
|
var getWidgetById = function(id, from_widget){
|
|
if (! id){
|
|
return from_widget;
|
|
}
|
|
if (typeof(id) != 'string') return id;
|
|
var ids = id.split('/');
|
|
var el = from_widget.dom_element;
|
|
var new_el = null;
|
|
var j = 0;
|
|
for (var i=0; i< ids.length; i++){
|
|
if (i == 0){
|
|
if (ids[i] == 'self'){
|
|
el = from_widget.dom_element;
|
|
continue;
|
|
}
|
|
if (ids[i]=='root'){
|
|
el = bricks_app.root.dom_element;
|
|
continue;
|
|
}
|
|
if (ids[i]=='app'){
|
|
return bricks_app;
|
|
}
|
|
if (ids[i] == 'window'){
|
|
el = Body.dom_element;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
try {
|
|
if (ids[i][0] == '-'){
|
|
var wid = substr(1, ids[i].length - 1)
|
|
new_el = el.closest('#' + wid);
|
|
} else {
|
|
new_el = el.querySelector('#' + ids[i]);
|
|
}
|
|
}
|
|
catch(err){
|
|
console.log('getWidgetById():i=', ids[i], id, 'not found', err);
|
|
return null;
|
|
}
|
|
if ( new_el == null ){
|
|
console.log('getWidgetById():', id, from_widget, 'el=', el, 'id=', ids[i]);
|
|
return null;
|
|
}
|
|
el = new_el;
|
|
}
|
|
if (typeof(el.bricks_widget) !== 'undefined'){
|
|
console.log('getWidgetById():', id, from_widget, el, 'widget');
|
|
return el.bricks_widget;
|
|
}
|
|
return el;
|
|
}
|
|
|
|
class BricksApp {
|
|
constructor(opts){
|
|
/*
|
|
opts = {
|
|
login_url:
|
|
"charsize:
|
|
"language":
|
|
"i18n":{
|
|
"url":'rrr',
|
|
"default_lang":'en'
|
|
},
|
|
"widget":{
|
|
"widgettype":"Text",
|
|
"options":{
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
this.opts = opts;
|
|
bricks_app = this;
|
|
this.login_url = opts.login_url;
|
|
this.charsize = this.opts.charsize || 20;
|
|
if (this.opts.language){
|
|
this.lang = this.opts.language;
|
|
}
|
|
else {
|
|
this.lang = navigator.language;
|
|
}
|
|
this.textList = [];
|
|
this.i18n = new I18n(opts.get('i18n', {}));
|
|
this.session_id = null;
|
|
createTooltip();
|
|
}
|
|
save_session(session){
|
|
this.session_id = session;
|
|
}
|
|
get_session(){
|
|
return this.session_id;
|
|
}
|
|
get_textsize(ctype){
|
|
var tsize = this.charsize;
|
|
var stimes = {
|
|
"text":1.0,
|
|
"title1":1.96,
|
|
"title2":1.80,
|
|
"title3":1.64,
|
|
"title4":1.48,
|
|
"title5":1.32,
|
|
"title6":1.16
|
|
}
|
|
tsize = parseInt(stimes[ctype] * tsize);
|
|
return tsize + 'px';
|
|
}
|
|
text_ref(textWidget){
|
|
this.textList.push(new WeakRef(textWidget));
|
|
}
|
|
text_remove_dead(){
|
|
var tList = this.textList;
|
|
for (var i=0;i<tList.length;i++){
|
|
if (! tList[i].deref()){
|
|
this.textList.remove(tList[i]);
|
|
}
|
|
}
|
|
}
|
|
async setup_i18n(){
|
|
let params = {'lang':this.lang};
|
|
d = await jcall(desc.url, {
|
|
"method":desc.method||'GET', params:params});
|
|
this.i18n.setup_dict(d);
|
|
}
|
|
async build(){
|
|
var opts = structuredClone(this.opts.widget);
|
|
var w = await widgetBuild(opts, Body);
|
|
return w
|
|
}
|
|
async run(){
|
|
await this.change_language(this);
|
|
var w = await this.build();
|
|
this.root = w;
|
|
Body.add_widget(w);
|
|
}
|
|
textsize_bigger(){
|
|
this.charsize = this.charsize * 1.05;
|
|
this.text_resize();
|
|
}
|
|
textsize_smaller(){
|
|
this.charsize = this.charsize * 0.95;
|
|
this.text_resize();
|
|
}
|
|
text_resize = function(){
|
|
for (var i=0;i<this.textList.length;i++){
|
|
if(this.textList[i].deref()){
|
|
var w = this.textList[i].deref();
|
|
var ts = this.get_textsize(w.ctype);
|
|
w.change_fontsize(ts);
|
|
}
|
|
}
|
|
}
|
|
change_language = async function(lang){
|
|
this.lang = lang;
|
|
await this.i18n.change_lang(lang);
|
|
for (var i=0;i<this.textList.length;i++){
|
|
if(this.textList[i].deref()){
|
|
var w = this.textList[i].deref();
|
|
if (w.opts.i18n) {
|
|
w.set_i18n_text();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
class Image extends JsWidget {
|
|
/*
|
|
{
|
|
url:
|
|
height:
|
|
width:
|
|
}
|
|
*/
|
|
constructor(opts){
|
|
super(opts);
|
|
this.opts = opts;
|
|
this.options_parse();
|
|
}
|
|
create(){
|
|
this.dom_element = document.createElement('img');
|
|
}
|
|
options_parse(){
|
|
if (this.opts.hasOwnProperty('url')){
|
|
this.set_url(this.opts.url);
|
|
}
|
|
if (this.opts.hasOwnProperty('width')){
|
|
this.width = this.opts.width;
|
|
this.dom_element.style.width = this.width;
|
|
}
|
|
if (this.opts.hasOwnProperty('height')){
|
|
this.height = this.opts.height;
|
|
this.dom_element.style.height = this.height;
|
|
}
|
|
}
|
|
set_url(url){
|
|
this.url = url;
|
|
this.dom_element.src = url;
|
|
}
|
|
}
|
|
|
|
class Icon extends Image {
|
|
constructor(opts){
|
|
super(opts);
|
|
this.opts.width = bricks_app.charsize;
|
|
this.opts.height = bricks_app.charsize;
|
|
this.ctype = 'text';
|
|
this.sizable();
|
|
this.set_fontsize();
|
|
}
|
|
change_fontsize(ts){
|
|
var siz = bricks_app.charsize;
|
|
this.set_width(siz);
|
|
this.set_height(siz);
|
|
}
|
|
set_fontsize(){
|
|
var siz = bricks_app.charsize;
|
|
this.set_width(siz);
|
|
this.set_height(siz);
|
|
}
|
|
set_width(siz){
|
|
this.dom_element.width = siz;
|
|
}
|
|
set_height(siz){
|
|
this.dom_element.height = siz;
|
|
}
|
|
}
|
|
|
|
class BlankIcon extends JsWidget {
|
|
constructor(opts){
|
|
opts.width = bricks_app.charsize;
|
|
opts.height = bricks_app.charsize;
|
|
super(opts);
|
|
this.ctype = 'text';
|
|
this.sizable();
|
|
this.set_fontsize();
|
|
}
|
|
change_fontsize(ts){
|
|
var siz = bricks_app.charsize + 'px';
|
|
this.set_width(siz);
|
|
this.set_height(siz);
|
|
}
|
|
set_fontsize(){
|
|
var siz = bricks_app.charsize + 'px';
|
|
this.set_width(siz);
|
|
this.set_height(siz);
|
|
}
|
|
set_width(siz){
|
|
this.dom_element.width = siz;
|
|
this.dom_element.style.width = siz;
|
|
}
|
|
set_height(siz){
|
|
this.dom_element.height = siz;
|
|
this.dom_element.style.height = siz;
|
|
}
|
|
}
|
|
|
|
Factory.register('Image', Image);
|
|
Factory.register('Icon', Icon);
|
|
Factory.register('BlankIcon', BlankIcon);
|
|
function url_params(data) {
|
|
return Object.keys(data).map(key => `${key}=${encodeURIComponent(data[key])}`).join('&');
|
|
}
|
|
class HttpText {
|
|
constructor(headers){
|
|
/*
|
|
var _headers = {
|
|
"Accept":"text/html",
|
|
}
|
|
_headers = {
|
|
"Accept": "application/json",
|
|
};
|
|
*/
|
|
if (!headers)
|
|
headers = {};
|
|
this.headers = headers || {
|
|
"Accept":"text/html",
|
|
};
|
|
this.headers.update(headers);
|
|
this.params = {
|
|
"_webbricks_":1
|
|
}
|
|
}
|
|
url_parse(url){
|
|
var a = url.split('?');
|
|
if (a.length == 1) return url;
|
|
url = a[0];
|
|
var a = a[1].split('&');
|
|
for (var i=0;i<a.length;i++){
|
|
var b;
|
|
b = a[i].split('=');
|
|
this.params[b[0]] = b[1];
|
|
}
|
|
return url;
|
|
}
|
|
|
|
async get_result_data(resp){
|
|
return await resp.text();
|
|
}
|
|
add_own_params(params){
|
|
if (! params)
|
|
params = {};
|
|
if (params instanceof FormData){
|
|
for ( const [key, value] of Object.entries(this.params)){
|
|
params.append(key, value);
|
|
}
|
|
}
|
|
else {
|
|
params = Object.assign(this.params, params);
|
|
}
|
|
var session = bricks_app.get_session();
|
|
if (session){
|
|
params.update({session:session});
|
|
}
|
|
return params;
|
|
}
|
|
add_own_headers(headers){
|
|
if (! headers){
|
|
headers = {};
|
|
}
|
|
return Object.assign(this.headers, headers);
|
|
}
|
|
|
|
async httpcall(url, {method='GET', headers=null, params=null}={}){
|
|
url = this.url_parse(url);
|
|
var data = this.add_own_params(params);
|
|
var header = this.add_own_headers(headers);
|
|
var _params = {
|
|
"method":method,
|
|
}
|
|
// _params.headers = headers;
|
|
if (method == 'GET' || method == 'HEAD') {
|
|
let pstr = url_params(data);
|
|
url = url + '?' + pstr;
|
|
} else {
|
|
if (data instanceof FormData){
|
|
_params.body = data;
|
|
} else {
|
|
_params.body = JSON.stringify(data);
|
|
}
|
|
}
|
|
const fetchResult = await fetch(url, _params);
|
|
var result=null;
|
|
result = await this.get_result_data(fetchResult);
|
|
if (fetchResult.ok){
|
|
var ck = fetchResult.headers.get('Set-Cookie');
|
|
if (ck){
|
|
var session = ck.split(';')[0];
|
|
bricks_app.save_session(session);
|
|
}
|
|
return result;
|
|
}
|
|
if (fetchResult.status == 401 && bricks_app.login_url){
|
|
return await this.withLoginInfo(url, _params);
|
|
}
|
|
console.log('method=', method, 'url=', url, 'params=', params);
|
|
console.log('jsoncall error:');
|
|
const resp_error = {
|
|
"type":"Error",
|
|
"message":result.message || 'Something went wrong',
|
|
"data":result.data || '',
|
|
"code":result.code || ''
|
|
};
|
|
const error = new Error();
|
|
error.info = resp_error;
|
|
return error;
|
|
}
|
|
async withLoginInfo(url, params){
|
|
var get_login_info = function(e){
|
|
console.log('login info:', e.target.getValue());
|
|
return e.target.getValue();
|
|
}
|
|
var w = await widgetBuild({
|
|
"id":"login_form",
|
|
"widgettype":"urlwidget",
|
|
"options":{
|
|
"url":bricks_app.login_url
|
|
}
|
|
});
|
|
var login_info = await new Promise((resolve, reject, w) => {
|
|
w.bind('submit', (event) => {
|
|
resolve(event.target.getValue());
|
|
event.target.dismiss();
|
|
});
|
|
w.bind('discard', (event) => {
|
|
resolve(null);
|
|
event.target.dismiss()
|
|
});
|
|
});
|
|
if (login_info){
|
|
this.set_authorization_header(params, lgin_info);
|
|
const fetchResult = await fetch(url, params);
|
|
var result=null;
|
|
result = await this.get_result_data(fetchResult);
|
|
if (fetchResult.ok){
|
|
return result;
|
|
}
|
|
if (fetchResult.status == 401){
|
|
return await this.withLoginInfo(url, params);
|
|
}
|
|
}
|
|
const resp_error = {
|
|
"type":"Error",
|
|
"message":result.message || 'Something went wrong',
|
|
"data":result.data || '',
|
|
"code":result.code || ''
|
|
};
|
|
const error = new Error();
|
|
error.info = resp_error;
|
|
return error;
|
|
}
|
|
set_authorization_header(params, lgin_info){
|
|
var auth = 'password' + '::' + login_info.user + '::' + login_info.password;
|
|
var rsa = bricks_app.rsa;
|
|
var code = rsa.encrypt(auth);
|
|
self.header.authorization = btoa(code)
|
|
}
|
|
async get(url, {headers=null, params=null}={}){
|
|
return await this.httpcall(url, {
|
|
method:'GET',
|
|
headers:headers,
|
|
params:params
|
|
});
|
|
}
|
|
async post(url, {headers=null, params=null}={}){
|
|
return await this.httpcall(url, {
|
|
method:'POST',
|
|
headers:headers,
|
|
params:params
|
|
});
|
|
}
|
|
}
|
|
|
|
class HttpJson extends HttpText {
|
|
constructor(headers){
|
|
if (!headers)
|
|
headers = {};
|
|
super(headers);
|
|
this.headers = {
|
|
"Accept": "application/json",
|
|
}
|
|
this.headers.update(headers);
|
|
}
|
|
async get_result_data(resp) {
|
|
return await resp.json()
|
|
}
|
|
}
|
|
|
|
var hc = new HttpText();
|
|
var tget = hc.get.bind(hc);
|
|
var tpost = hc.post.bind(hc);
|
|
jc = new HttpJson();
|
|
var jcall = jc.httpcall.bind(jc);
|
|
var jget = jc.get.bind(jc);
|
|
var jpost = jc.post.bind(jc);
|
|
|
|
class Oper {
|
|
constructor(v){
|
|
this.value = v;
|
|
}
|
|
__plus__(a, b){
|
|
console.log(a, b);
|
|
return new Oper(a.value + b.value);
|
|
}
|
|
__add__(a, b){
|
|
console.log(a, b);
|
|
return new Oper(a.value + b.value);
|
|
}
|
|
}
|
|
class Layout extends JsWidget {
|
|
constructor(options){
|
|
super(options);
|
|
this._container = true;
|
|
this.children = [];
|
|
}
|
|
|
|
add_widget(w, index){
|
|
if (! index || index>=this.children.length){
|
|
w.parent = this;
|
|
this.children.push(w);
|
|
this.dom_element.appendChild(w.dom_element);
|
|
return
|
|
}
|
|
var pos_w = this.children[index];
|
|
this.dom_element.insertBefore(w.dom_element, pos_w.dom_element);
|
|
this.children.insert(index+1, w);
|
|
}
|
|
remove_widgets_at_begin(cnt){
|
|
return this._remove_widgets(cnt, false);
|
|
}
|
|
remove_widgets_at_end(cnt){
|
|
return this._remove_widgets(cnt, true);
|
|
}
|
|
_remove_widgets(cnt, from_end){
|
|
var children = this.children.copy();
|
|
var len = this.children.length;
|
|
for (var i=0; i<len; i++){
|
|
if (i >= cnt) break;
|
|
var k = i;
|
|
if (from_end) k = len - 1 - i;
|
|
var w = children[k]
|
|
this.children.remove(w);
|
|
this.remove_widget(w);
|
|
}
|
|
}
|
|
remove_widget(w){
|
|
delete w.parent;
|
|
this.children = this.children.filter(function(item){
|
|
return item != w;
|
|
});
|
|
|
|
this.dom_element.removeChild(w.dom_element);
|
|
}
|
|
clear_widgets(w){
|
|
for (var i=0;i<this.children.length;i++){
|
|
this.children[i].parent = null;
|
|
}
|
|
this.children = [];
|
|
this.dom_element.replaceChildren();
|
|
}
|
|
}
|
|
|
|
class _Body extends Layout {
|
|
constructor(options){
|
|
super(options);
|
|
}
|
|
create(){
|
|
this.dom_element = document.getElementsByTagName('body')[0];
|
|
this.set_baseURI(this.dom_element.baseURI);
|
|
}
|
|
}
|
|
|
|
Body = new _Body();
|
|
|
|
class VBox extends Layout {
|
|
constructor(options){
|
|
super(options);
|
|
this.set_css('vbox');
|
|
}
|
|
}
|
|
|
|
class VFiller extends Layout {
|
|
constructor(options){
|
|
super(options);
|
|
this.set_css('vfiller');
|
|
}
|
|
}
|
|
|
|
class HBox extends Layout {
|
|
constructor(options){
|
|
super(options);
|
|
this.set_css('hbox');
|
|
}
|
|
}
|
|
|
|
class HFiller extends Layout {
|
|
constructor(options){
|
|
super(options);
|
|
this.set_css('hfiller');
|
|
}
|
|
}
|
|
|
|
Factory.register('HBox', HBox);
|
|
Factory.register('VBox', VBox);
|
|
Factory.register('HFiller', HFiller);
|
|
Factory.register('VFiller', VFiller);
|
|
|
|
/*
|
|
*/
|
|
class Menu extends VBox {
|
|
/*
|
|
{
|
|
"items":
|
|
}
|
|
*/
|
|
constructor(options){
|
|
super(options);
|
|
this.dom_element.style.display = "";
|
|
this.dom_element.style.position = "absolute";
|
|
this.dom_element.style.backgroundColor = options.bgcolor || "white";
|
|
this.dom_element.style.zIndex = "1000";
|
|
this.create_children(this.dom_element, this.opts.items);
|
|
this.bind('click', this.menu_clicked);
|
|
}
|
|
create_submenu_container(){
|
|
let cp = document.createElement('div');
|
|
cp.style.marginLeft = "15px";
|
|
cp.style.display = 'none';
|
|
return cp;
|
|
}
|
|
async menu_clicked(event){
|
|
let mit = event.target;
|
|
if (mit.children.length > 0){
|
|
for (var i=0;i<mit.children.length; i++){
|
|
if (mit.children[i].style.display == 'none'){
|
|
mit.children[i].style.display = "";
|
|
} else {
|
|
mit.children[i].style.display = 'none';
|
|
}
|
|
}
|
|
return
|
|
}
|
|
console.log('item clicked');
|
|
}
|
|
create_children(p, items){
|
|
console.log('create_children():items=', items, 'p=', p);
|
|
for (let i=0;i<items.length;i++){
|
|
let item = items[i];
|
|
let menu_item = this.create_menuitem(item);
|
|
p.appendChild(menu_item);
|
|
if (item.hasOwnProperty('items')){
|
|
let cp = this.create_submenu_container();
|
|
menu_item.appendChild(cp);
|
|
this.create_children(cp, item.items);
|
|
}
|
|
}
|
|
}
|
|
create_menuitem(item){
|
|
let i18n = bricks_app.i18n;
|
|
console.log('i18n=', i18n);
|
|
let e = document.createElement('div');
|
|
e.textContent = i18n._(item.label || item.name);
|
|
// e.description = item
|
|
console.log('create_menuitem():item=', item, 'obj=', e);
|
|
return e;
|
|
}
|
|
}
|
|
|
|
Factory.register('Menu', Menu);
|
|
class Modal extends Layout {
|
|
constructor(options){
|
|
/*
|
|
{
|
|
auto_open:
|
|
auto_close:
|
|
org_index:
|
|
width:
|
|
height:
|
|
bgcolor:
|
|
title:
|
|
archor: cc ( tl, tc, tr
|
|
cl, cc, cr
|
|
bl, bc, br )
|
|
}
|
|
*/
|
|
super(options);
|
|
this.set_width('100%');
|
|
this.set_height('100%');
|
|
this.ancestor_add_widget = Layout.prototype.add_widget.bind(this);
|
|
this.panel = new VBox({});
|
|
this.ancestor_add_widget(this.panel);
|
|
this.panel.set_width(this.opts.width);
|
|
this.panel.dom_element.style.backgroundColor = this.opts.bgcolor|| '#e8e8e8';
|
|
this.panel.set_height(this.opts.height);
|
|
this.panel.set_css('modal');
|
|
archorize(this.panel.dom_element, this.opts.get('archor', 'cc'));
|
|
this.create_title();
|
|
this.content = new VBox({width:'100%'});
|
|
this.panel.add_widget(this.content);
|
|
}
|
|
create_title(){
|
|
this.title_box = new HBox({width:'100%', height:'auto'});
|
|
this.title_box.set_css('title');
|
|
this.panel.add_widget(this.title_box);
|
|
this.title = new HBox({height:'100%'});
|
|
var icon = new Icon({url:bricks_resource('imgs/delete.png')});
|
|
icon.bind('click', this.dismiss.bind(this));
|
|
this.title_box.add_widget(this.title);
|
|
this.title_box.add_widget(icon);
|
|
}
|
|
create(){
|
|
var e = document.createElement('div');
|
|
e.style.display = "none"; /* Hidden by default */
|
|
e.style.position = "fixed"; /* Stay in place */
|
|
e.style.zIndex = this.opts.get('org_index', 0) + 1; /* Sit on top */
|
|
e.style.paddingTop = "100px"; /* Location of the box */
|
|
e.style.left = 0;
|
|
e.style.top = 0;
|
|
e.style.width = "100%"; /* Full width */
|
|
e.style.height = "100%"; /* Full height */
|
|
e.style.backgroundColor = 'rgba(0,0,0,0.4)'; /* Fallback color */
|
|
this.dom_element = e;
|
|
}
|
|
|
|
add_widget(w, index){
|
|
this.content.add_widget(w, index);
|
|
if (this.opts.auto_open){
|
|
this.open();
|
|
}
|
|
}
|
|
click_handler(event){
|
|
if (event.target == this.dom_element){
|
|
this.dismiss();
|
|
} else {
|
|
console.log('modal():click_handler()');
|
|
}
|
|
}
|
|
open(){
|
|
if (this.opts.auto_close){
|
|
var f = this.click_handler.bind(this);
|
|
this.bind('click', f);
|
|
}
|
|
this.dom_element.style.display = "";
|
|
}
|
|
dismiss(){
|
|
this.dom_element.style.display = "none";
|
|
if (this.opts.auto_close){
|
|
this.unbind('click', this.click_handler.bind(this));
|
|
}
|
|
}
|
|
}
|
|
|
|
class ModalForm extends Modal {
|
|
/*
|
|
{
|
|
auto_open:
|
|
auto_close:
|
|
org_index:
|
|
width:
|
|
height:
|
|
bgcolor:
|
|
archor: cc ( tl, tc, tr
|
|
cl, cc, cr
|
|
bl, bc, br )
|
|
title:
|
|
description:
|
|
dataurl:
|
|
submit_url:
|
|
fields:
|
|
}
|
|
*/
|
|
constructor(opts){
|
|
super(opts);
|
|
this.build_form();
|
|
}
|
|
build_form(){
|
|
var opts = {
|
|
title:this.opts.title,
|
|
description:this.opts.description,
|
|
dataurl:this.opts.dataurl,
|
|
submit_url:this.opts.submit_url,
|
|
fields:this.opts.fields
|
|
}
|
|
this.form = new Form(opts);
|
|
this.form.bind('submit', this.dismiss.bind(this));
|
|
}
|
|
}
|
|
Factory.register('Modal', Modal);
|
|
Factory.register('ModalForm', ModalForm);
|
|
|
|
/*
|
|
reply on "https://github.com/markedjs/marked"
|
|
add following lines before 'bricks.js'
|
|
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
|
*/
|
|
|
|
class MdText extends JsWidget {
|
|
/* options
|
|
{
|
|
"md_url":
|
|
"method":"GET"
|
|
"params":{}
|
|
}
|
|
*/
|
|
|
|
constructor(options){
|
|
super(options);
|
|
var f = this.build.bind(this);
|
|
this.load_event = new Event('loaded');
|
|
this.dom_element.style.overFlow='auto';
|
|
window.addEventListener('scroll', this.show_scroll.bind(this));
|
|
schedule_once(f, 0.01);
|
|
}
|
|
show_scroll(event){
|
|
console.log('scrollY=', window.scrollY);
|
|
}
|
|
build = async function(){
|
|
this._build(this.opts.md_url);
|
|
}
|
|
_build = async function(md_url){
|
|
var md_content = await tget(md_url);
|
|
this.dom_element.innerHTML = marked.parse(md_content);
|
|
|
|
/* change links in markdown to a bricks action */
|
|
var links = this.dom_element.getElementsByTagName('a');
|
|
for (var i=0; i<links.length; i ++){
|
|
var url = links[i].href;
|
|
links[i].href = '#';
|
|
links[i].onclick=this._build.bind(this, url);
|
|
}
|
|
this.dispatch('loaded', {'url':md_url});
|
|
}
|
|
}
|
|
class MarkdownViewer extends VBox {
|
|
/* options
|
|
{
|
|
navigator:true
|
|
recommentable:false
|
|
md_url:
|
|
method:"GET",
|
|
params:{}
|
|
}
|
|
*/
|
|
constructor(options){
|
|
super(options);
|
|
this.back_stack = [];
|
|
this.md_url = this.absurl(this.opts.md_url);
|
|
if (this.opts.navigator){
|
|
this.createBackButton();
|
|
}
|
|
this.mdtext = new MdText({
|
|
md_url:this.md_url
|
|
});
|
|
this.add_widget(this.mdtext);
|
|
this.mdtext.bind('loaded', this.add_back_stack.bind(this));
|
|
this.dom_element.style.overflow='auto';
|
|
this.dom_element.style.height='100%';
|
|
this.bind('scroll', this.show_scroll.bind(this));
|
|
}
|
|
show_scroll(event){
|
|
console.log('scrollY=', window.scrollY);
|
|
}
|
|
createBackButton = async function(){
|
|
var desc = {
|
|
"widgettype":"HBox",
|
|
"options":{},
|
|
"subwidgets":[
|
|
{
|
|
"widgettype":"Text",
|
|
"options":{
|
|
"text":"<<<<<<<"
|
|
}
|
|
}
|
|
]
|
|
}
|
|
var w = await widgetBuild(desc);
|
|
console.log('createBackButton():error, desc=', desc, 'w=', w);
|
|
var t = w.children[0];
|
|
console.log('createBackButton():text=',t);
|
|
t.bind('click',this.go_back.bind(this));
|
|
this.add_widget(w);
|
|
console.log('createBackButton():desc=',desc, 'w=', w);
|
|
}
|
|
add_back_stack(event){
|
|
console.log('go_back_stack():event=', event);
|
|
var url = event.params.url;
|
|
this.back_stack.push(url);
|
|
}
|
|
go_back = async function(event){
|
|
if (this.back_stack.length < 2){
|
|
return;
|
|
}
|
|
// ignore the current text url
|
|
this.back_stack.pop();
|
|
// get the back url
|
|
var url = this.back_stack.pop();
|
|
await this.mdtext._build(url);
|
|
}
|
|
|
|
build = async function(){
|
|
this._build(this.opts.md_url);
|
|
}
|
|
_build = async function(md_url){
|
|
var md_content = await tget(md_url);
|
|
this.md_el.innerHTML = marked.parse(md_content);
|
|
// this.md_el.baseURI = md_url;
|
|
|
|
/* change links in markdown to a bricks action */
|
|
var links = this.md_el.getElementsByTagName('a');
|
|
for (var i=0; i<links.length; i ++){
|
|
var url = links[i].href;
|
|
links[i].href = '#';
|
|
links[i].onclick=this._build.bind(this, url);
|
|
}
|
|
}
|
|
}
|
|
|
|
Factory.register('MarkdownViewer', MarkdownViewer);
|
|
class Video extends JsWidget {
|
|
constructor(options){
|
|
super(options);
|
|
this.dom_element.controls = "";
|
|
if (this.opts.autoplay)
|
|
this.dom_element.autoplay = "";
|
|
this.dom_element.type="application/vnd.apple.mpegurl";
|
|
this.dom_element.add_css('media-document');
|
|
this.dom_element.add_css('mac');
|
|
this.dom_element.add_css('video');
|
|
if (this.opts.url){
|
|
this.dom_element.src = this.opts.url;
|
|
}
|
|
}
|
|
create(){
|
|
this.dom_element = document.createElement('video');
|
|
}
|
|
}
|
|
|
|
class VideoPlayer extends VBox {
|
|
/*
|
|
we use [indigo-player](https://github.com/matvp91/indigo-player) as a base.
|
|
inside body, need to add following line before bricks.js
|
|
<script src="https://cdn.jsdelivr.net/npm/indigo-player@1/lib/indigo-pla yer.js"></script>
|
|
options
|
|
{
|
|
url:
|
|
}
|
|
*/
|
|
constructor(options){
|
|
super(options);
|
|
var autoplay = '';
|
|
if (this.opts.autoplay){
|
|
autoplay = 'autoplay';
|
|
}
|
|
var url = this.opts.url;
|
|
this.dom_element.innerHTML = `<video width="90%" controls ${autoplay} src="${url}" type="application/vnd.apple.mpegurl" class="media-document mac video" ></video>`;
|
|
this.video = this.dom_element.querySelector('video');
|
|
}
|
|
toggle_play(){
|
|
if (this.video.paused){
|
|
this.video.play();
|
|
} else {
|
|
this.video.pause();
|
|
}
|
|
}
|
|
|
|
}
|
|
Factory.register('VideoPlayer', VideoPlayer);
|
|
|
|
class AudioPlayer extends JsWidget {
|
|
/*
|
|
{
|
|
url:
|
|
autoplay:
|
|
}
|
|
*/
|
|
|
|
constructor(options){
|
|
super(options);
|
|
this.url = opt.url;
|
|
this.audio = this._create('audio');
|
|
// this.audio.autoplay = this.opts.autoplay;
|
|
this.audio.controls = true;
|
|
if (this.opts.autoplay){
|
|
this.audio.addEventListener('canplay', this.play_audio.bind(this));
|
|
}
|
|
this.audio.style.width = "100%"
|
|
var s = this._create('source');
|
|
s.src = this.opts.url;
|
|
this.audio.appendChild(s);
|
|
this.dom_element.appendChild(this.audio);
|
|
}
|
|
toggle_play(){
|
|
if (this.audio.paused){
|
|
this.audio.play();
|
|
} else {
|
|
this.audio.pause();
|
|
}
|
|
}
|
|
}
|
|
|
|
Factory.register('AudioPlayer', AudioPlayer);
|
|
class Toolbar extends Layout {
|
|
/* toolbar options
|
|
{
|
|
orientation:
|
|
target:
|
|
interval::
|
|
tools:
|
|
}
|
|
tool options
|
|
{
|
|
icon:
|
|
name:
|
|
label:
|
|
css:
|
|
}
|
|
|
|
event:
|
|
ready: after all the tools built, fire this event
|
|
command: after user click one of the tool will fire the event with params of the tools[i] object.
|
|
remove: after user delete a removable tool from the toolbar, will fire the event with its tool description object as params. the params can reach with event.params.
|
|
|
|
|
|
*/
|
|
constructor(options){
|
|
super(options);
|
|
this.toolList = [];
|
|
if (this.opts.orientation == 'vertical'){
|
|
this.bar = new VBox(options);
|
|
this.dom_element.classList.add('vtoolbar')
|
|
} else {
|
|
this.bar = new HBox(options);
|
|
this.dom_element.classList.add('htoolbar')
|
|
}
|
|
this.add_widget(this.bar);
|
|
this.clicked_btn = null;
|
|
this.preffix_css = this.opts.css || 'toolbar';
|
|
schedule_once(this.createTools.bind(this), 0.01);
|
|
}
|
|
add_interval_box(){
|
|
if (this.opts.orientation == 'vertical'){
|
|
this.bar.add_widget(new JsWidget({
|
|
height:this.opts.interval || '10px'
|
|
}));
|
|
} else {
|
|
this.bar.add_widget(new JsWidget({
|
|
width:this.opts.interval || '10px'
|
|
}));
|
|
}
|
|
}
|
|
createTools = async function(){
|
|
var l = this.opts.tools.length;
|
|
for (var i=0;i<l; i++){
|
|
await this.createTool(this.opts.tools[i]);
|
|
if (i < l -1 ){
|
|
this.add_interval_box();
|
|
}
|
|
}
|
|
this.dispatch('ready');
|
|
}
|
|
createTool = async function(desc){
|
|
var options = {
|
|
"widgettype":"Button",
|
|
"options":{
|
|
width:"auto",
|
|
orientation:"horizontal",
|
|
icon:desc.icon,
|
|
label:desc.label,
|
|
name:desc.name,
|
|
css:desc.css
|
|
}
|
|
};
|
|
var w = await widgetBuild(options);
|
|
if (! w){
|
|
console.log('Toolbar(): build widget failed', options);
|
|
return
|
|
}
|
|
w.bind('click', this.do_handle.bind(this, w));
|
|
w.tool_opts = desc;
|
|
this.add_removable(w);
|
|
this.toolList.push(w);
|
|
this.bar.add_widget(w);
|
|
return w;
|
|
}
|
|
remove_item(w, event){
|
|
this.bar.remove_widget(w);
|
|
this.toolList.remove(w);
|
|
w.unbind('click',this.do_handle.bind(this, w));
|
|
this.dispatch('remove', w.tool_opts);
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
}
|
|
do_handle(tool, event){
|
|
// var tool = event.target;
|
|
console.log('Toolbar() onclock,target=', event.target, tool);
|
|
var e = new Event('command');
|
|
var d = {};
|
|
d.update(tool.tool_opts);
|
|
if (this.opts.target){
|
|
d.target = this.opts.target;
|
|
}
|
|
this.dispatch('command', d);
|
|
|
|
if (this.clicked_btn){
|
|
this.clicked_btn.set_css(this.preffix_css + '-button-active', true);
|
|
}
|
|
tool.set_css(this.preffix_css + '-button-active');
|
|
this.clicked_btn = tool;
|
|
}
|
|
add_removable(item){
|
|
if (! item.tool_opts.removable) return;
|
|
var img_src = bricks_resource('imgs/delete.png');
|
|
var image = new Icon({url:img_src});
|
|
if (image){
|
|
item.add_widget(image);
|
|
image.bind('click',this.remove_item.bind(this, item));
|
|
console.log('Toolbar(): add_removable() for ', img_src);
|
|
} else {
|
|
console.log('Toolbar(): Image create error', img_src);
|
|
}
|
|
}
|
|
click(name){
|
|
for (var i=0;i<this.toolList.length;i++){
|
|
if (name==this.toolList[i].tool_opts.name){
|
|
this.toolList[i].dom_element.click();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Factory.register('Toolbar', Toolbar);
|
|
class TabPanel extends Layout {
|
|
/*
|
|
options
|
|
{
|
|
css:
|
|
tab_long: 100%
|
|
tab_pos:"top"
|
|
items:[
|
|
{
|
|
name:
|
|
label:"tab1",
|
|
icon:
|
|
removable:
|
|
refresh:false,
|
|
content:{
|
|
"widgettype":...
|
|
}
|
|
}
|
|
]
|
|
}
|
|
css:
|
|
tab
|
|
tab-button
|
|
tab-button-active
|
|
tab-button-hover
|
|
tab-content
|
|
*/
|
|
constructor(options){
|
|
super(options);
|
|
this.content_buffer = {};
|
|
this.cur_tab_name = '';
|
|
this.content_container = new VFiller({});
|
|
if (this.opts.tab_pos == 'top' || this.opts.tab_pos == 'bottom'){
|
|
this.set_css('vbox');
|
|
var height = this.opts.tab_wide || 'auto';
|
|
this.tab_container = new VBox({height:height});
|
|
this.tab_container.dom_element.style.width = this.opts.tab_long || '100%';
|
|
} else {
|
|
this.set_css('hbox');
|
|
var width= this.opts.tab_wide || 'auto';
|
|
this.tab_container = new VBox({width:width});
|
|
this.tab_container.dom_element.style.height = this.opts.tab_long || '100%';
|
|
}
|
|
if (this.opts.tab_pos == 'top' || this.opts.tab_pos == 'left'){
|
|
this.add_widget(this.tab_container);
|
|
this.add_widget(this.content_container);
|
|
} else {
|
|
this.add_widget(this.content_container);
|
|
this.add_widget(this.tab_container);
|
|
}
|
|
this.createToolbar();
|
|
this.set_css('tabpanel');
|
|
this.content_container.set_css('tabpanel-content');
|
|
}
|
|
show_first_tab(){
|
|
this.show_content_by_tab_name(this.opts.items[0].name);
|
|
}
|
|
show_content_by_tab_name(name){
|
|
this.toolbar.click(name);
|
|
}
|
|
createToolbar(){
|
|
var desc = {
|
|
tools:this.opts.items
|
|
};
|
|
var orient;
|
|
if (this.opts.tab_pos == 'top' || this.opts.tab_pos == 'bottom'){
|
|
orient = 'horizontal';
|
|
} else {
|
|
orient = 'vertical';
|
|
}
|
|
desc.orientation = orient;
|
|
this.toolbar = new Toolbar(desc);
|
|
this.toolbar.bind('command', this.show_tabcontent.bind(this));
|
|
this.toolbar.bind('remove', this.tab_removed.bind(this));
|
|
this.toolbar.bind('ready', this.show_first_tab.bind(this));
|
|
this.tab_container.add_widget(this.toolbar);
|
|
}
|
|
show_tabcontent = async function(event){
|
|
var tdesc = event.params;
|
|
var items = this.opts.items;
|
|
if (tdesc.name == this.cur_tab_name){
|
|
console.log('TabPanel(): click duplication click on same tab', tdesc)
|
|
return;
|
|
}
|
|
for (var i=0;i<items.length;i++){
|
|
if (tdesc.name == items[i].name){
|
|
var w = this.content_buffer.get(tdesc.name);
|
|
if (w && ! items[i].refresh){
|
|
this.content_container.clear_widgets();
|
|
this.content_container.add_widget(w);
|
|
this.cur_tab_name = name;
|
|
return;
|
|
}
|
|
w = await widgetBuild(items[i].content);
|
|
if (! w){
|
|
console.log('TabPanel():create content error', items[i].content);
|
|
return;
|
|
}
|
|
this.content_buffer[tdesc.name] = w;
|
|
this.content_container.clear_widgets();
|
|
this.content_container.add_widget(w);
|
|
this.cur_tab_name = tdesc.name;
|
|
return;
|
|
}
|
|
}
|
|
console.log('TabPanel(): click event handled but noting to do', tdesc)
|
|
}
|
|
add_tab(desc){
|
|
var item = this.toolbar.createTool(desc);
|
|
if (desc.removable){
|
|
this.add_removeable(item);
|
|
}
|
|
}
|
|
tab_removed(event){
|
|
var desc = event.params;
|
|
if (this.content_buffer.hasOwnProperty(desc.name))
|
|
delete this.content_buffer[desc.name];
|
|
|
|
if (desc.name == this.cur_tab_name){
|
|
this.show_first_tab();
|
|
}
|
|
}
|
|
}
|
|
|
|
Factory.register('TabPanel', TabPanel);
|
|
class UiType extends Layout {
|
|
constructor(opts){
|
|
super(opts);
|
|
this.name = this.opts.name;
|
|
this.required = opts.required || false;
|
|
this.ctype = 'text';
|
|
this.value = '';
|
|
}
|
|
|
|
getValue(){
|
|
var o = {}
|
|
o[this.name] = this.resultValue();
|
|
return o;
|
|
}
|
|
focus(){
|
|
this.dom_element.focus();
|
|
}
|
|
reset(){
|
|
var v = this.opts.value||this.opts.defaultvalue|| '';
|
|
this.setValue(v);
|
|
}
|
|
|
|
setValue(v){
|
|
if (! v)
|
|
v = '';
|
|
this.vlaue = v;
|
|
this.dom_element.value = v;
|
|
}
|
|
set_disabled(f){
|
|
this.dom_element.disabled = f;
|
|
}
|
|
set_readonly(f){
|
|
this.dom_element.readOnly = f;
|
|
}
|
|
set_required(f){
|
|
this.dom_element.required = f;
|
|
this.required = f;
|
|
}
|
|
}
|
|
|
|
class UiStr extends UiType {
|
|
static uitype='str';
|
|
/*
|
|
{
|
|
name:
|
|
value:
|
|
defaultValue:
|
|
align:"left", "center", "right"
|
|
length:
|
|
minlength:
|
|
tip:
|
|
width:
|
|
readonly:
|
|
required:
|
|
}
|
|
*/
|
|
constructor(opts){
|
|
super(opts);
|
|
this.sizable();
|
|
this.set_fontsize();
|
|
if (opts.readonly) {
|
|
this.set_readonly("Y");
|
|
} else {
|
|
this.set_readonly(false);
|
|
}
|
|
if (opts.width){
|
|
this.dom_element.style.width = opts.width;
|
|
}
|
|
|
|
}
|
|
create(){
|
|
var el = this._create('input');
|
|
this.dom_element = el;
|
|
this.pattern = '.*';
|
|
el.type = 'text';
|
|
el.id = el.name = this.opts.name;
|
|
if (this.opts.required)
|
|
el.required = true;
|
|
if (this.opts.css){
|
|
el.classList.add(this.opts.css);
|
|
this.actived_css = this.opts.css + '-actived';
|
|
} else {
|
|
el.classList.add('input');
|
|
this.actived_css = 'input_actived';
|
|
}
|
|
el.style.textAlign = this.opts.align || 'left';
|
|
if (this.opts.hasOwnProperty('length'))
|
|
el.maxlength = this.opts.length;
|
|
if (this.opts.hasOwnProperty('minlength'))
|
|
el.minlength = this.opts.minlength;
|
|
if (this.opts.hasOwnProperty('value'))
|
|
this.value = this.opts.value;
|
|
if (this.opts.defaultVlaue)
|
|
el.defaultValue = this.opts.defaultValue;
|
|
this.reset()
|
|
if (this.opts.tip)
|
|
el.placeholder = bricks_app.i18n._(this.opts.tip);
|
|
el.addEventListener('focus', this.onfocus.bind(this));
|
|
el.addEventListener('blur', this.onblur.bind(this));
|
|
el.addEventListener('input', this.set_value_from_input.bind(this))
|
|
}
|
|
onblur(event){
|
|
this.dom_element.classList.remove(this.actived_css);
|
|
}
|
|
onfocus(event){
|
|
this.dom_element.classList.add(this.actived_css);
|
|
}
|
|
check_pattern(value){
|
|
var r = new RegExp(this.pattern);
|
|
var v = value.match(r);
|
|
if (! v || v[0] == ''){
|
|
return null;
|
|
}
|
|
return v[0];
|
|
}
|
|
set_value_from_input(event){
|
|
var e = event.target;
|
|
if (e.value == ''){
|
|
this.value = '';
|
|
return
|
|
}
|
|
if (e.type == 'file'){
|
|
this.value = e.value;
|
|
return;
|
|
}
|
|
var v = this.check_pattern(e.value);
|
|
if (v == null){
|
|
e.value = this.value;
|
|
return
|
|
}
|
|
this.value = v;
|
|
var o = this.getValue();
|
|
this.dispatch('changed', o);
|
|
}
|
|
resultValue(){
|
|
return this.value;
|
|
}
|
|
setValue(v){
|
|
if (! v)
|
|
v = '';
|
|
this.value = v;
|
|
this.dom_element.value = '' + this.value;
|
|
}
|
|
}
|
|
|
|
class UiPassword extends UiStr {
|
|
static uitype='password';
|
|
/*
|
|
{
|
|
name:
|
|
value:
|
|
defaultValue:
|
|
align:"left", "center", "right"
|
|
length:
|
|
minlength:
|
|
tip:
|
|
readonly:
|
|
required:
|
|
}
|
|
*/
|
|
constructor(opts){
|
|
super(opts);
|
|
this.dom_element.type = 'password';
|
|
}
|
|
}
|
|
class UiInt extends UiStr {
|
|
static uitype='int';
|
|
/*
|
|
{
|
|
length:
|
|
}
|
|
*/
|
|
constructor(options){
|
|
super(options);
|
|
this.dom_element.style.textAlign = 'right';
|
|
this.dom_element.type = 'number';
|
|
this.pattern = '\\d*';
|
|
}
|
|
resultValue(){
|
|
return parseInt(this.value);
|
|
}
|
|
setValue(v){
|
|
if (! v)
|
|
v = '';
|
|
this.value = '' + v;
|
|
this.dom_element.value = '' + v;
|
|
}
|
|
|
|
}
|
|
class UiFloat extends UiInt {
|
|
static uitype='float';
|
|
/*
|
|
{
|
|
dec_len:
|
|
}
|
|
*/
|
|
constructor(options){
|
|
super(options);
|
|
this.pattern = '\\d*\\.?\\d+';
|
|
var dec_len = this.opts.dec_len || 2;
|
|
var step = 1;
|
|
for (var i=0; i<dec_len; i++)
|
|
step = step / 10;
|
|
this.dom_element.step = step;
|
|
}
|
|
resultValue(){
|
|
return parseFloat(this.value);
|
|
}
|
|
setValue(v){
|
|
if (! v)
|
|
v = '';
|
|
this.value = '' + v;
|
|
this.dom_element.value = '' + v;
|
|
}
|
|
}
|
|
class UiTel extends UiStr {
|
|
static uitype='tel';
|
|
/*
|
|
{
|
|
|
|
pattern:
|
|
}
|
|
*/
|
|
constructor(opts){
|
|
super(opts);
|
|
this.dom_element.type = 'tel';
|
|
if (this.opts.pattern)
|
|
this.dom_element.pattern = this.opts.pattern;
|
|
this.pattern = '[+]?\\d+';
|
|
}
|
|
}
|
|
|
|
class UiEmail extends UiStr {
|
|
static uitype='email';
|
|
/*
|
|
{
|
|
}
|
|
*/
|
|
constructor(opts){
|
|
super(opts);
|
|
this.dom_element.type = 'email';
|
|
if (this.opts.pattern)
|
|
this.dom_element.pattern = this.opts.pattern;
|
|
if (this.opts.pattern)
|
|
this.dom_element.pattern = this.opts.pattern;
|
|
}
|
|
}
|
|
|
|
class UiFile extends UiStr {
|
|
static uitype='file';
|
|
/*
|
|
{
|
|
accept:
|
|
capture:"user" or "environment"
|
|
multiple:
|
|
}
|
|
*/
|
|
constructor(opts){
|
|
super(opts);
|
|
this.dom_element.type = 'file';
|
|
if (this.opts.accept)
|
|
this.dom_element.accept = this.opts.accept;
|
|
if (this.opts.capture)
|
|
this.dom_element.capture = this.opts.capture;
|
|
if (this.opts.multiple)
|
|
this.dom_element.multiple = true;
|
|
}
|
|
setValue(v){
|
|
return;
|
|
this.value = v;
|
|
}
|
|
|
|
}
|
|
|
|
class UiCheck extends UiType {
|
|
static uitype = 'check';
|
|
constructor(opts){
|
|
super(opts);
|
|
UiCheck.prototype.update(Layout.prototype);
|
|
this.add_widget = Layout.prototype.add_widget.bind(this);
|
|
this.dom_element.style.width = 'auto';
|
|
this.dom_element.style.height = 'auto';
|
|
var state = 'unchecked';
|
|
if (opts.value){
|
|
state = 'checked';
|
|
}
|
|
this.ms_icon = new MultipleStateIcon({
|
|
state:state,
|
|
urls:{
|
|
checked:bricks_resource('imgs/checkbox-checked.png'),
|
|
unchecked:bricks_resource('imgs/checkbox-unchecked.png')
|
|
}
|
|
});
|
|
|
|
this.add_widget(this.ms_icon)
|
|
this.ms_icon.bind('state_changed', this.set_value_from_input.bind(this));
|
|
|
|
}
|
|
set_value_from_input(e){
|
|
var v;
|
|
if (this.ms_icon.state=='checked')
|
|
v = true;
|
|
else
|
|
v = false;
|
|
this.value = v;
|
|
var o = {};
|
|
o[this.name] = this.value;
|
|
this.dispatch('changed', o);
|
|
}
|
|
setValue(v){
|
|
this.value = v;
|
|
if (v)
|
|
this.ms_icon.set_state('checked');
|
|
else
|
|
this.ms_icon.set_state('unchecked');
|
|
}
|
|
resultValue(){
|
|
return this.value;
|
|
}
|
|
}
|
|
|
|
class UiCheckBox extends UiType {
|
|
static uitype='checkbox';
|
|
/*
|
|
{
|
|
name:
|
|
label:
|
|
value:
|
|
textField:'gg',
|
|
valueField:'hh',
|
|
otherField:'b',
|
|
data:[
|
|
{
|
|
'gg':
|
|
'hh':
|
|
'b':
|
|
}
|
|
]
|
|
or:
|
|
dataurl:
|
|
params:{},
|
|
method:
|
|
}
|
|
*/
|
|
constructor(opts){
|
|
super(opts);
|
|
this.valueField = opts.valueField || 'value';
|
|
this.textField = opts.textField || 'text';
|
|
this.value = this.opts.value || this.opts.defaultValue||[];
|
|
if (! Array.isArray(this.value)){
|
|
this.value = [ this.value ];
|
|
}
|
|
this.set_fontsize();
|
|
this.el_legend = this._create('legend');
|
|
var label = this.opts.label||this.opts.name;
|
|
this.el_legend.innerText = bricks_app.i18n._(label);
|
|
if (this.opts.dataurl){
|
|
schedule_once(this.load_data_onfly.bind(this), 0.01);
|
|
} else {
|
|
this.data = opts.data;
|
|
this.build_checkboxs();
|
|
}
|
|
}
|
|
create(){
|
|
this.dom_element = this._create('fieldset');
|
|
}
|
|
build_checkboxs(){
|
|
var data = this.data;
|
|
this.input_boxs = [];
|
|
for (var i=0; i<data.length;i++){
|
|
var hbox = new HBox({height:"auto",width:"100%"});
|
|
var opts = {}
|
|
var value = data[i][this.valueField];
|
|
if (this.value == value){
|
|
opts.value = true;
|
|
}
|
|
var check = new UiCheck(opts);
|
|
var otext = data[i][this.textField];
|
|
var txt = new Text({
|
|
otext:otext,
|
|
align:'left',
|
|
i18n:true});
|
|
txt.ht_left();
|
|
check.bind('changed', this.set_value_from_input.bind(this));
|
|
hbox.add_widget(check);
|
|
hbox.add_widget(txt);
|
|
this.add_widget(hbox);
|
|
this.input_boxs.push(check);
|
|
}
|
|
}
|
|
async load_data_onfly(){
|
|
var data = await jcall(this.opts.dataurl, {
|
|
"method":this.opts.method||'GET',
|
|
"params":this.opts.params});
|
|
this.data = data;
|
|
this.build_checkboxs();
|
|
}
|
|
set_value_from_input(event){
|
|
event.stopPropagation();
|
|
var e = event.target;
|
|
if (e.state=='checked'){
|
|
this.value.push(e.value);
|
|
} else {
|
|
this.value.remove(e.value)
|
|
}
|
|
var o = {};
|
|
o[this.name] = this.value;
|
|
this.dispatch('changed', o);
|
|
}
|
|
resultValue(){
|
|
return this.value;
|
|
}
|
|
setValue(v){
|
|
if (Array.isArray(v)){
|
|
this.value = v;
|
|
} else {
|
|
this.value = [v];
|
|
}
|
|
for (var i=0; i<this.input_boxs.length; i++){
|
|
if (this.value.includes(this.data[i][this.valueField])){
|
|
this.input_boxs[i].setValue(true);
|
|
} else {
|
|
this.input_boxs[i].setValue(false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
class UiDate extends UiStr {
|
|
static uitype='date';
|
|
/*
|
|
{
|
|
max_date:
|
|
min_date:
|
|
|
|
*/
|
|
constructor(options){
|
|
super(options);
|
|
this.opts_setup();
|
|
}
|
|
opts_setup(){
|
|
var e = this.dom_element;
|
|
e.type = 'date';
|
|
if (this.opts.max_date){
|
|
e.max = this.opts.max_date;
|
|
}
|
|
if (this.opts.min_date){
|
|
e.min = this.opts.min_date;
|
|
}
|
|
}
|
|
}
|
|
|
|
class UiText extends UiType {
|
|
static uitype='text';
|
|
/*
|
|
{
|
|
name:
|
|
value:
|
|
defaultValue:
|
|
tip:
|
|
rows:
|
|
cols:
|
|
readonly:
|
|
required:
|
|
}
|
|
*/
|
|
constructor(opts){
|
|
super(opts);
|
|
this.build();
|
|
this.sizable();
|
|
this.set_fontsize();
|
|
}
|
|
create(){
|
|
this.dom_element = this._create('textarea');
|
|
}
|
|
build(){
|
|
var e = this.dom_element;
|
|
e.id = e.name = this.opts.name;
|
|
e.rows = this.opts.rows || 5;
|
|
e.cols = this.opts.cols || 40;
|
|
// this.setValue(this.opts.value || this.opts.defaultvalue || '');
|
|
this.reset();
|
|
this.bind('input', this.set_value_from_input.bind(this))
|
|
}
|
|
set_value_from_input(event){
|
|
this.value = this.dom_element.innerText;
|
|
}
|
|
resultValue(){
|
|
return this.value;
|
|
}
|
|
setValue(v){
|
|
if (! v) v = '';
|
|
this.value = v;
|
|
this.dom_element.innerText = '';
|
|
this.dom_element.innerText = v;
|
|
debug('UiText: v=', v);
|
|
}
|
|
reset(){
|
|
var v = this.opts.value || this.opts.defaultvalue||'';
|
|
this.setValue(v);
|
|
}
|
|
}
|
|
|
|
class UiCode extends UiType {
|
|
/*
|
|
{
|
|
name:
|
|
value:
|
|
valueField:
|
|
textField:
|
|
defaultValue:
|
|
readonly:
|
|
required:
|
|
data:
|
|
dataurl:
|
|
params:
|
|
method:
|
|
}
|
|
*/
|
|
static uitype='code';
|
|
constructor(opts){
|
|
super(opts);
|
|
this.data = this.opts.data;
|
|
this.build();
|
|
}
|
|
create(){
|
|
this.dom_element = this._create('select');
|
|
}
|
|
build(){
|
|
this.dom_element.id = this.opts.name;
|
|
this.dom_element.name = this.opts.name;
|
|
if (this.opts.dataurl){
|
|
schedule_once(this.get_data.bind(this), 0.01);
|
|
return;
|
|
}
|
|
this.build_options(this.opts.data);
|
|
}
|
|
async get_data(event){
|
|
var params = this.opts.params;
|
|
if(event){
|
|
params.update(event.params);
|
|
}
|
|
var d = await jcall(this.opts.dataurl,
|
|
{
|
|
method:this.opts.method || 'GET',
|
|
params : params
|
|
});
|
|
this.data = d;
|
|
this.build_options(d);
|
|
}
|
|
build_options(data){
|
|
var e = this.dom_element;
|
|
e.replaceChildren();
|
|
var v = this.opts.value || this.opts.defaultvalue;
|
|
this.value = v;
|
|
this.option_widgets = {};
|
|
for (var i=0; i<data.length; i++){
|
|
var o = document.createElement('option');
|
|
o.value = data[i][this.opts.valueField||'value'];
|
|
o.innerText = bricks_app.i18n._(data[i][this.opts.textField||'text']);
|
|
this.option_widgets[o.value] = o;
|
|
if (o.value == v){
|
|
o.selected = true;
|
|
}
|
|
e.appendChild(o);
|
|
this.sizable_elements.push(o);
|
|
}
|
|
this.bind('input', this.set_value_from_input.bind(this))
|
|
this.sizable();
|
|
this.set_fontsize();
|
|
}
|
|
set_value_from_input(event){
|
|
this.value = this.dom_element.value;
|
|
this.dispatch('changed', this.getValue());
|
|
}
|
|
resultValue(){
|
|
return this.value;
|
|
}
|
|
setValue(v){
|
|
this.value = v;
|
|
for (var i=0; i<this.option_widgets.length; i++){
|
|
if (this.value == this.option_widgets[i].value){
|
|
this.option_widgets[i].checked = true
|
|
} else {
|
|
this.option_widgets[i].checked = true
|
|
}
|
|
}
|
|
}
|
|
reset(){
|
|
var v = this.opts.value||this.opts.defaultvalue||'';
|
|
this.setValue(v);
|
|
}
|
|
}
|
|
|
|
class _Input {
|
|
constructor(){
|
|
this.uitypes = [];
|
|
}
|
|
|
|
register(name, Klass){
|
|
if (! Klass){
|
|
console.log('Klass not defined', name);
|
|
return;
|
|
}
|
|
if (! Klass.uitype){
|
|
console.log('uitype of Klass not defined', name);
|
|
return;
|
|
|
|
}
|
|
Factory.register(name, Klass);
|
|
this.uitypes[Klass.uitype] = Klass;
|
|
}
|
|
factory(options){
|
|
var klass = this.uitypes.get(options.uitype);
|
|
if (klass){
|
|
return new klass(options);
|
|
}
|
|
console.log('create input for:', options.uitype, 'failed');
|
|
return null;
|
|
}
|
|
}
|
|
|
|
class UiAudio extends UiStr {
|
|
static uitype = 'audio';
|
|
constructor(opts){
|
|
super(opts);
|
|
this.autoplay = opts.autoplay;
|
|
this.readonly = opts.readonly;
|
|
this.icon = new Icon({
|
|
url: bricks_resource('imgs/right_arrow.png')});
|
|
this.add_widget(this.icon);
|
|
this.icon.bind('click', this.play_audio.bind(this));
|
|
this.player = new Audio({
|
|
url:this.value
|
|
});
|
|
if (this.autoplay){
|
|
schedule_once(this.autoplay_audio.bind(this), 1);
|
|
}
|
|
|
|
}
|
|
autoplay_audio(){
|
|
this.icon.dispatch('click');
|
|
}
|
|
play_audio(){
|
|
this.player.toggle_play();
|
|
}
|
|
play_audio(){
|
|
if(this.value!=this.player.src){
|
|
this.player.stop();
|
|
this.player.set_source(this.value);
|
|
this.player.play();
|
|
return
|
|
}
|
|
this.player.toggle_play();
|
|
this.btn.dispatch('click');
|
|
}
|
|
}
|
|
class UiVideo extends UiStr {
|
|
static uitype = 'video';
|
|
constructor(opts){
|
|
super(opts);
|
|
this.autoplay = opts.autoplay;
|
|
this.readonly = opts.readonly;
|
|
this.icon = new Icon({
|
|
url: bricks_resource('imgs/right_arrow.png')});
|
|
this.add_widget(this.icon);
|
|
this.icon.bind('click', this.play_audio.bind(this));
|
|
this.player = new VideoPlayer({
|
|
url:this.value
|
|
});
|
|
if (this.autoplay){
|
|
schedule_once(this.autoplay_audio.bind(this), 1);
|
|
}
|
|
|
|
}
|
|
autoplay_audio(){
|
|
this.icon.dispatch('click');
|
|
}
|
|
play_audio(){
|
|
this.player.toggle_play();
|
|
}
|
|
play_audio(){
|
|
if(this.value!=this.player.src){
|
|
this.player.stop();
|
|
this.player.set_source(this.value);
|
|
this.player.play();
|
|
return
|
|
}
|
|
this.player.toggle_play();
|
|
this.btn.dispatch('click');
|
|
}
|
|
}
|
|
var Input = new _Input();
|
|
Input.register('UiStr', UiStr);
|
|
Input.register('UiTel', UiTel);
|
|
Input.register('UiDate', UiDate);
|
|
Input.register('UiInt', UiInt);
|
|
Input.register('UiFloat', UiFloat);
|
|
Input.register('UiCheck', UiCheck);
|
|
Input.register('UiCheckBox', UiCheckBox);
|
|
Input.register('UiEmail', UiEmail);
|
|
Input.register('UiFile', UiFile);
|
|
Input.register('UiCode', UiCode);
|
|
Input.register('UiText', UiText);
|
|
Input.register('UiPassword', UiPassword);
|
|
Input.register('UiAudio', UiAudio);
|
|
Input.register('UiVideo', UiVideo);
|
|
class RegisterFunction {
|
|
constructor(){
|
|
this.rfs = {};
|
|
}
|
|
register(n, f){
|
|
this.rfs.update({n:f});
|
|
}
|
|
get(n){
|
|
return this.rfs.get(n);
|
|
}
|
|
}
|
|
class Button extends Layout {
|
|
/*
|
|
orientation:
|
|
height:100%,
|
|
width:100%,
|
|
item_rate:
|
|
tooltip:
|
|
color:
|
|
nonepack:
|
|
name:
|
|
icon:
|
|
label:
|
|
css:
|
|
action:{
|
|
target:
|
|
datawidget:
|
|
datamethod:
|
|
datascript:
|
|
dataparams:
|
|
rtdata:
|
|
actiontype:
|
|
...
|
|
}
|
|
*/
|
|
|
|
|
|
constructor(opts){
|
|
super(opts);
|
|
var style = {
|
|
display:"flex",
|
|
justifyContent:"center",
|
|
textAlign:"center",
|
|
alignItem:"center",
|
|
width:"auto",
|
|
height:"auto",
|
|
};
|
|
if (opts.nonepack){
|
|
style.padding = '0px';
|
|
style.border = '0';
|
|
} else {
|
|
style.padding = '0.5rem';
|
|
}
|
|
if (this.opts.orientation == 'horizontal'){
|
|
style.flexDirection = 'rows';
|
|
this.orient = 'h';
|
|
} else {
|
|
style.flexDirection = 'column';
|
|
this.orient = 'v';
|
|
}
|
|
this.item_rate = opts.item_rate || 1;
|
|
this.set_id(this.opts.name);
|
|
this.opts_setup();
|
|
this.dom_element.style.update(style);
|
|
}
|
|
create(){
|
|
this.dom_element = document.createElement('button');
|
|
}
|
|
opts_setup(){
|
|
var item_size = this.opts.item_size || bricks_app.charsize;
|
|
if (this.opts.icon){
|
|
var icon = new Icon({
|
|
rate:this.item_rate,
|
|
url:this.opts.icon
|
|
})
|
|
this.add_widget(icon);
|
|
icon.bind('click', this.target_clicked.bind(this));
|
|
}
|
|
if (this.opts.label){
|
|
var opts = {
|
|
rate:this.item_rate,
|
|
color:this.opts.color,
|
|
bgcolor:this.opts.bgcolor,
|
|
otext:this.opts.label,
|
|
i18n:true};
|
|
var txt = new Text(opts);
|
|
this.add_widget(txt);
|
|
txt.bind('click', this.target_clicked.bind(this));
|
|
}
|
|
}
|
|
target_clicked(event){
|
|
console.log('target_clicked() .... called ');
|
|
event.stopPropagation();
|
|
this.dispatch('click', this.opts);
|
|
if (this.opts.action){
|
|
if (this.opts.debug){
|
|
console.log('debug:opts=', this.opts);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Factory.register('Button', Button);
|
|
class Accordion extends VBox {
|
|
/*
|
|
{
|
|
item_size:
|
|
items:[
|
|
{
|
|
icon:
|
|
text:
|
|
content:{
|
|
widgettype:
|
|
...
|
|
}
|
|
}
|
|
]
|
|
}
|
|
*/
|
|
constructor(opts){
|
|
super(opts);
|
|
var item_size = this.opts.item_size || '25px';
|
|
this.set_height('100%');
|
|
var items = this.opts.items;
|
|
this.items = [];
|
|
this.subcontents = {};
|
|
var item_css = this.opts.css || 'accordion' + '-button';
|
|
var content_css = this.opts.css || 'accordion' + '-content';
|
|
for (var i=0; i< items.length; i++){
|
|
var opts = {
|
|
name:items[i].name,
|
|
icon:items[i].icon,
|
|
text:items[i].text,
|
|
height:'auto',
|
|
orientation:'horizontal'
|
|
}
|
|
var b = new Button(opts);
|
|
b.bind('click', this.change_content.bind(this));
|
|
this.items.push(b);
|
|
this.add_widget(b);
|
|
}
|
|
this.content = new VBox({});
|
|
}
|
|
async change_content(evnet){
|
|
var b = event.target.bricks_widget;
|
|
var name = b.opts.name;
|
|
console.log('accordion: button=', b, 'name=', name);
|
|
var pos = -1;
|
|
for (var i=0; i< this.opts.items.length; i++){
|
|
if (name == this.opts.items[i].name){
|
|
pos = i;
|
|
break
|
|
}
|
|
}
|
|
if (pos==-1){
|
|
debug('Accordion():name=',name, 'not found in items',this.opts.items);
|
|
}
|
|
var c = this.subcontents.get(name);
|
|
if (! c){
|
|
c = await widgetBuild(this.opts.items[pos].content);
|
|
this.subcontents[name] = c;
|
|
}
|
|
this.content.clear_widgets();
|
|
this.content.add_widget(c);
|
|
try {
|
|
this.remove_widget(this.content);
|
|
}
|
|
catch(e){
|
|
;
|
|
}
|
|
this.add_widget(this.content, pos+1);
|
|
}
|
|
}
|
|
|
|
Factory.register('Accordion', Accordion);
|
|
class TreeNode extends VBox {
|
|
constructor(tree, parent, data){
|
|
var opts = {
|
|
width:'100%',
|
|
height:'auto',
|
|
overflow:'hidden'
|
|
}
|
|
super(opts);
|
|
this.tree = tree;
|
|
this.parent = parent;
|
|
this.children_loaded = false;
|
|
this.data = data;
|
|
this.is_leaf = this.data.is_leaf;
|
|
this.params = {id:this.data[this.tree.opts.idField]};
|
|
if (this.tree.multitype_tree){
|
|
this.params['type'] = this.data[this.tree.opts.typeField];
|
|
}
|
|
var n = new HBox({
|
|
height:'auto',
|
|
overflow:'hidden',
|
|
width:'100%'
|
|
})
|
|
n.dom_element.style.margin = bricks_app.charsize * 0.2;
|
|
this.add_widget(n);
|
|
n.bind('click', this.tree.node_click_handle.bind(this.tree, this));
|
|
this.node_widget = n;
|
|
this.create_node_content(n);
|
|
if (! this.data.is_leaf) {
|
|
this.container = new VBox({height:'auto', overflow:'hidden'});
|
|
this.add_widget(this.container);
|
|
this.container.dom_element.style.marginLeft = bricks_app.charsize + 'px';
|
|
if (this.data.children){
|
|
this.tree.create_node_children(this, this.data.children);
|
|
}
|
|
this.container.hide();
|
|
}
|
|
}
|
|
selected(flg){
|
|
if (flg){
|
|
this.str_w.set_css('selected');
|
|
} else {
|
|
this.str_w.set_css('selected',true);
|
|
}
|
|
}
|
|
async toggleExpandCollapse(event){
|
|
if (event.params == 'open') {
|
|
await this.expand();
|
|
} else {
|
|
this.collapse()
|
|
}
|
|
}
|
|
async expand(){
|
|
if (this.is_leaf){
|
|
return;
|
|
}
|
|
if (this.tree.opts.dataurl && !this.is_leaf && !this.children_loaded){
|
|
await this.tree.get_children_data(this)
|
|
this.children_loaded = true;
|
|
}
|
|
this.container.show();
|
|
}
|
|
collapse(){
|
|
if (this.is_leaf){
|
|
return;
|
|
}
|
|
this.container.hide();
|
|
}
|
|
create_node_content(widget){
|
|
var img_size = bricks_app.charsize;
|
|
if (this.is_leaf){
|
|
widget.add_widget(new BlankIcon({}));
|
|
} else {
|
|
var srcs = this.tree.opts.node_state_imgs || {};
|
|
var sources = {};
|
|
sources['open'] = srcs.get('open', bricks_resource('imgs/down_arrow.png'));
|
|
sources['close'] = srcs.get('close', bricks_resource('imgs/right_arrow.png'));
|
|
this.trigle = new MultipleStateIcon({
|
|
state:'close',
|
|
urls:sources,
|
|
height:img_size,
|
|
width:img_size
|
|
});
|
|
this.trigle.bind('state_changed', this.toggleExpandCollapse.bind(this));
|
|
widget.add_widget(this.trigle);
|
|
}
|
|
var dtype = this.data[this.tree.opts.typeField];
|
|
var icon = TypeIcons.get(dtype);
|
|
if (!icon && this.tree.opts.default_type){
|
|
icon = TypeIcons.get(his.tree.opts.default_type);
|
|
}
|
|
if (!icon){
|
|
icon = bricks_resource('imgs/folder.png');
|
|
}
|
|
var img = new Icon({
|
|
url:icon
|
|
});
|
|
widget.add_widget(img);
|
|
var txt = this.data[this.tree.opts.textField];
|
|
widget.add_widget(
|
|
this.str_w = new Text({text:txt}));
|
|
this.input = new UiStr({name:'text', value:txt});
|
|
this.input.bind('blur', this.edit_handle.bind(this));
|
|
widget.add_widget(this.str_w);
|
|
}
|
|
edit(){
|
|
this.node_widget.remove_widget(this.str_w);
|
|
this.input.setValue(this.str_w.text);
|
|
this.node_widget.add_widget(this.input);
|
|
}
|
|
async edit_handle(){
|
|
if (this.input.value==this.str_w.text)
|
|
return;
|
|
var v = this.input.value;
|
|
r = await this.syncdata('edit');
|
|
this.data[this.tree.opts.textField] = v;
|
|
this.str_w = new Text({text:v});
|
|
this.node_widget.remove_widget(this.input);
|
|
this.node_widget.add_widget(this.str_w);
|
|
}
|
|
async syncdata(mode){
|
|
}
|
|
}
|
|
|
|
class Tree extends VBox {
|
|
/*
|
|
{
|
|
row_height:
|
|
multitype_tree:false,
|
|
idField:
|
|
textField:
|
|
type_icons:
|
|
typeField:
|
|
default_type:
|
|
data:
|
|
dataurl:
|
|
node_state_imgs:{
|
|
open:url,
|
|
close:url
|
|
},
|
|
admin:{
|
|
{
|
|
addurl:
|
|
deleteurl:
|
|
updateurl:
|
|
othertools:[
|
|
]
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
constructor(options){
|
|
super(options);
|
|
this.set_height('100%');
|
|
this.row_height = this.opts.row_height || '35px';
|
|
this.multitype_tree = this.opts.multitype_tree||false;
|
|
this.selected_node = null;
|
|
this.create_toolbar();
|
|
this.container = new VScrollPanel({});
|
|
this.add_widget(this.container);
|
|
this.data_id = null;
|
|
if (this.opts.dataurl){
|
|
schedule_once(0.01, this.get_children_data.bind(this, this));
|
|
}
|
|
this.create_node_children(this, this.opts.data);
|
|
}
|
|
create_toolbar(){
|
|
}
|
|
async get_children_data(node){
|
|
var d = await jcall(this.opts.dataurl,{
|
|
method : this.opts.method || 'GET',
|
|
params : node.params
|
|
})
|
|
if (d.length == 0){
|
|
node.is_leaf = true;
|
|
} else {
|
|
this.create_tree_nodes(node, d);
|
|
}
|
|
}
|
|
create_node_children(node, data){
|
|
for (var i=0; i<data.length; i++){
|
|
var n = new TreeNode(this, node, data[i]);
|
|
node.container.add_widget(n);
|
|
}
|
|
}
|
|
node_click_handle(node, event){
|
|
if (this.selected_node){
|
|
this.selected_node.selected(false);
|
|
}
|
|
this.selected_node = node;
|
|
node.selected(true);
|
|
this.dispatch('node_click', node);
|
|
}
|
|
}
|
|
|
|
class EditableTree extends Tree {
|
|
/*
|
|
{
|
|
...
|
|
admin:{
|
|
url:
|
|
add:{
|
|
icon:
|
|
}
|
|
delete_node:{
|
|
icon:
|
|
}
|
|
move_up:
|
|
move_down:
|
|
move_top:
|
|
move_bottom:
|
|
}
|
|
}
|
|
*/
|
|
constructor(opts){
|
|
super(opts);
|
|
}
|
|
create_toolbar(){
|
|
if (!this.opts.admin){
|
|
return
|
|
}
|
|
var desc = {
|
|
height:'auto',
|
|
tools:[
|
|
{
|
|
name:'add',
|
|
icon:bricks_resource('imgs/add.png')
|
|
},
|
|
{
|
|
name:'edit',
|
|
icon:bricks_resource('imgs/edit.png')
|
|
},
|
|
{
|
|
name:'move_top',
|
|
icon:bricks_resource('imgs/move_top.png')
|
|
},
|
|
{
|
|
name:'move_up',
|
|
icon:bricks_resource('imgs/move_up.png')
|
|
},
|
|
{
|
|
name:'move_down',
|
|
icon:bricks_resource('imgs/move_down.png')
|
|
},
|
|
{
|
|
name:'move_button',
|
|
icon:bricks_resource('imgs/move_bottom.png')
|
|
},
|
|
{
|
|
name:'delete',
|
|
icon:bricks_resource('imgs/delete_node.png')
|
|
}
|
|
]
|
|
}
|
|
this.toolbar = new Toolbar(desc);
|
|
this.toolbar.bind('command', this.command_handle.bind(this));
|
|
this.add_widget(this.toolbar, 0);
|
|
}
|
|
command_handle(e){
|
|
console.log('command event fire ...', e);
|
|
var name = e.params.name;
|
|
switch (name) {
|
|
case 'add':
|
|
this.add_node();
|
|
break;
|
|
case 'delete':
|
|
this.delete_node();
|
|
break;
|
|
case 'edit':
|
|
this.edit_node();
|
|
break;
|
|
case 'move_top':
|
|
this.move_top();
|
|
break;
|
|
case 'move_up':
|
|
this.move_up();
|
|
break;
|
|
case 'move_down':
|
|
this.move_down();
|
|
break;
|
|
case 'move_bottom':
|
|
this.move_bottom();
|
|
break;
|
|
}
|
|
}
|
|
add_node(){
|
|
var node = this;
|
|
if (this.selected_node) node = this.selected_node;
|
|
var data = { };
|
|
data[this.opts.idField] = 'undefined';
|
|
data[this.opts.textField] = 'new node';
|
|
var n = new TreeNode(this, node, data);
|
|
node.container.add_widget(n);
|
|
n.edit();
|
|
console.log('add_node() finished ...');
|
|
}
|
|
edit_node(){
|
|
if (! this.selected_node){
|
|
return;
|
|
}
|
|
this.selected_node.edit();
|
|
}
|
|
delete_node(){
|
|
if (! this.selected_node){
|
|
return;
|
|
}
|
|
this.selected_node.delete();
|
|
}
|
|
move_top(){
|
|
if (! this.selected_node){
|
|
return;
|
|
}
|
|
this.selected_node.move_top();
|
|
}
|
|
move_up(){
|
|
if (! this.selected_node){
|
|
return;
|
|
}
|
|
this.selected_node.move_up();
|
|
}
|
|
move_down(){
|
|
if (! this.selected_node){
|
|
return;
|
|
}
|
|
this.selected_node.move_down();
|
|
}
|
|
move_botton(){
|
|
if (! this.selected_node){
|
|
return;
|
|
}
|
|
this.selected_node.move_botton();
|
|
}
|
|
}
|
|
class PolymorphyTree extends Tree {
|
|
/*
|
|
{
|
|
root:[t1],
|
|
nodetypes:{
|
|
t1:{
|
|
idField:
|
|
typeField:
|
|
textField:
|
|
icon:
|
|
contextmenu:
|
|
subtypes:[]
|
|
}
|
|
}
|
|
data:
|
|
dataurl:
|
|
}
|
|
*/
|
|
constructor(opts){
|
|
super(opts);
|
|
}
|
|
}
|
|
Factory.register('Tree', Tree);
|
|
Factory.register('EditableTree', EditableTree);
|
|
class MultipleStateImage extends Layout {
|
|
/*
|
|
{
|
|
state:
|
|
urls:{
|
|
state1:url1,
|
|
state2:url2,
|
|
...
|
|
}
|
|
width:
|
|
height:
|
|
}
|
|
*/
|
|
constructor(opts){
|
|
super(opts);
|
|
this.state = this.opts.state
|
|
var desc = {
|
|
urls : this.opts.urls[this.state],
|
|
width:this.opts.width,
|
|
height:this.opts.height
|
|
}
|
|
this.img = new Image(desc);
|
|
this.add_widget(this.img);
|
|
this.img.bind('click', this.change_state.bind(this));
|
|
}
|
|
set_state(state){
|
|
this.state = state;
|
|
this.img.set_url(this.opts.urls[state]);
|
|
}
|
|
|
|
change_state(event){
|
|
event.stopPropagation();
|
|
var states = Object.keys(this.opts.urls);
|
|
for (var i=0;i<states.length;i++){
|
|
if (states[i] == this.state){
|
|
var k = i + 1;
|
|
if (k >= states.length) k = 0;
|
|
this.state = states[k];
|
|
this.img.set_url(this.opts.urls[this.state]);
|
|
this.dispatch('state_changed', this.state);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
class MultipleStateIcon extends Icon {
|
|
constructor(opts){
|
|
opts.url = opts.urls[opts.state];
|
|
super(opts);
|
|
this.state = opts.state;
|
|
this.urls = opts.urls;
|
|
this.bind('click', this.change_state.bind(this));
|
|
}
|
|
change_state(event){
|
|
event.stopPropagation();
|
|
var states = Object.keys(this.urls);
|
|
for (var i=0;i<states.length;i++){
|
|
if (states[i] == this.state){
|
|
var k = i + 1;
|
|
if (k >= states.length) k = 0;
|
|
this.set_state(states[k]);
|
|
this.dispatch('state_changed', this.state);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
set_state(state){
|
|
this.state = state;
|
|
this.set_url(this.urls[state]);
|
|
}
|
|
|
|
}
|
|
Factory.register('MultipleStateImage', MultipleStateImage);
|
|
|
|
|
|
class FormBody extends VBox {
|
|
/*
|
|
{
|
|
title:
|
|
description:
|
|
fields: [
|
|
{
|
|
"name":,
|
|
"label":,
|
|
"removable":
|
|
"icon":
|
|
"content":
|
|
},
|
|
...
|
|
]
|
|
}
|
|
*/
|
|
constructor(opts){
|
|
super(opts);
|
|
this.name_inputs = {};
|
|
if (this.opts.title){
|
|
var t = new Title2({
|
|
otext:this.opts.title,
|
|
height:'auto',
|
|
i18n:true});
|
|
this.add_widget(t);
|
|
}
|
|
if (this.opts.description){
|
|
var d = new Text({
|
|
otext:this.opts.description,
|
|
height:'auto',
|
|
i18n:true});
|
|
this.add_widget(d);
|
|
}
|
|
this.form_body = new Layout({width:'100%',
|
|
overflow:'auto'
|
|
});
|
|
this.add_widget(this.form_body);
|
|
this.form_body.set_css('multicolumns');
|
|
this.build_fields();
|
|
}
|
|
reset_data(){
|
|
for (var name in this.name_inputs){
|
|
if (! this.name_inputs.hasOwnProperty(name)){
|
|
continue;
|
|
}
|
|
var w = this.name_inputs[name];
|
|
w.reset();
|
|
}
|
|
}
|
|
|
|
async validation(){
|
|
var data = {};
|
|
for (var name in this.name_inputs){
|
|
if (! this.name_inputs.hasOwnProperty(name)){
|
|
continue;
|
|
}
|
|
|
|
var w = this.name_inputs[name];
|
|
var d = w.getValue();
|
|
if (w.required && ( d[name] == '' || d[name] === null)){
|
|
console.log('data=', data, 'd=', d);
|
|
w.focus();
|
|
return;
|
|
}
|
|
data.update(d);
|
|
}
|
|
if (this.submit_url){
|
|
var rzt = await jcall(this.submit_url,
|
|
{
|
|
params:data
|
|
});
|
|
}
|
|
this.dispatch('submit', data);
|
|
}
|
|
|
|
build_fields(){
|
|
var fields = this.opts.fields;
|
|
for (var i=0; i<fields.length; i++){
|
|
var box = new VBox({height:'auto',overflow:'none'});
|
|
box.set_css('inputbox');
|
|
this.form_body.add_widget(box);
|
|
var txt = new Text({
|
|
otext:fields[i].label||fields[i].name,
|
|
height:'auto',
|
|
i18n:true});
|
|
box.add_widget(txt);
|
|
var w = Input.factory(fields[i]);
|
|
if (w){
|
|
box.add_widget(w);
|
|
this.name_inputs[fields[i].name] = w;
|
|
console.log(fields[i].uitype, 'create Input ok');
|
|
} else {
|
|
console.log(fields[i], 'createInput failed');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
class Form extends VBox {
|
|
/*
|
|
{
|
|
title:
|
|
description:
|
|
cols:
|
|
dataurl:
|
|
toolbar:
|
|
submit_url:
|
|
fields
|
|
}
|
|
*/
|
|
constructor(opts){
|
|
super(opts);
|
|
this.body = new FormBody(opts);
|
|
this.add_widget(this.body);
|
|
this.build_toolbar(this);
|
|
}
|
|
build_toolbar(widget){
|
|
var box = new HBox({height:'auto', width:'100%'});
|
|
widget.add_widget(box);
|
|
var tb_desc = this.opts.toolbar || {
|
|
width:"auto",
|
|
tools:[
|
|
{
|
|
icon:bricks_resource('imgs/submit.png'),
|
|
name:'submit',
|
|
label:'Submit'
|
|
},
|
|
{
|
|
icon:bricks_resource('imgs/cancel.png'),
|
|
name:'cancel',
|
|
label:'Cancel'
|
|
}
|
|
]
|
|
};
|
|
var tbw = new Toolbar(tb_desc);
|
|
tbw.bind('command', this.command_handle.bind(this));
|
|
box.add_widget(new HFiller());
|
|
box.add_widget(tbw);
|
|
box.add_widget(new HFiller());
|
|
}
|
|
command_handle(event){
|
|
var params = event.params;
|
|
console.log('Form(): click_handle() params=', params);
|
|
if (!params){
|
|
error('click_handle() get a null params');
|
|
return
|
|
}
|
|
if (params.name == 'submit'){
|
|
this.validation();
|
|
} else if (params.name == 'cancel'){
|
|
this.cancel();
|
|
} else if (params.name == 'reset'){
|
|
this.reset_data();
|
|
} else {
|
|
if (params.action){
|
|
f = buildEventHandler(this, params);
|
|
f(event);
|
|
}
|
|
}
|
|
}
|
|
cancel(){
|
|
this.dispatch('cancel');
|
|
}
|
|
|
|
}
|
|
|
|
class TabForm extends Form {
|
|
/*
|
|
options
|
|
{
|
|
css:
|
|
tab_long: 100%
|
|
tab_pos:"top"
|
|
items:[
|
|
{
|
|
name:
|
|
label:"tab1",
|
|
icon:
|
|
removable:
|
|
refresh:false,
|
|
content:{
|
|
"widgettype":...
|
|
}
|
|
}
|
|
]
|
|
}
|
|
{
|
|
...
|
|
fields:[
|
|
{
|
|
}
|
|
]
|
|
*/
|
|
constructor(opts){
|
|
super(opts);
|
|
}
|
|
build_fields(fields){
|
|
}
|
|
}
|
|
|
|
Factory.register('Form', Form);
|
|
// Factory.register('TabForm', TabForm);
|
|
class Popup extends VBox {
|
|
/*
|
|
{
|
|
holder:
|
|
title:
|
|
auto_open:
|
|
auto_dismiss:
|
|
archor:cc
|
|
timeout:
|
|
}
|
|
*/
|
|
constructor(opts){
|
|
super(opts);
|
|
this.holder = opts.holder;
|
|
this.task = null;
|
|
this.title = opts.title|| 'Title';
|
|
this.archor = opts.archor || 'cc';
|
|
this.timeout = opts.timeout;
|
|
this.set_css('message');
|
|
this.build();
|
|
archorize(this.dom_element, this.archor);
|
|
}
|
|
|
|
build(){
|
|
var tb = new HBox({height:'40px'});
|
|
tb.set_css('title');
|
|
this.add_widget(tb);
|
|
var tit = new Text({otext:this.title, i18n:true});
|
|
this.content = new VBox({});
|
|
VBox.prototype.add_widget.bind(this)(this.content);
|
|
tb.add_widget(tit);
|
|
this.holder = Body;
|
|
if (this.opts.holder){
|
|
if (type(this.opts.holder) == 'string'){
|
|
this.holder = getWidgetById(this.opts.holder, Body);
|
|
} else {
|
|
this.holder = this.opts.holder;
|
|
}
|
|
}
|
|
}
|
|
open(){
|
|
this.holder.add_widget(this);
|
|
if (this.timeout && this.timeout > 0){
|
|
this.task = schedule_once(this.dismiss.bind(this), this.timeout);
|
|
}
|
|
}
|
|
add_widget(w, idx){
|
|
this.content.add_widget(w, idx);
|
|
if (this.opts.auto_open){
|
|
this.open();
|
|
}
|
|
}
|
|
dismiss(){
|
|
if (this.task){
|
|
this.task.cancel();
|
|
this.task = null
|
|
}
|
|
this.holder.remove_widget(this);
|
|
}
|
|
}
|
|
|
|
class Message extends VBox {
|
|
/*
|
|
{
|
|
title:
|
|
message:
|
|
params:
|
|
auto_open:
|
|
auto_dismiss:
|
|
archor:cc
|
|
timeout:
|
|
}
|
|
*/
|
|
constructor(opts){
|
|
super(opts);
|
|
var t = new Text({otext:this.opts.text,
|
|
i18n:true});
|
|
this.add_widget(t);
|
|
}
|
|
}
|
|
|
|
class Error extends Message {
|
|
constructor(opts){
|
|
super(opts);
|
|
this.set_css('error');
|
|
}
|
|
}
|
|
|
|
class PopupForm extends Popup {
|
|
/*
|
|
{
|
|
form:{
|
|
}
|
|
}
|
|
*/
|
|
constructor(options){
|
|
super(options);
|
|
this.form = new Form(this.opts.form);
|
|
this.add_widget(this.form);
|
|
this.form.bind('submit', this.close_popup.bind(this));
|
|
this.form.bind('discard', this.close_popup.bind(this));
|
|
}
|
|
close_popup(e){
|
|
this.dismiss();
|
|
}
|
|
}
|
|
Factory.register('Message', Message);
|
|
Factory.register('Error', Error);
|
|
Factory.register('PopupForm', PopupForm);
|
|
class BufferedDataLoader {
|
|
/*
|
|
{
|
|
url:
|
|
method:
|
|
params:
|
|
buffer_pages:
|
|
pagerows:
|
|
}
|
|
usage:
|
|
var p = Paging({...});
|
|
p.loadData(); // return page(1) data
|
|
p.getPage(5); // return page(5) data
|
|
p.nextPage()
|
|
p.previousPage()
|
|
*/
|
|
constructor(w, opts){
|
|
this.widget = w;
|
|
this.url = opts.url;
|
|
this.loading = false
|
|
this.method = opts.method || 'GET';
|
|
this.params = opts.params || {};
|
|
this.buffer_pages = opts.buffer_pages || 5;
|
|
this.pagerows = opts.pagerows || 60;
|
|
this.cur_page = -1;
|
|
this.buffer = {};
|
|
this.buffered_pages = 0;
|
|
this.total_record = -1;
|
|
this.cur_params = {};
|
|
}
|
|
async loadData(params){
|
|
this.buffer = {};
|
|
if (!params) params = {};
|
|
var p = this.params.copy();
|
|
p.update(params);
|
|
p.rows = this.pagerows;
|
|
this.cur_params = p;
|
|
this.cur_page = 1;
|
|
return this.loadPage();
|
|
}
|
|
|
|
async loadPage(page){
|
|
if (this.loading) return;
|
|
this.loading = true;
|
|
if (this.buffered_pages >= this.buffer_pages){
|
|
this.widget.del_old_rows(this.pagerows, this.direction);
|
|
this.buffered_pages -= 1;
|
|
}
|
|
var params = this.cur_params.copy();
|
|
params.page = this.cur_page;
|
|
params.rows = this.pagerows;
|
|
var d = await jcall(this.url, {
|
|
method:this.method,
|
|
params:params});
|
|
this.total_records = d.total;
|
|
d.page = this.cur_page;
|
|
d.total_page = this.total_records / this.pagerows;
|
|
if (d.total_page * this.pagerows < this.total_record){
|
|
d.total_page += 1;
|
|
}
|
|
this.total_page = d.total_page;
|
|
this.widget.add_rows(d.rows);
|
|
this.buffered_pages += 1;
|
|
this.loading = false;
|
|
return d;
|
|
}
|
|
|
|
async nextPage(){
|
|
if (this.loading) return;
|
|
if (this.cur_page >= this.total_page){
|
|
return;
|
|
}
|
|
this.direction = 'down';
|
|
this.cur_page += 1;
|
|
return await this.loadPage();
|
|
}
|
|
async previousPage(){
|
|
if (this.loading) return;
|
|
if (this.cur_page <= 1){
|
|
return
|
|
}
|
|
this.direction = 'up';
|
|
this.cur_page -= 1;
|
|
return await this.loadPage();
|
|
}
|
|
}
|
|
|
|
var low_handle = function(widget, dim, last_pos, cur_pos, maxlen, winsize){
|
|
var dir = cur_pos - last_pos;
|
|
var max_rate = cur_pos / (maxlen - winsize);
|
|
var min_rate = cur_pos / maxlen;
|
|
if (!widget.threshold && dir > 0 && max_rate >= widget.max_threshold){
|
|
console.log('max_threshold reached ...');
|
|
widget.thresgold = true;
|
|
widget.dispatch('max_threshold');
|
|
return
|
|
}
|
|
if (!widget.threshold && dir < 0 && min_rate <= widget.min_threshold){
|
|
console.log('min_threshold reached ...');
|
|
widget.thresgold = true;
|
|
widget.dispatch('min_threshold');
|
|
return
|
|
}
|
|
console.log('scroll_handle() called ...', max_rate, cur_pos, maxlen, winsize);
|
|
}
|
|
|
|
class HScrollPanel extends HFiller {
|
|
/*
|
|
{
|
|
min_threshold:
|
|
max_threshold:
|
|
}
|
|
*/
|
|
constructor(opts){
|
|
super(opts);
|
|
this.min_threshold = opts.min_threshold || 0.02;
|
|
this.max_threshold = opts.max_threshold || 0.95;
|
|
this.bind('scroll', this.scroll_handle.bind(this))
|
|
this.last_scrollLeft = this.dom_element.scrollLeft;
|
|
this.threshold = false;
|
|
}
|
|
scroll_handle(event){
|
|
if (event.target != this.dom_element){
|
|
// console.log('HScroll():scroll on other', event.target);
|
|
return;
|
|
}
|
|
var e = this.dom_element;
|
|
if ( e.scrollWidth - e.clientWidth < 1) {
|
|
// console.log('HScroll():same size');
|
|
return;
|
|
}
|
|
low_handle(this, 'x', this.last_scrollLeft,
|
|
e.scrollLeft,
|
|
e.scrollWidth,
|
|
e.clientWidth);
|
|
|
|
this.last_scrollLeft = e.scrollLeft;
|
|
}
|
|
}
|
|
|
|
class VScrollPanel extends VFiller {
|
|
/*
|
|
{
|
|
min_threshold:
|
|
max_threshold:
|
|
}
|
|
*/
|
|
constructor(opts){
|
|
super(opts);
|
|
this.min_threshold = opts.min_threshold || 0.02;
|
|
this.max_threshold = opts.max_threshold || 0.95;
|
|
this.bind('scroll', this.scroll_handle.bind(this))
|
|
this.last_scrollTop = this.dom_element.scrollTop;
|
|
}
|
|
scroll_handle(event){
|
|
if (event.target != this.dom_element){
|
|
// console.log('scroll on other', event.target);
|
|
return;
|
|
}
|
|
var e = this.dom_element;
|
|
if ( e.scrollHeight - e.clientHeight < 2) {
|
|
// console.log('same size');
|
|
return;
|
|
}
|
|
low_handle(this, 'y', this.last_scrollTop,
|
|
e.scrollTop,
|
|
e.scrollHeight,
|
|
e.clientHeight);
|
|
this.last_scrollTop = e.scrollTop;
|
|
}
|
|
}
|
|
|
|
Factory.register('VScrollPanel', VScrollPanel);
|
|
Factory.register('HScrollPanel', HScrollPanel);
|
|
|
|
var set_max_height = function(r1, r2){
|
|
var h = r1.dom_element.offsetHeight;
|
|
if (h < r2.dom_element.offsetHeight){
|
|
h = r2.dom_element.offsetHeight;
|
|
}
|
|
if (h == 0){
|
|
h = 30;
|
|
}
|
|
var flex = '0 0 ' + h + 'px';
|
|
r1.set_style('flex', flex);
|
|
r2.set_style('flex', flex);
|
|
}
|
|
|
|
class Row {
|
|
constructor(dg, rec) {
|
|
this.dg = dg;
|
|
this.data = rec.copy();
|
|
this.freeze_cols = [];
|
|
this.normal_cols = [];
|
|
this.name_widgets = {};
|
|
this.click_handler = this.dg.click_handler.bind(this.dg, this);
|
|
this.freeze_row = this.create_col_widgets(this.dg.freeze_fields, this.freeze_cols);
|
|
if (this.freeze_row){
|
|
this.freeze_row.set_css('datagrid-row');
|
|
this.freeze_row.set_style('width', this.freeze_width + 'px');
|
|
}
|
|
this.normal_row = this.create_col_widgets(this.dg.normal_fields, this.normal_cols);
|
|
if (this.normal_row){
|
|
this.normal_row.set_css('datagrid-row');
|
|
this.normal_row.set_style('width', this.normal_width + 'px');
|
|
}
|
|
if (this.freeze_row && this.normal_row) {
|
|
set_max_height(this.freeze_row, this.normal_row);
|
|
}
|
|
}
|
|
create_col_widgets(fields, cols) {
|
|
for (var i = 0; i < fields.length; i++) {
|
|
var f = fields[i];
|
|
var opts = f.uioptions || {};
|
|
var w;
|
|
opts.update({
|
|
name: f.name,
|
|
label: f.label,
|
|
uitype: f.uitype,
|
|
width: f.width,
|
|
required: true,
|
|
readonly: true
|
|
});
|
|
if (opts.uitype == 'button') {
|
|
opts.icon = f.icon;
|
|
opts.action = f.action;
|
|
opts.action.params = this.data.copy();
|
|
w = new Button(opts);
|
|
buildEventBind(this.dg, w, 'click', opts.action);
|
|
} else {
|
|
opts.value = this.data[f.name],
|
|
w = Input.factory(opts);
|
|
w.bind('click', this.click_handler);
|
|
}
|
|
w.dom_element.style['min-width'] = w.width + 'px';
|
|
w.set_style('flex', '0 0 ' + convert2int(f.width) + 'px');
|
|
cols.push(w);
|
|
this.name_widgets[f.name] = w;
|
|
}
|
|
if (cols.length > 0) {
|
|
var row = new HBox({ height: 'auto' })
|
|
for (var i = 0; i < cols.length; i++) {
|
|
row.add_widget(cols[i]);
|
|
}
|
|
return row;
|
|
}
|
|
return null;
|
|
}
|
|
selected() {
|
|
if (this.freeze_row) {
|
|
this.freeze_cols.forEach(w => { w.set_css('selected', false) })
|
|
}
|
|
if (this.normal_row) {
|
|
this.normal_cols.forEach(w => { w.set_css('selected', false) })
|
|
}
|
|
}
|
|
unselected() {
|
|
if (this.freeze_row) {
|
|
this.freeze_cols.forEach(w => { w.set_css('selected', true) })
|
|
}
|
|
if (this.normal_row) {
|
|
this.normal_cols.forEach(w => { w.set_css('selected', true) })
|
|
}
|
|
}
|
|
toogle_select(e, f) {
|
|
if (f) e.classList.add('selected');
|
|
else e.classList.remove('selected');
|
|
}
|
|
}
|
|
|
|
class DataGrid extends VBox {
|
|
/*
|
|
{
|
|
data:
|
|
dataurl:
|
|
method:
|
|
params:
|
|
title:
|
|
description:
|
|
show_info:
|
|
miniform:
|
|
toolbar:
|
|
tailer:
|
|
row_height:
|
|
header_css:
|
|
body_css:
|
|
fields:[
|
|
{
|
|
name:
|
|
label:
|
|
datatype:
|
|
uitype:
|
|
uioptions:
|
|
freeze:
|
|
width:
|
|
}
|
|
]
|
|
}
|
|
*/
|
|
constructor(opts) {
|
|
super(opts);
|
|
this.loading = false;
|
|
this.select_row = null;
|
|
this.set_css('datagrid');
|
|
this.dataurl = opts.dataurl;
|
|
this.method = opts.method;
|
|
this.params = opts.params;
|
|
this.title = opts.title;
|
|
this.check = opts.check || false;
|
|
this.lineno = opts.lineno || false;
|
|
this.description = opts.description;
|
|
this.show_info = opts.show_info;
|
|
this.admin = opts.admin;
|
|
this.row_height = opts.row_height;
|
|
this.fields = opts.fields;
|
|
this.header_css = opts.header_css || 'grid_header';
|
|
this.body_css = opts.body_css || 'grid_body';
|
|
if (this.title) {
|
|
this.title_bar = new HBox({ height: 'auto' });
|
|
this.add_widget(this.title_bar);
|
|
var tw = new Title1({ otext: this.title, i18n: true });
|
|
this.title_bar.add_widget(tw);
|
|
}
|
|
if (this.description) {
|
|
this.descbar = new HBox({ height: 'auto' });
|
|
this.add_widget(this.descbar);
|
|
var dw = new Text({ otext: this.description, i18n: true });
|
|
this.descbar.add_widget(dw);
|
|
}
|
|
|
|
if (this.opts.miniform || this.opts.toolbar){
|
|
this.admin_bar = new HBox({height:'auto'});
|
|
}
|
|
if (this.opts.miniform){
|
|
this.miniform = new MiniForm(this.opts.miniform);
|
|
this.miniform.bind('input', this.miniform_input.bind(this));
|
|
this.admin_bar.add_widget(this.miniform);
|
|
}
|
|
if (this.opts.toolbar) {
|
|
this.admin_bar.add_widget(new HFiller({}));
|
|
self.toolbar = new Toolbar(this.opts.toolbar);
|
|
self.toolbar.bind('command', this.command_handle.bind(this));
|
|
this.admin_bar.add_widget(this.toolbar);
|
|
}
|
|
this.create_parts();
|
|
if (this.show_info) {
|
|
this.infow = new HBox({ height: '40px' });
|
|
this.add_widget(this.infow);
|
|
}
|
|
if (this.dataurl) {
|
|
this.loader = new BufferedDataLoader(this, {
|
|
pagerows: 80,
|
|
buffer_pages: 5,
|
|
url: absurl(this.dataurl, this),
|
|
methiod: this.method,
|
|
params: this.params
|
|
})
|
|
schedule_once(this.loader.loadData.bind(this.loader), 0.01);
|
|
if (this.freeze_body) {
|
|
this.freeze_body.bind('x_min_threshold', this.loader.previousPage.bind(this.loader));
|
|
this.freeze_body.bind('x_max_threshold', this.loader.nextPage.bind(this.loader));
|
|
}
|
|
this.normal_body.bind('x_min_threshold', this.loader.previousPage.bind(this.loader));
|
|
this.normal_body.bind('x_max_threshold', this.loader.nextPage.bind(this.loader));
|
|
} else {
|
|
if (this.data) {
|
|
this.add_rows(this.data);
|
|
}
|
|
}
|
|
}
|
|
miniform_input(event){
|
|
var params = this.miniform.getValue();
|
|
this.loader.loadData(params);
|
|
}
|
|
loadData(params){
|
|
this.loader.loadData(params)
|
|
}
|
|
command_handle(event){
|
|
}
|
|
del_old_rows(cnt, direction) {
|
|
if (this.freeze_body) {
|
|
if (direction == 'down') {
|
|
this.freeze_body.remove_widgets_at_begin(cnt);
|
|
} else {
|
|
this.freeze_body.remove_widgets_at_end(cnt);
|
|
}
|
|
}
|
|
if (direction == 'down') {
|
|
this.normal_body.remove_widgets_at_begin(cnt);
|
|
} else {
|
|
this.normal_body.remove_widgets_at_end(cnt);
|
|
}
|
|
}
|
|
add_rows(records, direction) {
|
|
var index = null;
|
|
if (direction == 'down') {
|
|
index = 0
|
|
}
|
|
for (var i = 0; i < records.length; i++) {
|
|
this.add_row(records[i], index);
|
|
}
|
|
}
|
|
add_row(data, index) {
|
|
var row = new Row(this, data);
|
|
if (this.freeze_body)
|
|
this.freeze_body.add_widget(row.freeze_row, index);
|
|
if (this.normal_body)
|
|
this.normal_body.add_widget(row.normal_row, index);
|
|
}
|
|
check_desc() {
|
|
return {
|
|
freeze:true,
|
|
uitype: 'check',
|
|
name: '_check',
|
|
width: '20px'
|
|
}
|
|
}
|
|
lineno_desc() {
|
|
return {
|
|
freeze:true,
|
|
uitype: 'int',
|
|
name: '_lineno',
|
|
label: '#',
|
|
width: '100px'
|
|
}
|
|
}
|
|
create_parts() {
|
|
this.freeze_width = 0;
|
|
this.normal_width = 0;
|
|
var hbox = new HBox({});
|
|
hbox.set_css('datagrid-grid');
|
|
this.add_widget(hbox);
|
|
this.freeze_fields = [];
|
|
this.normal_fields = [];
|
|
if (this.check) {
|
|
this.fields.push(this.check_desc());
|
|
}
|
|
if (this.lineno) {
|
|
this.fields.push(this.lineno_desc());
|
|
}
|
|
for (var i = 0; i < this.fields.length; i++) {
|
|
var f = this.fields[i];
|
|
if (!f.width || f.width <= 0 ) f.width = 100;
|
|
if (f.freeze) {
|
|
this.freeze_fields.push(f);
|
|
this.freeze_width += convert2int(f.width);
|
|
} else {
|
|
this.normal_fields.push(f);
|
|
this.normal_width += convert2int(f.width);
|
|
|
|
}
|
|
}
|
|
this.freeze_part = null;
|
|
this.normal_part = null;
|
|
console.log('width=', this.freeze_width, '-', this.normal_width, '...');
|
|
if (this.freeze_fields.length > 0) {
|
|
this.freeze_part = new VBox({});
|
|
this.freeze_part.set_css('datagrid-left');
|
|
this.freeze_part.set_style('width', this.freeze_width + 'px');
|
|
this.freeze_header = new HBox({ height: 'auto', width: 'auto' });
|
|
this.freeze_header.set_css('datagrid-row');
|
|
this.freeze_header.set_style('width', this.freeze_width + 'px');
|
|
this.freeze_body = new VScrollPanel({ width: 'auto' })
|
|
this.freeze_body.set_css('datagrid-body');
|
|
this.freeze_body.bind('scroll', this.coscroll.bind(this));
|
|
// this.freeze_body.dom_element.style.marginBottom = 18 + 'px';
|
|
}
|
|
if (this.normal_fields.length > 0) {
|
|
this.normal_part = new VBox({
|
|
width: this.normal_width + 'px',
|
|
height:'100%',
|
|
csses:"hscroll"
|
|
});
|
|
this.normal_part.set_css('datagrid-right');
|
|
this.normal_header = new HBox({
|
|
});
|
|
this.normal_header.set_css('datagrid-row');
|
|
this.normal_header.set_style('flex',
|
|
'0 0 ' + this.normal_width + 'px');
|
|
this.normal_body = new VScrollPanel({
|
|
csses:"vbox vscroll",
|
|
height:"100%",
|
|
width: this.normal_width + 'px'
|
|
});
|
|
this.normal_body.set_css('datagrid-body')
|
|
// this.normal_body.dom_element.style.marginBottom = 18 + 'px';
|
|
}
|
|
this.create_header();
|
|
if (this.freeze_fields.length > 0) {
|
|
this.freeze_part.add_widget(this.freeze_header);
|
|
this.freeze_part.add_widget(this.freeze_body);
|
|
hbox.add_widget(this.freeze_part);
|
|
}
|
|
if (this.normal_fields.length > 0) {
|
|
this.normal_part.add_widget(this.normal_header);
|
|
this.normal_part.add_widget(this.normal_body);
|
|
this.normal_body.bind('scroll', this.coscroll.bind(this));
|
|
this.normal_body.bind('min_threshold', this.load_previous_data.bind(this));
|
|
this.normal_body.bind('max_threshold', this.load_next_data.bind(this));
|
|
hbox.add_widget(this.normal_part);
|
|
}
|
|
}
|
|
load_previous_data() {
|
|
console.log('event min_threshold fired ........');
|
|
this.loader.previousPage();
|
|
}
|
|
load_next_data() {
|
|
console.log('event max_threshold fired ........');
|
|
this.loader.nextPage();
|
|
}
|
|
coscroll(event) {
|
|
var w = event.target.bricks_widget;
|
|
if (w == this.freeze_body) {
|
|
this.normal_body.dom_element.scrollTop = w.dom_element.scrollTop;
|
|
} else if (w == this.normal_body && this.freeze_body) {
|
|
this.freeze_body.dom_element.scrollTop = w.dom_element.scrollTop;
|
|
}
|
|
}
|
|
|
|
create_header() {
|
|
for (var i = 0; i < this.freeze_fields.length; i++) {
|
|
var f = this.freeze_fields[i];
|
|
var t = new Text({
|
|
otext: f.label || f.name,
|
|
i18n: true,
|
|
});
|
|
if (f.width) {
|
|
t.set_style('flex','0 0 ' + convert2int(f.width) + 'px');
|
|
} else {
|
|
t.set_style('flex','0 0 100px');
|
|
}
|
|
this.freeze_header.add_widget(t);
|
|
t.dom_element.column_no = 'f' + i;
|
|
}
|
|
for (var i = 0; i < this.normal_fields.length; i++) {
|
|
var f = this.normal_fields[i];
|
|
var t = new Text({
|
|
otext: f.label || f.name,
|
|
i18n: true,
|
|
});
|
|
if (f.width) {
|
|
t.set_style('flex','0 0 ' + convert2int(f.width) + 'px');
|
|
} else {
|
|
t.set_style('flex','0 0 100px');
|
|
}
|
|
this.normal_header.add_widget(t);
|
|
t.dom_element.column_no = 'n' + i;
|
|
}
|
|
if (this.normal_header && this.freeze_header) {
|
|
set_max_height(this.normal_header,
|
|
this.freeze_header);
|
|
}
|
|
}
|
|
click_handler(row, event) {
|
|
if (this.selected_row) {
|
|
this.selected_row.unselected();
|
|
}
|
|
this.selected_row = row;
|
|
this.selected_row.selected();
|
|
this.dispatch('row_click', row);
|
|
console.log('DataGrid():click_handler, row=', row, 'event=', event);
|
|
}
|
|
}
|
|
|
|
Factory.register('DataGrid', DataGrid);
|
|
class MiniForm extends HBox {
|
|
/*
|
|
{
|
|
defaultname:
|
|
label_width:
|
|
input_width:
|
|
params:
|
|
"fields":[
|
|
{
|
|
name:
|
|
label:
|
|
icon:
|
|
uitype:
|
|
uiparams:
|
|
}
|
|
...
|
|
]
|
|
}
|
|
*/
|
|
constructor(opts){
|
|
opts.width = 'auto';
|
|
opts.height = 'auto';
|
|
super(opts);
|
|
this.build();
|
|
}
|
|
build(){
|
|
var name = this.opts.defaultname;
|
|
if (!name){
|
|
name = this.opts.fields[0].name;
|
|
}
|
|
this.build_options();
|
|
this.build_widgets(name);
|
|
}
|
|
build_widgets(name){
|
|
if (this.input){
|
|
this.input.unbind('input', this.input_handle.bind(this));
|
|
}
|
|
this.clear_widgets();
|
|
this.add_widget(this.choose);
|
|
var f = this.opts.fields.find( i => i.name==name);
|
|
var desc = f.copy();
|
|
desc.width = 'auto';
|
|
var i = Input.factory(desc);
|
|
i.bind('input', this.input_handle.bind(this));
|
|
this.add_widget(i);
|
|
this.input = i;
|
|
}
|
|
build_options(){
|
|
var desc = {
|
|
width:"90px",
|
|
name:"name",
|
|
uitype:"code",
|
|
valueField:'name',
|
|
textField:'label',
|
|
data:this.opts.fields
|
|
};
|
|
var w = Input.factory(desc);
|
|
w.bind('changed', this.change_input.bind(this));
|
|
this.choose = w;
|
|
this.add_widget(w);
|
|
}
|
|
show_options(e){
|
|
console.log('show_options() called ...');
|
|
this.choose.show();
|
|
}
|
|
change_input(e){
|
|
var name = this.choose.value;
|
|
this.build_widgets(name);
|
|
}
|
|
input_handle(e){
|
|
var d = this.getValue();
|
|
console.log('input_handle() ..', d);
|
|
this.dispatch('input', d);
|
|
}
|
|
getValue(){
|
|
var d = this.opts.params || {};
|
|
var v = this.input.getValue();
|
|
d.update(v);
|
|
return d;
|
|
}
|
|
}
|
|
|
|
Factory.register('MiniForm', MiniForm);
|
|
|
|
/*
|
|
need xterm.js
|
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/xterm@4.19.0/css/xterm.css" />
|
|
<script src="https://cdn.jsdelivr.net/npm/xterm@4.19.0/lib/xterm.js"></script>
|
|
*/
|
|
class XTerminal extends JsWidget {
|
|
/*
|
|
{
|
|
ws_url:
|
|
host:
|
|
ssh_port:
|
|
user:
|
|
}
|
|
*/
|
|
constructor(opts){
|
|
super(opts);
|
|
schedule_once(this.open.bind(this), 0.1);
|
|
}
|
|
async open(){
|
|
try {
|
|
this.term = new Terminal({
|
|
cursorBlink: "block"
|
|
});
|
|
}
|
|
catch(e){
|
|
console.log(e);
|
|
return;
|
|
}
|
|
|
|
this.ws = new WebSocket(this.opts.ws_url, "echo-protocol");
|
|
var curr_line = "";
|
|
var entries = [];
|
|
this.term.open(this.dom_element);
|
|
this.term.write("web shell $ ");
|
|
|
|
this.term.prompt = () => {
|
|
if (curr_line) {
|
|
let data = { method: "command", command: curr_line };
|
|
this.ws.send(JSON.stringify(data));
|
|
}
|
|
};
|
|
this.term.prompt();
|
|
|
|
// Receive data from socket
|
|
this.ws.onmessage = msg => {
|
|
this.term.write("\r\n" + JSON.parse(msg.data).data);
|
|
curr_line = "";
|
|
};
|
|
|
|
this.term.on("key", function(key, ev) {
|
|
//Enter
|
|
if (ev.keyCode === 13) {
|
|
if (curr_line) {
|
|
entries.push(curr_line);
|
|
this.term.write("\r\n");
|
|
this.term.prompt();
|
|
}
|
|
} else if (ev.keyCode === 8) {
|
|
// Backspace
|
|
if (curr_line) {
|
|
curr_line = curr_line.slice(0, curr_line.length - 1);
|
|
this.term.write("\b \b");
|
|
}
|
|
} else {
|
|
curr_line += key;
|
|
this.term.write(key);
|
|
}
|
|
});
|
|
|
|
// paste value
|
|
this.term.on("paste", function(data) {
|
|
curr_line += data;
|
|
this.term.write(data);
|
|
});
|
|
}
|
|
}
|
|
|
|
Factory.register('XTerminal', XTerminal);
|
|
|