This commit is contained in:
yumoqing 2023-09-14 10:28:07 +08:00
parent 018b267269
commit 77994be32a
143 changed files with 9451 additions and 1 deletions

21
README.md Normal file → Executable file
View File

@ -1,2 +1,21 @@
# bricks # Bricks
A new web application development framework to make web application development more easier like play bricks
## Documentation
Documents in English, please read from [docs](docs/index.md), 中文资料看[这里](docs/cn/index.md)
## Development base on components
We built web development components which use a options objects as API.
third party can develops their component suply the standard of components API
Most front-end development tools only help user to build the front-end UI, and use script to build the app's logic.
Bricks not only build the UI but also the front-end logic.
Bricks provide a new mathiciam to description the event fire, and event handler, Bricks use json data to descripts event and it handler, when event fire, according the json data, Bricks dynamicly constructs the event handler.
## Dependanance
[Marked](https://github.com/yumoqing/marked) is a tool for markdown text parser, extends from [MarkedJs marked](https://github.com/markedjs/marked), we extends audio and video link, user can directly use `!v[text](url)` pattern to show a video player, and `!a[text](url)` pattern to show a audio player

BIN
bricks/.DS_Store vendored Executable file

Binary file not shown.

72
bricks/accordion.js Normal file
View File

@ -0,0 +1,72 @@
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 = objget(this.subcontents,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);

46
bricks/aggrid.js Normal file
View File

@ -0,0 +1,46 @@
class AG_Grid extends JsWidget {
/*
{
dataurl:
fields:
}
*/
constructor(opts){
super(opts);
ag_opts = {}
}
init(){
this.datasource = {
getRows:this.getRows,
startRow
this.ag_opts.columnDefs = [];
this.ag_opts.rowModelType = 'infinite';
this.ag_opts.maxConcurrentDatasourceRequests: 1,
this.ag_opts.datasource = this.datasource;
for (var i=0; i< this.opts.fields.length; i++){
var f = {
width:this.opts.fields[i].width | 100,
headerName:this.opts.fields[i].label|this.opts.fields[i].name,
field:this.opts.fields[i].name
},
this.ag_opts.columnDefs.push(f)
}
this.ag_opts.defaultColDef = {sortable: true, filter: true};
this.ag_opts.rowSelection = 'multiple';
this.ag_opts.animateRows = true;
this.ag_opts.onCellClicked = this.cell_clicked.bind(this);
this.aggrid = new agGrid.Grid(this.dom_element, this.ag_opts);
fetch(this.opts.dataurl)
.then(response => response.json())
.then(data => {
// load fetched data into grid
this.ag_opts.api.setRowData(data);
});
}
cell_clicked(params){
console.log('clicked, params=', params);
}
}

34
bricks/audio.js Executable file
View File

@ -0,0 +1,34 @@
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);

10
bricks/baseKnowledge.txt Executable file
View File

@ -0,0 +1,10 @@
css 子元素按照父元素位置定位
```
#parentDiv { position:relative; }
#childDiv { position:absolute; left:50px; top:20px; }
```
css 子元素按照window位置定位( use top:, left:, right:, and bottom: to position as you see fit.)
```
#yourDiv { position:fixed; bottom:40px; right:40px; }
```

460
bricks/bricks.js Executable file
View File

@ -0,0 +1,460 @@
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,
event);
}
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 = objcopy(desc.options);
extend(options.params, 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 = objcopy(desc.options);
extend(options.options, rtdata);
return f.bind(target, target, desc.mode || 'replace', options);
}
var buildRegisterFunctionHandler = function(w, target, rtdata, desc){
var f = objget(registerfunctions, desc.rfname);
if( ! f){
console.log('rfname:', desc.rfname, 'not registed', desc);
return null;
}
var params = {};
if (desc.params){
extend(params, desc.params);
}
if (rtdata){
extend(params, 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 = {};
extend(params, desc.params)
extend(params, rtdata);
return f.bind(target, params);
}
var buildScriptHandler = function(w, target, rtdata, desc){
var params = {};
extend(params, desc.params)
extend(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 = {};
extend(params, desc.params)
extend(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(objget(opts, '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(){
console.log('build() begin', this.opts.widget, 'body=', Body);
var opts = structuredClone(this.opts.widget);
var w = await widgetBuild(opts, Body);
console.log('build() end', this.opts.widget, w, 'body=', Body);
return w
}
async run(){
await this.change_language(this);
var w = await this.build();
this.root = w;
console.log('w=', w, 'Body=', Body, Wterm, 'Factory=', Factory)
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();
}
}
}
}
}

9
bricks/build.cmd Normal file
View File

@ -0,0 +1,9 @@
set SOURCES=utils.js i18n.js factory.js widget.js bricks.js image.js jsoncall.js myoperator.js layout.js menu.js modal.js markdown_viewer.js video.js audio.js toolbar.js tab.js input.js registerfunction.js button.js accordion.js tree.js multiple_state_image.js form.js message.js paging.js scroll.js datagrid.js miniform.js terminal.js
del ..\dist\bricks.js
echo %SOURCES%
type %SOURCES% > ..\dist\bricks.js
copy css\*.* ..\dist\css
copy imgs/*.* ..\dist\imgs
copy ..\examples\*.* ..\dist\examples
copy ..\docs\*.* ..\dist\docs
rem uglifyjs --compress --mangle %SOURCES% > ..\dist\bricks.min.js

32
bricks/build.sh Executable file
View File

@ -0,0 +1,32 @@
SOURCES="uitypesdef.js utils.js i18n.js factory.js widget.js \
bricks.js image.js \
jsoncall.js myoperator.js layout.js menu.js modal.js \
markdown_viewer.js video.js audio.js toolbar.js tab.js \
input.js registerfunction.js button.js accordion.js \
tree.js multiple_state_image.js form.js message.js \
paging.js scroll.js datagrid.js dataviewer.js \
miniform.js wterm.js "
echo ${SOURCES}
cat ${SOURCES} > ../dist/bricks.js
uglifyjs --compress --mangle -- ../dist/bricks.js > ../dist/bricks.min.js
if [ ! -d ../dist/css ];then
rm -rf ../dist/css
mkdir ../dist/css
fi
if [ ! -d ../dist/examples ];then
rm -rf ../dist/examples
mkdir ../dist/examples
fi
if [ ! -d ../dist/docs ];then
rm -rf ../dist/docs
mkdir ../dist/docs
fi
cp -a css/* ../dist/css
cp -a imgs/* ../dist/imgs
cp -a ../examples ../dist
cp -a ../docs ../dist
cp -a ../dist/* ~/www/wwwroot/bricks
# cd ../dist
# cp -a * ~/www/wwwroot/bricks

92
bricks/button.js Normal file
View File

@ -0,0 +1,92 @@
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();
extend(this.dom_element.style, 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);

7
bricks/css.js Normal file
View File

@ -0,0 +1,7 @@
var css = {
multicolumns:{
columnWidth:'350px',
colummGap:'10px'
}
}

254
bricks/css/bricks.css Executable file
View File

@ -0,0 +1,254 @@
html,
body {
height: 100%;
margin: 0;
overflow: auto;
}
.griddata {
display: grid;
grid-gap: 1px;
}
.modal {
padding: 10px;
color: #e8e8e8;
background-color: rgba(0.3, 0.3, 0.3, 1);
border: 1px solid #ccc;
border-radius: 5px;
margin-bottom: 10px;
}
.modal>.title {
background-color: #a0a0a0;
}
.message {
padding: 10px;
width: 30%;
height: 30%;
background-color: #f0f0f0;
border: 1px solid #ccc;
border-radius: 5px;
margin-bottom: 10px;
}
.error {
padding: 10px;
width: 30%;
height: 30%;
background-color: #f0f0f0;
border: 1px solid #ccc;
border-radius: 5px;
margin-bottom: 10px;
}
.message>.title {
background-color: #3030f0;
}
.vscroll {
overflow-x: scroll;
}
.hscroll {
overflow-y: scroll;
}
.scroll {
overflow:scroll;
}
.error>.title {
background-color: #f03030;
}
.vcontainer {
display: flex;
flex-direction: column;
height:100%;
scroll:auto;
}
.vbox {
display: flex;
flex-direction: column;
}
.hcontainer {
display: flex;
flex-direction: row;
width:100%;
scroll:auto;
}
.hbox {
display: flex;
flex-direction: row;
}
.fixitem {
flex:none;
}
.filler, .hfiller, .vfiller {
flex: auto;
scroll:auto;
}
.vfiller .vbox:last-child {
overflow-x: overlay;
}
.vline {
width:1px;
height:100%;
background-colir:#999;
}
.hline {
height:1px;
width:100%;
background-colir:#999;
}
.hfiller::-webkit-scrollbar {
display: none;
}
.flc {
width: 203px;
overflow-y: scroll;
overflow-x: visible;
}
.vtoolbar {
heigth: 100%;
background-color: #f1f1f1;
border: 1px solid #ccc;
}
.selected {
background-color: #d4d4d4;
}
.htoolbar {
width: 100%;
height: 40px;
background-color: #f1f1f1;
border: 1px solid #ccc;
}
.toolbar-button {
background-color: inherit;
float: left;
border: none;
outline: none;
cursor: pointer;
padding: 14px 16px;
transition: 0.3s;
border: 1px solid #888;
}
.toolbar-button-active {
background-color: #ddd;
}
.tabpanel {
background-color: #ededed;
border: 3px solid #888;
}
.tabpanel-content {
background-color: #f8f8f8;
border: 2px solid #888;
}
.multicolumns {
column-width: 340px;
colomn-gap: 10px';
overflow-x:none;
}
.popup {
z-index: 1000;
position: absolution;
background-color: #f1f1f1;
border: 1px solid #c1c1c1;
}
.inputbox {
background-color: #cccccc;
border: 1px solid #bbbbbb;
padding: 10px;
margin: 0 0 1em 0;
}
button[tooltip]:hover::after {
content: attr(tooltip);
display: block;
position: absolute;
transform: translateX(-50%);
}
div[tooltip]:hover::after {
content: attr(tooltip);
display: block;
position: absolute;
transform: translateX(-50%);
}
input[tooltip],
div[tooltip] {
width: max-content;
margin: auto;
}
.datagrid {
display:flex;
flex-direction:column;
width:100%;
height:100%;
}
.datagrid-grid {
width: 100%;
flex: 1;
overflow: auto;
display: flex;
flex-direction: row;
}
.datagrid-left {
height:100%;
display: flex;
flex-direction: column;
overflow: auto;
}
.datagrid-left>.scrollbar {
width:0px;
opacity:0;
}
.datagrid-right {
flex:1 0 ;
height:100%;
overflow: auto;
display: flex;
flex-direction: column;
}
.grid_header, .grid_footer {
height: 50px;
background-color: blue;
}
.datagrid-row {
flex:0 0 150px;
display: flex;
flex-direction: row;
}
.datagrid-body {
width: 100%;
flex: 1;
overflow: auto;
display: flex;
flex-direction: column;
}

384
bricks/datagrid.js Normal file
View File

@ -0,0 +1,384 @@
class Row {
constructor(dg, rec) {
this.dg = dg;
this.data = objcopy(rec);
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');
}
}
create_col_widgets(fields, cols) {
for (var i = 0; i < fields.length; i++) {
var f = fields[i];
var opts = f.uioptions || {};
var w;
extend(opts, {
name: f.name,
label: f.label,
uitype: f.uitype,
width: f.width,
required: true,
row_data: objcopy(this.data),
readonly: true
});
if (opts.uitype == 'button') {
opts.icon = f.icon;
opts.action = f.action;
opts.action.params = objcopy(this.data);
opts.action.params.row = this;
w = new Button(opts);
w.bind('click', this.button_click.bind(w))
} else {
w = viewFactory(opts, this.data);
w.bind('click', this.click_handler);
}
w.desc_dic = opts;
w.rowObj = this;
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;
}
button_click(event){
this.getValue=function(){
return this.desc_dic.row_data;
}
var desc = this.desc_dic.action;
desc.datawidget = this;
desc.datamethod = 'getValue';
var f = universal_handler(this, this.rowObj, desc);
}
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),
method: this.method,
params: this.params
})
schedule_once(this.loader.loadData.bind(this.loader), 0.01);
if (this.freeze_body) {
this.freeze_body.bind('min_threshold', this.loader.previousPage.bind(this.loader));
this.freeze_body.bind('max_threshold', this.loader.nextPage.bind(this.loader));
}
this.normal_body.bind('min_threshold', this.loader.previousPage.bind(this.loader));
this.normal_body.bind('max_threshold', this.loader.nextPage.bind(this.loader));
} else {
if (this.data) {
this.add_rows(this.data);
}
}
}
clear_data(){
if (this.normal_body)
this.normal_body.clear_widgets();
if (this.freeze_body)
this.freeze_body.clear_widgets()
this.selected_row = null;
}
miniform_input(event){
var params = this.miniform.getValue();
this.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) {
if (! direction) direction = 'down';
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: this.row_height + 'px', width: this.freeze_width + 'px'});
this.freeze_body = new VScrollPanel({ height: "100%",
width: this.freeze_width + 'px' })
this.freeze_body.set_css('datagrid-body');
this.freeze_body.bind('scroll', this.coscroll.bind(this));
}
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({ height: this.row_height + 'px', width: this.normal_width + 'px'});
// this.normal_header.set_css('datagrid-row');
this.normal_body = new VScrollPanel({
height:"100%",
width: this.normal_width + 'px'
});
this.normal_body.set_css('datagrid-body')
}
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;
}
}
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);

70
bricks/dataviewer.js Normal file
View File

@ -0,0 +1,70 @@
class DataViewer extends VScrollPanel {
/*
opts={
dataurl:
pagerows:
params:
buffer_page:
viewer_url: or
viewer_tmpl:
}
*/
constructor(opts){
opts.width = '100%';
super(opts);
this.loader = new BufferedDataLoader(this, {
pagerows: opts.pagerows || 80,
buffer_pages: opts.buffer_pages || 5,
url: opts.dataurl,
method: opts.method || 'GET',
params: opts.params
});
this.set_css('multicolumns');
this.bind('min_threshold',
this.loader.previousPage.bind(this.loader))
this.bind('max_threshold',
this.loader.nextPage.bind(this.loader))
if (opts.viewer_tmpl){
this.viewer_tmpl = opts.viewer_tmpl;
}
if (opts.viewer_url){
this.get_tmpl_string();
}
}
loadData(params){
this.loader.loadData(params)
}
get_tmpl_string(){
fetch(this.opts.viewer_url)
.then(response => response.text())
.then(data => {
console.log('viewer_tmpl=', data);
this.viewer_tmpl = data
schedule_once(this.loader.loadData.bind(this.loader), 0.01);
});
}
clear_data(){
this.clear_widgets();
}
add_rows = async function(rows, direction){
for (var i=0;i<rows.length;i++){
await this.add_row(rows[i], direction);
}
}
add_row = async function(row, direction){
var s = obj_fmtstr(row, this.viewer_tmpl);
var desc = JSON.parse(s);
var w = await widgetBuild(desc, this);
if (! w){
console.log(desc, 'widgetBuild() failed...........');
return;
}
w.row_data = row;
var index = null;
if (direction == 'down') index = 0;
console.log('w=', w, 'this=', this);
this.add_widget(w, index);
}
}
Factory.register('DataViewer', DataViewer);

17
bricks/factory.js Executable file
View File

@ -0,0 +1,17 @@
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_();

213
bricks/form.js Normal file
View File

@ -0,0 +1,213 @@
class FormBody extends VBox {
/*
{
title:
description:
fields: [
{
"name":,
"label":,
"removable":
"icon":
"content":
},
...
]
}
*/
constructor(opts){
opts.width = '100%';
opts.scrollY = 'scroll';
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();
}
}
getValue(){
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;
}
extend(data, d);
}
return data;
}
async validation(){
var data = this.getValue();
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:
notoolbar:False,
cols:
dataurl:
toolbar:
submit_url:
fields
}
*/
constructor(opts){
super(opts);
this.body = new FormBody(opts);
this.add_widget(this.body);
if (! opts.notoolbar)
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.body.validation();
} else if (params.name == 'cancel'){
this.cancel();
} else if (params.name == 'reset'){
this.body.reset_data();
} else {
if (params.action){
f = buildEventHandler(this, params);
if (f) f(event);
}
}
}
getValue(){
return this.body.getValue();
}
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);

50
bricks/i18n.js Executable file
View File

@ -0,0 +1,50 @@
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(obj, itxt);
}
return txt;
}
is_loaded(lang){
if (objget(this.lang_msgs, lang)) return true;
return false;
}
setup_dict(dic, lang){
this.cur_lang = lang;
extend(this.lang_msgs, {lang:dic});
this.msgs = dic;
}
async change_lang(lang){
if (objget(this.lang_msgs, lang)){
this.msgs = objget(this.lang_msgs, 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);
}
}

94
bricks/image.js Executable file
View File

@ -0,0 +1,94 @@
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);

BIN
bricks/imgs/.DS_Store vendored Normal file

Binary file not shown.

BIN
bricks/imgs/add.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

BIN
bricks/imgs/delete.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
bricks/imgs/delete_node.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
bricks/imgs/down_arrow.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

BIN
bricks/imgs/down_dir.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

BIN
bricks/imgs/edit.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
bricks/imgs/folder.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

BIN
bricks/imgs/move_bottom.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

BIN
bricks/imgs/move_down.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
bricks/imgs/move_top.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

BIN
bricks/imgs/move_up.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
bricks/imgs/reset.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
bricks/imgs/right_arrow.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

BIN
bricks/imgs/submit.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

707
bricks/input.js Executable file
View File

@ -0,0 +1,707 @@
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);
extend(UiCheck.prototype, 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){
extend(params, 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 = objget(this.uitypes, 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);

10
bricks/install.bat Normal file
View File

@ -0,0 +1,10 @@
echo copy ..\dist\*.* e:\gadget\wwwroot\bricks
copy ..\dist\*.* e:\gadget\wwwroot\bricks
echo copy css\*.* e:\gadget\wwwroot\bricks\css
copy css\*.* e:\gadget\wwwroot\bricks\css
copy imgs\*.* e:\gadget\wwwroot\bricks\imgs
echo copy imgs\*.* e:\gadget\wwwroot\bricks\imgs
copy ..\docs\*.* e:\gadget\wwwroot\docs
echo copy ..\docs\*.* e:\gadget\wwwroot\docs
copy ..\examples\*.* e:\gadget\wwwroot\examples
echo copy ..\examples\*.* e:\gadget\wwwroot\examples

196
bricks/jsoncall.js Executable file
View File

@ -0,0 +1,196 @@
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",
};
extend(this.headers, 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){
extend(params,{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 = objget(fetchResult.headers, '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",
}
extend(this.headers, 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);

98
bricks/layout.js Executable file
View File

@ -0,0 +1,98 @@
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 = objcopy(this.children);
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('vcontainer');
}
}
class VFiller extends Layout {
constructor(options){
super(options);
this.set_css('vfiller');
}
}
class HBox extends Layout {
constructor(options){
super(options);
this.set_css('hcontainer');
}
}
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);

128
bricks/markdown_viewer.js Executable file
View File

@ -0,0 +1,128 @@
/*
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);

62
bricks/menu.js Executable file
View File

@ -0,0 +1,62 @@
/*
*/
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);

109
bricks/message.js Normal file
View File

@ -0,0 +1,109 @@
class BPopup 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');
VBox.prototype.add_widget.bind(this)(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 BMessage extends BPopup {
/*
{
title:
message:
params:
auto_open:
auto_dismiss:
archor:cc
timeout:
}
*/
constructor(opts){
super(opts);
var t = new Text({otext:this.opts.message,
i18n:true});
this.add_widget(t);
}
}
class BError extends BMessage {
constructor(opts){
super(opts);
this.set_css('error');
}
}
class PopupForm extends BPopup {
/*
{
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('BMessage', BMessage);
Factory.register('BError', BError);
Factory.register('PopupForm', PopupForm);

83
bricks/miniform.js Normal file
View File

@ -0,0 +1,83 @@
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 = objcopy(f);
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();
extend(d, v);
return d;
}
}
Factory.register('MiniForm', MiniForm);

4
bricks/minify_tools.txt Executable file
View File

@ -0,0 +1,4 @@
AJaxMin from Microsoft
https://developers.google.com/closure/compiler
YUI Compressor
Uglify-js

121
bricks/modal.js Executable file
View File

@ -0,0 +1,121 @@
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, objget(this.opts, '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 = objget(this.opts, '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);

View File

@ -0,0 +1,75 @@
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);

13
bricks/myoperator.js Executable file
View File

@ -0,0 +1,13 @@
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);
}
}

91
bricks/paging.js Normal file
View File

@ -0,0 +1,91 @@
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.initial();
}
initial(){
this.cur_page = -1;
this.buffer = {};
this.buffered_pages = 0;
this.total_record = -1;
this.cur_params = {};
}
async loadData(params){
this.initial();
this.widget.clear_data();
this.buffer = {};
if (!params) params = {};
var p = objcopy(this.params);
extend(p, 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 = objcopy(this.cur_params);
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.direction);
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();
}
}

11
bricks/registerfunction.js Executable file
View File

@ -0,0 +1,11 @@
class RegisterFunction {
constructor(){
this.rfs = {};
}
register(n, f){
extend(this.rfs, {n:f});
}
get(n){
return objget(this.rfs, n);
}
}

86
bricks/scroll.js Normal file
View File

@ -0,0 +1,86 @@
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){
widget.thresgold = true;
widget.dispatch('max_threshold');
return
}
if (!widget.threshold && dir < 0 && min_rate <= widget.min_threshold){
widget.thresgold = true;
widget.dispatch('min_threshold');
return
}
}
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);

27
bricks/stdhtml.html Normal file
View File

@ -0,0 +1,27 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/bricks/css/bricks.css" />
<link href="https://vjs.zencdn.net/8.3.0/video-js.min.css" rel="stylesheet">
<title>Standard Html</title>
</head>
<body>
<script src="https://vjs.zencdn.net/8.3.0/video.min.js"></script>
<script src="/bricks/bricks.js"></script>
<script>
const opts = {
"widget": {
"widgettype":"urlwidget",
"options":{
"url":"tree.json"
}
}
};
const app = new BricksApp(opts);
app.run();
</script>
</body>
</html>

125
bricks/tab.js Executable file
View File

@ -0,0 +1,125 @@
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 = objget(this.content_buffer, 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);

130
bricks/toolbar.js Executable file
View File

@ -0,0 +1,130 @@
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 = {};
extend(d, 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);

356
bricks/tree.js Executable file
View File

@ -0,0 +1,356 @@
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'] = objget(srcs, 'open', bricks_resource('imgs/down_arrow.png'));
sources['close'] = objget(srcs, '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 = objget(TypeIcons, dtype);
if (!icon && this.tree.opts.default_type){
icon = objget(TypeIcons, 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);

124
bricks/uitypesdef.js Executable file
View File

@ -0,0 +1,124 @@
class UiTypesDef {
constructor(opts){
this.opts = opts;
this.uitypes = {
}
}
set(uitype, viewBuilder, inputBuilder){
if (! this.uitypes[uitype]){
this.uitypes[uitype] = {};
}
this.uitypes[uitype].viewBuilder = viewBuilder;
this.uitypes[uitype].inputBuilder = inputBuilder;
}
get(uitype){
if (! this.uitypes[uitype]){
return (null, null);
}
return [this.uitypes[uitype].viewBuilder,
this.uitypes[uitype].inputBuilder];
}
getInputBuilder(uitype){
if (! this.uitypes[uitype]){
return Null;
}
return this.uitypes[uitype].inputBuilder;
}
getViewBuilder(uitype){
return this.uitypes[uitype].viewBuilder;
}
setViewBuilder(uitype, Builder){
if (! this.uitypes[uitype]){
this.uitypes[uitype] = {};
}
this.uitypes[uitype].viewBuilder = Builder;
}
setInputBuilder(uitype, Builder){
if (! this.uitypes[uitype]){
this.uitypes[uitype] = {};
}
this.uitypes[uitype].inputBuilder = Builder;
}
}
var uitypesdef = new UiTypesDef();
var viewFactory = function(desc, rec){
var uitype = desc.uitype;
var builder = uitypesdef.getViewBuilder(uitype) ||
uitypesdef.getViewBuilder('str');
if (! builder) return Null;
var w = builder(desc, rec);
return w;
}
var intputFactory = function(desc, rec){
var uitype = desc.uitype;
var builder = uitypesdef.getInputBuilder(uitype) ||
uitypesdef.getInputBuilder('str');
if (! builder) return Null;
return builder(desc, rec);
}
var buildText = function(text, halign){
if (['left', 'right'].indexOf(halign)< 0){
halign = 'left';
}
var w = new Text({
text:text || '',
overflow:'hidden',
wrap:true,
halign:'left'
});
return w;
}
var strViewBuilder = function(desc, rec){
var v = rec[desc.name];
return buildText(v, 'left');
}
uitypesdef.setViewBuilder('str', strViewBuilder);
var strInputBuilder = function(desc, rec) {
var v = rec[desc.name];
desc[value] = v;
return new UiStr(desc);
}
uitypesdef.setInputBuilder('str', strInputBuilder);
var passwordViewBuilder = function(desc, rec){
return new buildText('******');
}
uitypesdef.setViewBuilder('password', passwordViewBuilder);
var intViewBuilder = function(desc, rec){
var v = rec[desc.name] + '';
return buildText(v, 'right');
}
uitypesdef.setViewBuilder('int', intViewBuilder);
var floatViewBuilder = function(desc, rec){
var v = rec[desc.name];
v = v.toFixed(desc.dec_len||2)
v = v + '';
return buildText(v, 'right');
}
uitypesdef.setViewBuilder('float', floatViewBuilder);
var codeViewBuilder = function(desc, rec){
var opts = objcopy(desc)
if (opts.uiparams) extend(opts, opts.uiparams);
var name = desc.textFeild || 'text';
var v = rec[name];
if (! v) {
name = desc.valueField || 'value';
v = rec[name];
}
return buildText(v, 'left')
}
uitypesdef.setViewBuilder('code', codeViewBuilder);
var passwordInputBuilder = function(desc, rec){
return new UiPassword(desc);
}
uitypesdef.setInputBuilder('password', passwordInputBuilder);

281
bricks/utils.js Executable file
View File

@ -0,0 +1,281 @@
class _TypeIcons {
constructor(){
this.kv = {}
}
get(n, defaultvalue){
return objget(this.kv, 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 (objget(import_cache, 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 (objget(import_cache, 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;
}
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 = obj_fmtstr(o, '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);
}
}
var objcopy = function(obj){
var o = {}
for ( k in obj){
if (obj.hasOwnProperty(k)){
o[k] = obj[k];
}
}
return o;
}

72
bricks/video.js Executable file
View File

@ -0,0 +1,72 @@
/*
we use videojs for video play
https://videojs.com
*/
class Video extends JsWidget {
/*
{
vtype:
url:
autoplay:
}
*/
constructor(options){
super(options);
this.dom_element.type="application/vnd.apple.mpegurl";
this.set_css('video-js');
this.set_css('vjs-big-play-centered');
this.set_css('vjs-fluid');
this.cur_url = options.url;
this.cur_vtype = options.vtype;
console.log('here1');
schedule_once(this.create_player.bind(this), 0.1);
}
create(){
this.dom_element = document.createElement('video');
}
create_player(){
console.log('here2');
this.player = videojs(this.dom_element);
console.log('here3');
this._set_source();
}
_set_source(){
console.log('--------set source ---------');
}
set_source(url){
this.cur_url = url;
this._set_source();
}
}
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);
Factory.register('Video', Video);

49
bricks/views.js Normal file
View File

@ -0,0 +1,49 @@
/*
uitype:
str
password
int
float
tel
email
file
'check
checkbox
date
text
code
video
audio
*/
var ViewStr = function(desc){
var w = Text({
'text':desc.value,
'halign':'left'
})
if (desc.row_data)
w.desc_object = desc;
return w;
}
uitypesdef.setViewKlass('str', ViewStr);
var ViewPassword = function(desc){
var w = Text({
'text':"****",
'halign':'left'
})
if (desc.row_data)
w.data = desc.row_data;
return w;
}
class ViewType extends JsWidget {
constructor(opts){
super(opts)
}
}
class ViewStr extends ViewType {
}

310
bricks/widget.js Executable file
View File

@ -0,0 +1,310 @@
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]];
}
extend(this.dom_element.style, 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){
extend(this.dom_element.style, 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){
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);

52
bricks/wterm.js Normal file
View File

@ -0,0 +1,52 @@
/*
dependent on xterm.js
*/
class Wterm extends JsWidget {
/*
{
ws_url:
host:
ssh_port:
user:
}
*/
constructor(opts){
super(opts);
schedule_once(this.open.bind(this), 0.5);
}
async open(){
var term = new Terminal();
this.term = term;
term.open(this.dom_element);
var ws = new WebSocket(this.opts.ws_url);
console.log('FitAddon=', FitAddon);
this.fitAddon = new FitAddon.FitAddon()
term.loadAddon(this.fitAddon)
// this.fitAddon.fit()
this.bind('resize', this.term_resize.bind(this))
ws.onmessage = msg => {
term.write(JSON.parse(msg.data).data);
};
term.onData(function(key) {
//Enter
let msg = {
data:{data:key},
type:1
}
ws.send(key);
});
term.focus();
term.paste("ls -l\n");
}
term_resize(){
try {
this.fitAddon.fit();
} catch(e){
console.log('resize error', e);
}
}
}
Factory.register('Wterm', Wterm);

0
dist/README.md vendored Normal file
View File

BIN
docs/.DS_Store vendored Executable file

Binary file not shown.

21
docs/BoxLayout.md Executable file
View File

@ -0,0 +1,21 @@
# BoxLayout
Boxlayout is a one direction layout, it layout subwidgets on vertical direction or horizontal direction.
## use case
none
## inheritance
BoxLayout inherited from Layout
## options
```
{
orientation:
}
```
### orientation
orientation get value of 'vertical' or 'horizontal', means layout child widget in veritcal or horizontal direction
###
## method
none
## event
none

11
docs/HBox.md Executable file
View File

@ -0,0 +1,11 @@
# HBox
Hbox is a horizontal layout widget, it layout subwidgeta in horizontal direction
## use case
## inheritance
HBox inherited fromm BoxLayout
## options
## method
none
## event
none

41
docs/README.md Executable file
View File

@ -0,0 +1,41 @@
# Bricks documentation
A web application development platform to make the web application development more easy.
It let developments choose the UI components provide by bricks or provide by third parts to construct his or her application just like use bricks to build toys.
## Installation
## Use Case
## Widget List
[Widgets](widgets.md)
## UI Components
### Layout
Layout is the base Layout, please click [Layout](layout.md) for more detail.
### BoxLayout
BoxLayout is a one direction layout, for more information, please click [BoxLayout](boxlayout.md)
### HBox
HBox is a layout to layout subwidgets in horizontal direction, please click [HBox](hbox.md) for more information.
### VBox
VBox is a layout to layout its subwidgets in vertical direction, please click [VBox](vbox.md) for more information.
### Text
### Title1
### Title2
### Title3
### Title4
### Title5
### Title6
### Image
Image show image on you dom tree, please click [image](image.md) for more information about Image widget
### MarkdownViewer
a markdown document viewer widget, for more information, please see [markdownviewer](markdownviewer.md)
### VideoPlayer
a video player widget base on [indigo-player](https://github.com/matvp91/indigo-player), please look at [videoplayer](videoplayer.md) for more information.
### Modal
### Toolbar
toolbar component provide user a toobar service, for more information pleaseclick [toolbar](toolbar.md)

135
docs/cn/bricks.md Normal file
View File

@ -0,0 +1,135 @@
# widgetBuild
函数用于创建bricks框架的控件实例。
## 语法
var w = widgetBuild(desc, widget)
## 参数说明
### desc
desc是一个对象类型数据创建控件参数必须有"widgettype", "options"属性, 可选属性有"subwidegets""binds"
### widget
widget是一个控件实例用于解析desc中出现的url
## 返回值
* null 出错可能1widgettype类型未注册或不存在2json文件格式不对
* 新创建的控件实体
## 例子
tree.json
```
{
"widgettype":"Tree",
"options":{
"idField":"id",
"textField":"text",
"data":[
{
"id":1,
"text":"node1",
"is_leaf":false,
"children":[
{
"id":11,
"text":"node11",
"is_leaf":false,
"children":[
{
"id":112,
"text":"node.12",
"is_leaf":false,
"children":[
{
"id":1112,
"text":"node1112",
"is_leaf":true
},
{
"id":1113,
"text":"node113",
"is_leaf":true
}
]
},
{
"id":113,
"text":"node113",
"is_leaf":true
}
]
},
{
"id":12,
"text":"node12",
"is_leaf":true
},
{
"id":13,
"text":"node13",
"is_leaf":true
}
]
},
{
"id":2,
"text":"node2",
"is_leaf":false,
"children":[
{
"id":21,
"text":"node21",
"is_leaf":true
},
{
"id":22,
"text":"node22",
"is_leaf":true
},
{
"id":23,
"text":"node23",
"is_leaf":true
}
]
},
{
"id":3,
"text":"node3",
"is_leaf":true
}
]
}
}
```
tree.html
```
<html>
<head>
<link rel="stylesheet" href="/bricks/css/bricks.css" />
</head>
<body>
<script src="/bricks/bricks.js"></script>
<script>
const opts =
{
"widget": {
"widgettype":"urlwidget",
"options":{
"url":"tree.json"
}
}
}
;
const app = new BricksApp(opts);
app.run();
</script>
</body>
</html>
```
这是一个树形控件, 运行[这里](https://github.com/bricks/examples/tree.html)
更多的例子请看
[bricks控件例子](https://github.com/yumoqing/bricks/examples)

60
docs/cn/bricksapp.md Normal file
View File

@ -0,0 +1,60 @@
# BricksApp
BricksApp是Bricks框架的应用类 BricksApp用来初始化Bricks环境创建Bricks页面的根控件
并将生成的根控件插入的全局控件Body中
Body对应的dom_element为页面的“body”元素。
后续可以通过bricks_app全局变量来引用BricksApp实例。
## 构建选项
```
opts = {
login_url:
"charsize:
"language":
"i18n":{
"url":'rrr',
"default_lang":'en'
},
"widget":{
"widgettype":"Text",
"options":{
}
}
}
```
### login_url
 登录url按照后台设置当需要访问受控url时会从此URL返回的json数据创建登录窗体用户登录
### chartsize
字符大小缺省16px
### languange
页面使用的语言,两个字符的语言
### i18n
定义国际化数据获取路径url按照GET方式language作为参数像后台获取改语言的json格式的翻译数据。
### widget
根控件描述对象
## 属性
### opts
保存创建选项
### login_url
用于构建登录控件的url
### charsize
字符大小所有输入控件Text IconTitle都受此影响控件大小。
### lang
前端界面语言,选项指定或获取缺省语言
### textList
### i18n
### session_id
## 方法
## 事件

78
docs/cn/descjson.md Normal file
View File

@ -0,0 +1,78 @@
# 创建控件的Json文件格式说明
Bricks在服务器端使用Json文件格式存储控件描述文件前端获得json文件后转化为json对象并用词json对象调用widgetBuild函数创建Bricks控件。
控件描述json文件必须含有“widgettype” 和”options“两个属性。“subwidgets”属性用来定义此控件包含的子控件。“binds”用于定义此控件或其子控件的事件处理
## widgettype说明
widgettype是一个字符串属性。其值为Bricks中的所有控件类型或"urlwidget"
可用的控件类型可以在[控件类型清单](widgettypes.md)中查找
## options
对象类型每个控件有特定的options属性清参看每个控件的说明
## subwidgets
数组类型数组中的每个元素必须是一个对象类型数据与desc作用一样。
参见widgetBuild函数的desc说明
## binds
一个数组,在控件上定义一到多个事件绑定,数组中的每一个元素定义一个绑定,
Bricks支持5种数据绑定
每种绑定类型都支持下述属性
### wid
字符串类型或控件类型绑定事件的控件缺省是binds坐在的构造控件。
### event
字符串类型,事件名称, 绑定针对的事件字符串名称
### actiontype
绑定类型支持“urlwidget", "method", "script", "registerfunction", "event"
### conform
对象类型确认控件的options如存在则此绑定需要用户确认后再执行
### 获取实时数据作为参数
绑定任务获取实时数据作为参数,需要给定以下属性:
* datawidget字符串或控件类型获取实时数据的控件
* datamethod字符串类型控件的方法使用params作为参数调用
获取实时数据的方法
* datascript字符串类型 js脚本使用return返回数据
* dataparams参数
datamethod 优先datascript从datawidget控件中通过datamethod
### target
字符串或控件实例目标控件实例如果是字符串使用”getWidgetById“函数获得目标控件实例
### urlwidget绑定
urlwidget绑定需要一个options属性和一个mode属性在此属性中需要
* url字符串类型 获取desc数据的url
* mehtod字符串类型url调用的方法缺省”GET“
* params对象类型调用的参数
绑定创建的控件添加到target控件中
### method
需要指定target参数和method参数
* target类型为字符串或控件类型
如果是字符串使用“getWidgetById”函数获取控件实例。
* method字符串target实例的方法
* params传递给方法的参数
method绑定方法将事件绑定到target控件的一个方法并用params传递参数
### script
绑定脚本,此方法将事件绑定到一个脚本,支持以下属性
* script字符串脚本正文
* params对象类型脚本可以访问params变量来获取参数。
### registerfunction
事件绑定一个注册函数, 参看[RegisterFunction](registerfunction.md)
支持以下属性:
rfname字符串已注册的函数名称
params对象类型调用注册函数时作为参数传递给注册函数。
### event
绑定事件需指定target触发target对象的一个事件
支持以下属性
dispatch_event需触发的事件名
params传递给事件的参数处理函数可以使用evemt.params获得此参数
作为参数调用target实例的方法

58
docs/cn/index.md Normal file
View File

@ -0,0 +1,58 @@
# bricks
像搭积木一样的编写前端应用bricks希望提供开发者所需的底层显示控件开发应用时采用简单的组装和插拔方式完成应用的开发
使用bricks开发应用的好处
* 质量和性能可控开发者开发的应用质量和性能依赖bricks提供的提供底层控件。
* 简单的开发方式开发者以编写json数据文件的形式开发前端应用以后可以提供一个可视化开发平台
## Bricks控件描述文件
Bricks使用json格式描述控件具体格式要求请看[控件描述文件](descjson.md)
## BricksApp
Bricks应用类参见[BricksApp](bricksapp.md)
## bricks主要函数
### widgetBuild
创建控件函数,详细说明请看[这里](bricks.md)
### getWigetById
从DOM树查找控件详细说明请看[这里](bricks.md)
## bricks控件
Bricks的所有控件均继承自JsWidget控件间的继承关系请看[控件继承树](inherittree.md)
控件是bricks的基础每个控件实现了特定显示功能或布局能力开发者使用这些控件来构建应用
bricks的开发理念是应用开发可分为底层控件的开发以及操控底层控件来搭建应用。
通过这样分工,让精通底层开发的人员专心开发底层控件,而精通操控控件的人员专心搭建应用,从而提高开发效率和开发质量,希望大家参与进来一起做。
关于控件更多的信息,请看[控件](widgettypes.md)
## 依赖
如果要使用Markdown需要引用marked模块
如果用到图表, 需要引用echarts
* [Marked](https://github.com/markedjs/marked)
* [echarts](https://echarts.apache.org/zh/index.html)
## build
bricks 使用uglifyjs 压缩
在bricks目录下执行
```
./build.sh
```
命令执行完后在项目的dist目录下会生成bricks.js 和bricks.min.js并将examples和docs目录复制到本机到wwwroot目录
按照开发者本地web服务器的配置请修改build.sh的目标路径。
## 引用
```
<link rel="stylesheet" href="/bricks/css/bricks.css" />
<script src="/bricks/bricks.js"></script>
```
## 例子
配置好本地服务器后可以使用build.sh将bricks项目内容复制到本地网站后台之后在网站的examples目录中查看bricks提供的例子程序

102
docs/cn/inherittree.md Normal file
View File

@ -0,0 +1,102 @@
# Bricks控件继承树
```
JsWidget
|
--- AudioPlayer
|
--- Image
| |
| --- Icon
|
___ BlankIcon
|
___ Layout
| |
| --- VBox
| | |
| | --- Accordion
| | |
| | --- DataGrid
| | |
| | --- Form
| | |
| | --- MarkdownViewer
| | |
| | --- Menu
| | |
| | --- Message
| | | |
| | | --- Error
| | |
| | --- Popup
| | |
| | --- ScrollPanel
| | |
| | --- TreeNode
| | |
| | --- Tree
| | | |
| | | --- EditableTree
| | |
| | --- VideoPlayer
| |
| --- HBox
| | |
| | --- MiniForm
| |
| --- MultipleStateImage
| |
| --- Toolbar
| |
| --- TabPanel
| |
| --- Modal
| |
| --- HFiller
| |
| --- VFiller
| |
| --- Button
| |
| --- UiType
| | |
| | --- UiStr
| | | |
| | | --- UiPassword
| | | |
| | | --- UiInt
| | | | |
| | | | --- UiFloat
| | | |
| | | --- UiTel
| | | |
| | | --- UiEmail
| | | |
| | | --- UiFile
| | | |
| | | --- UiDate
| | | |
| | | --- UiAudio
| | | |
| | | --- UiVideo
| | |
| | --- UiCheck
| | |
| | --- UiCheckBox
| | |
| | --- UiText
| | |
| | --- UiCode
|
--- MdText
|
___ Video
|
--- TextBase
|
--- Text
|
--- Title1, Title2, Title3, Title4, Title5, Title6
```

25
docs/cn/jswidget.md Normal file
View File

@ -0,0 +1,25 @@
# JsWidget
Bricks框架的最原始的控件所有Bricks的控件均继承自JsWidget或其后代。
## 构建选项
## 属性
### dom_element
dom_element是控件对应的dom元素。
## 方法
### create()
创建dom元素的方法并将新创建的元素保存在dom_element属性中JsWidget创建一个”div“的元素 子类通过提供自己的create函数创建自己特定的dom元素。
### set_css(css_name, delflg)
增加或删除一个css类 当delflg为真时删除一个“css_name”css类否则增加一个“css_name”类
### sizable
sizable函数让当前类有按照bricks_app.charsize的大小设置自身大小的能力并在charsize变化时改变自身的大小
### change_fontsizets
change_fontsize函数由bricks_app.textsize_bigger()textsize_smaller函数调用用来改变控件的大小
## 事件

9
docs/cn/pattern.md Normal file
View File

@ -0,0 +1,9 @@
# XXX
## 创建选项
## 属性
## 方法
## 事件

45
docs/cn/widgettypes.md Normal file
View File

@ -0,0 +1,45 @@
# 控件列表
Bricks内置许多的显示控件所有显示控件都继承自JsWidget容器控件Layout几就继承自JsWidget其他的容器HBox VBox继承自Layout
# 控件继承树
# 控件清单
* [JsWidget](jswidget.md) 祖先控件,对应于<div>元素
* [Accordion](accordion.md):可折叠控件
* [AudioPlayer](audioplayer.md):音频播放器
* [Button](button.md):按钮控件
* [DataGrid](datagrid.md):数据表格控件
* [Form](form.md):输入表单控件
* [Image](image.md):图片控件
* [Icon](icon.md):图标控件
* [BlankIcon](blackicon.js):空白图标控件
* [HBox](hbox.md):横向布局器
* [VBox](vbox.md):纵向布局器
* [HFiller](hfiller.md):横向填充控件
* [VFiller](vfiller.md):纵向填充控件
* [MarkdownViewer](markdonwviewer.md)markdown文档显示控件
* [Menu](menu.md):菜单控件
* [Message](message.md):消息控件
* [Error](error.md):错误信息控件
* [PopupForm](popupform.md):弹出表单控件
* [MiniForm](meniform.md):多选单一输入表单
* [Modal](modal.md):独占控件
* [ModalForm](modalform.md):独占表单
* [MultipleStateImage](multiplestateimage.md):多状态图像显示控件
* [ScrollPanel](scrollpanel.md):滚动面板
* [VScrollPanel](vscrollpanel.md):纵向滚动面板
* [HScrollPanel](hscrollpanel.md):横向滚动面板
* [TabPanel][tabpanel.md):标签面板控件
* [Toolbar](toolbar.md):工具条控件
* [Tree](tree.md):树状显示控件
* [EditableTree](editabletree.md):可编辑树状显示控件
* [VideoPlayer](videoplayer.md):视频播放器控件
* [Text](text.md):正文控件
* [Title1](title1.md)标题1控件
* [Title2](title2.md)标题2控件
* [Title3](title3.md)标题3控件
* [Title4](title4.md)标题4控件
* [Title5](title5.md)标题5控件
* [Title6](title6.md)标题6控件
#

57
docs/image.md Executable file
View File

@ -0,0 +1,57 @@
# Image
Image widget show image
## use case
```
<html>
<head>
<link rel="stylesheet" href="../css/bricks.css">
</head>
<body>
<script src="../bricks.js"></script>
<script>
const opts =
{
"widget": {
"widgettype":"Image",
"options":{
"source":"https://cdn.pixabay.com/photo/2018/04/26/16/31/marine-3352341_960_720.jpg",
"height":"100%",
"width":"100%"
}
}
}
;
const app = new BricksApp(opts);
app.run();
</script>
</body>
</html>
```
the result is <br>
![image](image.png)
## inheritance
Image inherited from JsWidget
## options
```
{
"source":
"width":
"height":
}
```
### source
source is url to the image
### width
optional, default is 100%, specified the image's width
### height
optional, default is 100%, specified the image's height
## method
none
## event
none

BIN
docs/image.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 974 KiB

1
docs/index.md Symbolic link
View File

@ -0,0 +1 @@
README.md

16
docs/layout.md Executable file
View File

@ -0,0 +1,16 @@
# Layout
Layout is a layout widget for layout widgets inside it
## use case
none
## inheritance
Layout inherited from JsWidget
## options
## method
### add_widget
add_widget(w) append a widget name by w, to the caller
### remove_widget
remove_widget(w) remove widget named by w from caller's children
### clear_widgets
clear_widgets() remove all children widget.
## event
none

13
docs/markdownviewer.md Executable file
View File

@ -0,0 +1,13 @@
# MarkdownView
## Options attributes
MarkdownViewer has following attributes:
### md_url
md_url is a url address in which it is a markdown text file.
## Description
MarkdownViewer parses and show the markdown content child dom element. and ifound and modify element with tag name "a" to change href to '#' and add onclick action to get and show the markdown content inside this MarkdownViewer dom elementame "a" in markdown content.
## Event
none

7
docs/pw.md Executable file
View File

@ -0,0 +1,7 @@
# Widget Name
description of the Widget
## use case
## inheritance
## options
## method
## event

7
docs/text.md Executable file
View File

@ -0,0 +1,7 @@
# Text
Text is a widget to show text
## use case
## inheritance
## options
## method
## event

105
docs/toolbar.md Executable file
View File

@ -0,0 +1,105 @@
# Toolbar
The Toolbar widget provides a toolbar componets which provide a tool bar interface service, and you can add, delete tool at runtime dynamicly.
## use case
```
<html>
<head>
<link rel="stylesheet" href="../css/bricks.css">
</head>
<body>
<script src="../bricks.js"></script>
<script>
const opts =
{
"widget": {
"widgettype":"Toolbar",
"options":{
"target":null,
"orientation":"vertical",
"tools":[
{
"name":"c",
"Label":"C",
"icon":null
},
{
"name":"javascript",
"Label":"JavaScript",
"icon":null
},
{
"name":"php",
"Label":"PHP",
"icon":null
},
{
"name":"python",
"Label":"Python",
"icon":null
},
]
}
}
}
;
const app = new BricksApp(opts);
app.run();
</script>
</body>
</html>
```
and the result like this:<br>
![toolbar screenshot](toolbar.png)
## inheritance
Toolbar inherited from BoxLayout
## options
```
{
orientation:
tools:
}
```
### orientation
should be 'vertical' means tool widgets will arrange in vertical or 'horizontal' means tool widgets will arrange in horizontal
### tools:
a array contains a list of tool objects
### tool object
tool object has following properties:
```
{
icon:
name:
label:
removable:
}
```
#### icon is a image url, it is optional, if present, the tool will show a icon before the tool's text
#### name
name is a itendify field for the tools, it should be unique in this tools of this options scope.
#### label
label will translate by i18n for the language user used, and the result will be show. if label is miss, use the name of the tool object.
#### removable
if removable is true, the tool widget will append a delete image widget, and will remove the tool widget if user click the delete image widget.
## method
### createTool
createTool(toolOptions)
createTool create a tool widget and append the to end of the toolbar.
## event
toolbar has some events other widgets can bind to, they are:
### ready
after constructor call finished, and all the tools built, toolbar will fire this event
### command
after user click on one of the tool widgets, toolbar will fire this event with the tool's options used to construct it as the parameters, can get the parameters using "event.params"
.
### remove
after user click delete image widget, toolbar delete the tool widget from toolbar, and fire this event.

BIN
docs/toolbar.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

11
docs/vbox.md Executable file
View File

@ -0,0 +1,11 @@
# VBox
Vbox is a vertical layout widget, it layout subwidgeta in vertical direction
## use case
## inheritance
VBox inherited fromm BoxLayout
## options
## method
none
## event
none

87
docs/videoplayer.md Executable file
View File

@ -0,0 +1,87 @@
# VideoPlayer
## Usage
### play a mp4 file
```
<html>
<head>
</head>
<body>
<script src="../bricks.js"></script>
<script>
const opts =
{
"widget": {
"id":"videoplayer",
"widgettype":"VideoPlayer",
"options":{
"autoplay":true,
"url":"http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4",
"type":"mp4"
}
}
}
;
const app = new BricksApp(opts);
app.run();
</script>
</body>
</html>
```
### play a m3u8 stream
```
<html>
<head>
</head>
<body>
<script src="https://cdn.jsdelivr.net/npm/indigo-player@1/lib/indigo-player.js"></script>
<script src="../bricks.js"></script>
<script>
/*
https://abc-iview-mediapackagestreams-2.akamaized.net/out/v1/6e1cc6d25ec0480ea099a5399d73bc4b/index.m3u8
https://cbcnewshd-f.akamaihd.net/i/cbcnews_1@8981/index_2500_av-p.m3u8
*/
const opts =
{
"widget": {
"id":"videoplayer",
"widgettype":"VideoPlayer",
"options":{
"autoplay":true,
"source":"https://abc-iview-mediapackagestreams-2.akamaized.net/out/v1/6e1cc6d25ec0480ea099a5399d73bc4b/index.m3u8",
"srctype":"hls"
}
}
}
;
const app = new BricksApp(opts);
app.run();
</script>
</body>
</html>
```
you will see ti like this
![example video](m3u8.png "see it?")
## options
to be a VideoPlayer instance, you need to provide a options with contains
following attributes
### source
the url of a video resouce, VideoPlayer can play mp4, m3u8 video
### srctype
specify the source type, should be 'mp4' or 'hls' for m3u8 'dash' for play a Dash manifest
## play avi file
!v[sample video](http://kimird.com/video/sample-avi-file.avi)
## play flv file
!v[sample video](http://kimird.com/video/sample-flv-file.flv)
## play mkv file
!v[sample video](http://kimird.com/video/sample-mkv-file.mkv)
## play mov file
!v[sample video](http://kimird.com/video/sample-mov-file.mov)
## play mp4 file
!v[sample video](http://kimird.com/video/sample-mp4-file.mp4)
## play webm file
!v[sample video](http://kimird.com/video/sample-webm-file.webm)
## play m3u8 file
!v[abc news TV](https://abc-iview-mediapackagestreams-2.akamaized.net/out/v1/6e1cc6d25ec0480ea099a5399d73bc4b/index.m3u8)

49
docs/widgets.md Normal file
View File

@ -0,0 +1,49 @@
# Bricks widgets
## Accordion
## AudioPlayer
## BlankIcon
## BoxLayout
[boxlayout](BoxLayout.md)
## Button
## DataGrid
## EditableTree
## Error
## Form
## HBox
[hbox](HBox.md)
## HFiller
## HScrollPanel
## Icon
## Image
[image](image.md)
## MarkdownViewer
(markdown viewer](markdownviewer.md)
## Menu
## Message
## MiniForm
## Modal
## ModalForm
## MultipleStateImage
## PopupForm
## TabForm
## TabPanel
## Text
[text](text.md)
## Title1
## Title2
## Title3
## Title4
## Title5
## Title6
## Toolbar
[Toolbar](toolbar.md)
## Tree
## VBox
[VBox](vbox.md)
## VFiller
## VScrollPanel
## Video
## VideoPlayer
[VideoPlayer](videoplayer.md)
## XTerminal

BIN
examples/.DS_Store vendored Executable file

Binary file not shown.

52
examples/accordion.html Normal file
View File

@ -0,0 +1,52 @@
<html>
<head>
<link rel="stylesheet" href="/bricks/css/bricks.css">
</head>
<body>
<script src="/bricks/bricks.js"></script>
<script>
const opts =
{
"widget": {
"widgettype":"Accordion",
"options":{
"item_size":'28px',
"items":[
{
"name":"test1",
"icon":"/imgs/t.png",
"text":"accordion 1 test",
"content":{
"widgettype":"Text",
"options":{
"text":"text content 1",
"i81n":true
}
}
},
{
"name":"test2",
"icon":"/imgs/t.png",
"text":"accordion 2 test",
"content":{
"widgettype":"Text",
"options":{
"text":"text content 2",
"i81n":true
}
}
}
]
}
}
}
;
const app = new BricksApp(opts);
app.run();
</script>
</body>
</html>

28
examples/audio.html Executable file
View File

@ -0,0 +1,28 @@
<html>
<head>
</head>
<body>
<script src="http://kimird.com/bricks/bricks.js"></script>
<script>
/*
https://abc-iview-mediapackagestreams-2.akamaized.net/out/v1/6e1cc6d25ec0480ea099a5399d73bc4b/index.m3u8
https://cbcnewshd-f.akamaihd.net/i/cbcnews_1@8981/index_2500_av-p.m3u8
*/
const opts =
{
"widget": {
"id":"audioplayer",
"widgettype":"AudioPlayer",
"options":{
"autoplay":true,
"url":"http://kimird.com/songs/sample-mp3-file.mp3"
}
}
}
;
const app = new BricksApp(opts);
app.run();
</script>
</body>
</html>

8
examples/bricks.tmpl Executable file
View File

@ -0,0 +1,8 @@
{% if not webbricks or webbricks==0 %}
{% include "header.html" %}
{% endif -%}
{% include content %}
{% if not webbricks %}
{% include "footer.html" %}
{% endif -%}

57
examples/button.html Normal file
View File

@ -0,0 +1,57 @@
<html>
<head>
<link rel="stylesheet" href="/bricks/css/bricks.css">
</head>
<body>
<script src="/bricks/bricks.js"></script>
<script>
const opts =
{
"widget": {
"widgettype":"HBox",
"options":{
"height":"auto",
"width":"100%"
},
"subwidgets":[
{
"widgettype":"Button",
"options":{
"name":"test",
"icon":"/bricks/imgs/submit.png",
"label":"test once",
"tooltip":"This is a joke too",
"nonepack":true,
"orientation":"horizontal"
}
},
{
"widgettype":"Button",
"options":{
"name":"test",
"icon":"/bricks/imgs/reset.png",
"tooltip":"This is a joke",
"label":"test twice",
"nonepack":true,
"orientation":"horizontal"
}
}
],
"binds":[
{
"wid":"self",
"event":"click",
"actiontype":"script",
"script":"alert('HBox clicked');"
}
]
}
}
;
const app = new BricksApp(opts);
app.run();
</script>
</body>
</html>

344
examples/config.js Executable file
View File

@ -0,0 +1,344 @@
alert('here ....');
const langs = [
{ title: 'English', path: '/home', matchPath: /^\/(home|ecosystem|support)/ },
{ title: '简体中文', path: '/zh-Hans/', matchPath: /^\/zh-Hans/ },
];
docute.init({
landing: 'landing.html',
title: 'APlayer',
repo: 'DIYgod/APlayer',
twitter: 'DIYgod',
'edit-link': 'https://github.com/MoePlayer/APlayer/tree/master/docs',
nav: {
default: [
{
title: 'Home', path: '/home'
},
{
title: 'Ecosystem', path: '/ecosystem'
},
{
title: 'Support APlayer', path: '/support'
},
{
title: 'Languages', type: 'dropdown', items: langs
}
],
'zh-Hans': [
{
title: '首页', path: '/zh-Hans/'
},
{
title: '生态', path: '/zh-Hans/ecosystem'
},
{
title: '支持 APlayer', path: '/zh-Hans/support'
},
{
title: '选择语言', type: 'dropdown', items: langs
}
],
},
plugins: [
docsearch({
apiKey: '',
indexName: 'aplayer',
tags: ['english', 'zh-Hans'],
url: 'https://aplayer.js.org'
}),
evanyou(),
player()
]
});
function player () {
return function (context) {
context.event.on('landing:updated', function () {
console.log('landing:updated');
clearPlayer();
aplayer0();
aplayer1();
});
context.event.on('content:updated', function () {
console.log('content:updated');
clearPlayer();
for (let i = 0; i < document.querySelectorAll('.load').length; i++) {
document.querySelectorAll('.load')[i].addEventListener('click', function () {
window[this.parentElement.id] && window[this.parentElement.id]();
});
}
});
};
}
function clearPlayer () {
for (let i = 0; i < 10; i++) {
if (window['ap' + i]) {
window['ap' + i].destroy();
}
}
}
function aplayer1 () {
window.ap1 = new APlayer({
container: document.getElementById('aplayer1'),
theme: '#F57F17',
lrcType: 3,
audio: [{
name: '光るなら',
artist: 'Goose house',
url: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/hikarunara.mp3',
cover: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/hikarunara.jpg',
lrc: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/hikarunara.lrc',
theme: '#ebd0c2'
}, {
name: 'トリカゴ',
artist: 'XX:me',
url: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/darling.mp3',
cover: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/darling.jpg',
lrc: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/darling.lrc',
theme: '#46718b'
}, {
name: '前前前世',
artist: 'RADWIMPS',
url: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/yourname.mp3',
cover: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/yourname.jpg',
lrc: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/yourname.lrc',
theme: '#505d6b'
}]
});
}
function aplayer0 () {
window.ap0 = new APlayer({
container: document.getElementById('aplayer0'),
fixed: true,
lrcType: 3,
audio: [{
name: '前前前世',
artist: 'RADWIMPS',
url: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/yourname.mp3',
cover: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/yourname.jpg',
lrc: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/yourname.lrc',
theme: '#505d6b'
}, {
name: 'トリカゴ',
artist: 'XX:me',
url: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/darling.mp3',
cover: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/darling.jpg',
lrc: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/darling.lrc',
theme: '#46718b'
}, {
name: '光るなら',
artist: 'Goose house',
url: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/hikarunara.mp3',
cover: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/hikarunara.jpg',
lrc: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/hikarunara.lrc',
theme: '#ebd0c2'
}]
});
}
function aplayer2 () {
window.ap2 = new APlayer({
container: document.getElementById('aplayer2'),
audio: [{
name: '光るなら',
artist: 'Goose house',
url: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/hikarunara.mp3',
cover: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/hikarunara.jpg',
theme: '#ebd0c2'
}]
});
}
function aplayer3 () {
window.ap3 = new APlayer({
container: document.getElementById('aplayer3'),
mini: false,
autoplay: false,
loop: 'all',
order: 'random',
preload: 'auto',
volume: 0.7,
mutex: true,
listFolded: false,
listMaxHeight: 90,
lrcType: 3,
audio: [{
name: '光るなら',
artist: 'Goose house',
url: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/hikarunara.mp3',
cover: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/hikarunara.jpg',
lrc: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/hikarunara.lrc',
theme: '#ebd0c2'
}, {
name: 'トリカゴ',
artist: 'XX:me',
url: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/darling.mp3',
cover: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/darling.jpg',
lrc: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/darling.lrc',
theme: '#46718b'
}, {
name: '前前前世',
artist: 'RADWIMPS',
url: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/yourname.mp3',
cover: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/yourname.jpg',
lrc: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/yourname.lrc',
theme: '#505d6b'
}]
});
}
function aplayer4 () {
window.ap4 = new APlayer({
container: document.getElementById('aplayer4'),
lrcType: 3,
audio: [{
name: '光るなら',
artist: 'Goose house',
url: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/hikarunara.mp3',
cover: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/hikarunara.jpg',
lrc: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/hikarunara.lrc',
theme: '#ebd0c2'
}]
});
}
function aplayer5 () {
window.ap5 = new APlayer({
container: document.getElementById('aplayer5'),
lrcType: 3,
audio: [{
name: '光るなら',
artist: 'Goose house',
url: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/hikarunara.mp3',
cover: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/hikarunara.jpg',
lrc: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/hikarunara.lrc',
theme: '#ebd0c2'
}, {
name: 'トリカゴ',
artist: 'XX:me',
url: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/darling.mp3',
cover: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/darling.jpg',
lrc: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/darling.lrc',
theme: '#46718b'
}, {
name: '前前前世',
artist: 'RADWIMPS',
url: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/yourname.mp3',
cover: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/yourname.jpg',
lrc: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/yourname.lrc',
theme: '#505d6b'
}]
});
}
function aplayer6 () {
window.ap6 = new APlayer({
container: document.getElementById('aplayer6'),
mini: true,
audio: [{
name: '光るなら',
artist: 'Goose house',
url: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/hikarunara.mp3',
cover: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/hikarunara.jpg',
theme: '#ebd0c2'
}]
});
}
function aplayer7 () {
window.ap7 = new APlayer({
container: document.getElementById('aplayer7'),
audio: [{
name: '光るなら(HLS)',
artist: 'Goose house',
url: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/hls/hikarunara.m3u8',
cover: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/hikarunara.jpg',
theme: '#ebd0c2',
type: 'hls'
}, {
name: '光るなら',
artist: 'Goose house',
url: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/hikarunara.mp3',
cover: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/hikarunara.jpg',
theme: '#ebd0c2'
}, {
name: 'トリカゴ',
artist: 'XX:me',
url: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/darling.mp3',
cover: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/darling.jpg',
theme: '#46718b'
}, {
name: '前前前世',
artist: 'RADWIMPS',
url: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/yourname.mp3',
cover: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/yourname.jpg',
theme: '#505d6b'
}]
});
}
function aplayer8 () {
window.ap8 = new APlayer({
container: document.getElementById('aplayer8'),
theme: '#e9e9e9',
audio: [{
name: '光るなら',
artist: 'Goose house',
url: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/hikarunara.mp3',
cover: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/hikarunara.jpg',
}, {
name: 'トリカゴ',
artist: 'XX:me',
url: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/darling.mp3',
cover: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/darling.jpg',
}, {
name: '前前前世',
artist: 'RADWIMPS',
url: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/yourname.mp3',
cover: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/yourname.jpg',
}]
});
const colorThief = new ColorThief();
window.ap8.on('switchaudio', function (index) {
if (!window.ap8.options.audio[index].theme) {
colorThief.getColorAsync(window.ap8.options.audio[index].cover, function (color) {
window.ap8.theme(`rgb(${color[0]}, ${color[1]}, ${color[2]})`, index);
});
}
});
}
function aplayer9 () {
window.ap9 = new APlayer({
container: document.getElementById('aplayer9'),
fixed: true,
lrcType: 3,
audio: [{
name: '光るなら',
artist: 'Goose house',
url: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/hikarunara.mp3',
cover: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/hikarunara.jpg',
lrc: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/hikarunara.lrc',
theme: '#ebd0c2'
}, {
name: 'トリカゴ',
artist: 'XX:me',
url: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/darling.mp3',
cover: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/darling.jpg',
lrc: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/darling.lrc',
theme: '#46718b'
}, {
name: '前前前世',
artist: 'RADWIMPS',
url: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/yourname.mp3',
cover: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/yourname.jpg',
lrc: 'https://cn-south-17-aplayer-46154810.oss.dogecdn.com/yourname.lrc',
theme: '#505d6b'
}]
});
}

22
examples/datagrid.html Normal file
View File

@ -0,0 +1,22 @@
<html>
<head>
<link rel="stylesheet" href="/bricks/css/bricks.css">
</head>
<body>
<script src="/bricks/bricks.js"></script>
<script>
const opts =
{
"widget": {
"widgettype":"urlwidget",
"options":{
"url":"users.bcrud"
}
}
};
const app = new BricksApp(opts);
app.run();
</script>
</body>
</html>

893
examples/datagrid.json Normal file
View File

@ -0,0 +1,893 @@
{
"widgettype": "DataGrid",
"options": {
"data": [
{
"id": 0,
"tv_group": "中央电视台0211s大多数大多",
"tv_name": "cctv1",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_grade123":"1233333333333333",
"channel_grade456":"45666666666666",
"channel_code": 0
},
{
"id": 1,
"tv_group": "中央电视台1但是对方的三个都是人生",
"tv_name": "cctv2",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 1
},
{
"id": 2,
"tv_group": "中央电视台2是的SV程序多渠道",
"tv_name": "cctv3",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 2
},
{
"id": 3,
"tv_group": "中央电视台3广发鬼地方个大概的",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
},
{
"id": 3,
"tv_group": "中央电视台3三大SV徐秩序发生",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
},
{
"id": 3,
"tv_group": "中央电视台3所发生的公司根深蒂固",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
},
{
"id": 4,
"tv_group": "中央电视台3发顺丰第三方士大夫",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
},
{
"id": 5,
"tv_group": "中央电视台3发送到仨女的是否",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
},
{
"id": 6,
"tv_group": "中央电视台3胜多负少的城市的城市",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
},
{
"id": 7,
"tv_group": "中央电视台3阿发撒撒发",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
},
{
"id": 8,
"tv_group": "中央电视台3是撒让人本地服务",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
},
{
"id": 9,
"tv_group": "中央电视台3VS对方水电费",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
},
{
"id": 10,
"tv_group": "中央电视台3八分熟",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
},
{
"id": 11,
"tv_group": "中央电视台3",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
},
{
"id": 12,
"tv_group": "中央电视台3",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
},
{
"id": 13,
"tv_group": "中央电视台3",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
},
{
"id": 14,
"tv_group": "中央电视台3",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
},
{
"id": 15,
"tv_group": "中央电视台3",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
},
{
"id": 16,
"tv_group": "中央电视台3",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
},
{
"id": 17,
"tv_group": "中央电视台3",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
},
{
"id": 18,
"tv_group": "中央电视台3",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
},
{
"id": 18,
"tv_group": "中央电视台3",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
},
{
"id": 18,
"tv_group": "中央电视台3",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
},
{
"id": 18,
"tv_group": "中央电视台3",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
},
{
"id": 18,
"tv_group": "中央电视台3",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
},
{
"id": 18,
"tv_group": "中央电视台3",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
},
{
"id": 18,
"tv_group": "中央电视台3",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
},
{
"id": 18,
"tv_group": "中央电视台3",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
},
{
"id": 18,
"tv_group": "中央电视台3",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
},
{
"id": 18,
"tv_group": "中央电视台3",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
},
{
"id": 18,
"tv_group": "中央电视台3",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
},
{
"id": 18,
"tv_group": "中央电视台3",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
},
{
"id": 18,
"tv_group": "中央电视台3",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
},
{
"id": 18,
"tv_group": "中央电视台3",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
},
{
"id": 18,
"tv_group": "中央电视台3",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
},
{
"id": 18,
"tv_group": "中央电视台3",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
},
{
"id": 18,
"tv_group": "中央电视台3",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
},
{
"id": 18,
"tv_group": "中央电视台3",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
},
{
"id": 18,
"tv_group": "中央电视台3",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
},
{
"id": 18,
"tv_group": "中央电视台3",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
},
{
"id": 18,
"tv_group": "中央电视台3",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
},
{
"id": 18,
"tv_group": "中央电视台3",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
},
{
"id": 18,
"tv_group": "中央电视台3",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
},
{
"id": 18,
"tv_group": "中央电视台3",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
},
{
"id": 18,
"tv_group": "中央电视台3",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
},
{
"id": 18,
"tv_group": "中央电视台3",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
},
{
"id": 18,
"tv_group": "中央电视台3",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
},
{
"id": 18,
"tv_group": "中央电视台3",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
},
{
"id": 18,
"tv_group": "中央电视台3",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
},
{
"id": 18,
"tv_group": "中央电视台3",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
},
{
"id": 18,
"tv_group": "中央电视台3",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
},
{
"id": 19,
"tv_group": "中央电视台3",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
},
{
"id": 20,
"tv_group": "中央电视台3",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
},
{
"id": 21,
"tv_group": "中央电视台3",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
}, {
"id": 22,
"tv_group": "中央电视台3",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
}, {
"id": 23,
"tv_group": "中央电视台3",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
}, {
"id": 24,
"tv_group": "中央电视台3",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
}, {
"id": 25,
"tv_group": "中央电视台3",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
",channel_code": 3
}, {
"id": 26,
"tv_group": "中央电视台3",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
}, {
"id": 27,
"tv_group": "中央电视台3",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
}, {
"id": 28,
"tv_group": "中央电视台3",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
}, {
"id": 29,
"tv_group": "中央电视台3",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
},
{
"id": 30,
"tv_group": "中央电视台3",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
},
{
"id": 31,
"tv_group": "中央电视台3",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
},
{
"id": 32,
"tv_group": "中央电视台3",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
},
{
"id": 33,
"tv_group": "中央电视台3",
"tv_name": "cctv4",
"logo_url": "baidu",
"url": "http://www.baidu.com",
"media_type": "广告,电影",
"download_date": "2022.12.12 12:02:02",
"channel_delay": "3000",
"channel_grade": "一级",
"channel_code": 3
}
],
"title": "Test",
"description": "get all iptv channels",
"fields": [
{
"name": "id",
"uitype": "str",
"datatype": "str",
"label": "编号",
"width": 210.0,
"readonly": true,
"freeze": true
},
{
"name": "tv_group",
"uitype": "str",
"datatype": "str",
"label": "频道组",
"width": 210.0,
"readonly": true,
"freeze":true
},
{
"name": "tv_name",
"uitype": "str",
"datatype": "str",
"label": "频道名称",
"width": 210.0,
"readonly": true,
"freeze": true
},
{
"name": "logo_url",
"uitype": "str",
"datatype": "str",
"label": "台标url",
"width": 210.0,
"readonly": true
},
{
"name": "url",
"uitype": "str",
"datatype": "str",
"label": "url",
"width": 210.0,
"readonly": true
},
{
"name": "media_type",
"uitype": "str",
"datatype": "str",
"label": "媒体类型",
"width": 210.0,
"readonly": true
},
{
"name": "download_date",
"uitype": "date",
"datatype": "str",
"label": "下载日期",
"width": 140.0,
"readonly": true
},
{
"name": "channel_delay",
"uitype": "int",
"datatype": "long",
"label": "频道延迟",
"width": 210.0,
"dec": 0,
"readonly": true
},
{
"name": "channel_grade",
"uitype": "int",
"datatype": "long",
"label": "频道等级",
"width": 210.0,
"dec": 0,
"readonly": true
},
{
"name": "channel_grade123",
"uitype": "int",
"datatype": "long",
"label": "频道等级123",
"width": 210.0,
"dec": 0,
"readonly": true
},
{
"name": "channel_grade456",
"uitype": "int",
"datatype": "long",
"label": "频道等级456",
"width": 210.0,
"dec": 0,
"readonly": true
}
]
}
}

22
examples/datagrid1.html Normal file
View File

@ -0,0 +1,22 @@
<html>
<head>
<link rel="stylesheet" href="/bricks/css/bricks.css">
</head>
<body>
<script src="/bricks/bricks.js"></script>
<script>
const opts =
{
"widget": {
"widgettype":"urlwidget",
"options":{
"url":"iptvchannels.bcrud"
}
}
};
const app = new BricksApp(opts);
app.run();
</script>
</body>
</html>

24
examples/docs.html Executable file
View File

@ -0,0 +1,24 @@
<html>
<head>
</head>
<body>
<script src="/marked.js"></script>
<script src="http://kimird.com/bricks/bricks.js"></script>
<script>
const opts =
{
"widget": {
"widgettype":"MarkdownViewer",
"options":{
"navigator":true,
"md_url":"../docs/index.md"
}
}
}
;
const app = new BricksApp(opts);
app.run();
</script>
</body>
</html>

1
examples/echo.ws Normal file
View File

@ -0,0 +1 @@
return 'resp:' + ws_data

View File

@ -0,0 +1,24 @@
<html>
<head>
<link rel="stylesheet" href="/bricks/css/bricks.css">
</head>
<body>
<script src="/bricks/bricks.js"></script>
<script>
const opts =
{
"widget": {
"widgettype":"urlwidget",
"options":{
"url":"editabletree.json"
}
}
}
;
const app = new BricksApp(opts);
app.run();
</script>
</body>
</html>

View File

@ -0,0 +1,84 @@
{
"widgettype":"EditableTree",
"options":{
"idField":"id",
"admin":{},
"textField":"text",
"data":[
{
"id":1,
"text":"node1",
"is_leaf":false,
"children":[
{
"id":11,
"text":"node11",
"is_leaf":false,
"children":[
{
"id":112,
"text":"node.12",
"is_leaf":false,
"children":[
{
"id":1112,
"text":"node1112",
"is_leaf":true
},
{
"id":1113,
"text":"node113",
"is_leaf":true
}
]
},
{
"id":113,
"text":"node113",
"is_leaf":true
}
]
},
{
"id":12,
"text":"node12",
"is_leaf":true
},
{
"id":13,
"text":"node13",
"is_leaf":true
}
]
},
{
"id":2,
"text":"node2",
"is_leaf":false,
"children":[
{
"id":21,
"text":"node21",
"is_leaf":true
},
{
"id":22,
"text":"node22",
"is_leaf":true
},
{
"id":23,
"text":"node23",
"is_leaf":true
}
]
},
{
"id":3,
"text":"node3",
"is_leaf":true
}
]
}
}

105
examples/error.html Normal file
View File

@ -0,0 +1,105 @@
<html>
<head>
<link rel="stylesheet" href="/bricks/css/bricks.css">
</head>
<body>
<script src="/bricks/bricks.js"></script>
<script>
const opts =
{
"widget": {
"widgettype":"Tree",
"options":{
"idField":"id",
"textField":"text",
"data":[
{
"id":1,
"text":"node1",
"is_leaf":false,
"children":[
{
"id":11,
"text":"node11",
"is_leaf":false,
"children":[
{
"id":112,
"text":"node.12",
"is_leaf":false,
"children":[
{
"id":1112,
"text":"node1112",
"is_leaf":true
},
{
"id":1113,
"text":"node113",
"is_leaf":true
}
]
},
{
"id":113,
"text":"node113",
"is_leaf":true
}
]
},
{
"id":12,
"text":"node12",
"is_leaf":true
},
{
"id":13,
"text":"node13",
"is_leaf":true
}
]
},
{
"id":2,
"text":"node2",
"is_leaf":false,
"children":[
{
"id":21,
"text":"node21",
"is_leaf":true
},
{
"id":22,
"text":"node22",
"is_leaf":true
},
{
"id":23,
"text":"node23",
"is_leaf":true
}
]
},
{
"id":3,
"text":"node3",
"is_leaf":true
}
]
}
}
}
;
const app = new BricksApp(opts);
app.run();
var m = new Error({
"title":"TEst Message",
"message":"This is a test message",
"auto_open":true,
"timeout":30
})
</script>
</body>
</html>

2
examples/fileupload.dspy Executable file
View File

@ -0,0 +1,2 @@
print(params_kw)
return params_kw

Some files were not shown because too many files have changed in this diff Show More