Merge branch 'main' of git.kaiyuancloud.cn:yumoqing/bricks

This commit is contained in:
yumoqing 2025-04-08 22:02:35 +08:00
commit 63564940dc
75 changed files with 2855 additions and 556 deletions

View File

@ -0,0 +1,2 @@
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.FitAddon=t():e.FitAddon=t()}(window,function(){return function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}return r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=0)}([function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=function(){function e(){}return e.prototype.activate=function(e){this._terminal=e},e.prototype.dispose=function(){},e.prototype.fit=function(){var e=this.proposeDimensions();if(e&&this._terminal){var t=this._terminal._core;this._terminal.rows===e.rows&&this._terminal.cols===e.cols||(t._renderService.clear(),this._terminal.resize(e.cols,e.rows))}},e.prototype.proposeDimensions=function(){if(this._terminal&&this._terminal.element&&this._terminal.element.parentElement){var e=this._terminal._core,t=window.getComputedStyle(this._terminal.element.parentElement),r=parseInt(t.getPropertyValue("height")),n=Math.max(0,parseInt(t.getPropertyValue("width"))),o=window.getComputedStyle(this._terminal.element),i=r-(parseInt(o.getPropertyValue("padding-top"))+parseInt(o.getPropertyValue("padding-bottom"))),a=n-(parseInt(o.getPropertyValue("padding-right"))+parseInt(o.getPropertyValue("padding-left")))-e.viewport.scrollBarWidth;return{cols:Math.max(2,Math.floor(a/e._renderService.dimensions.actualCellWidth)),rows:Math.max(1,Math.floor(i/e._renderService.dimensions.actualCellHeight))}}},e}();t.FitAddon=n}])});
//# sourceMappingURL=xterm-addon-fit.js.map

75
bricks/asr.js Normal file
View File

@ -0,0 +1,75 @@
var bricks = window.bricks || {};
bricks.ASRClient = class extends bricks.VBox {
/*
options:
{
start_icon:record.png,
stop_icon:stop.png
ws_url:
icon_options
ws_params:
}
event:
start: start recording, no params
stop: stop recording, no params
transtext: recognised text, params={
"content":
"speaker":
"start":
"end":
}
*/
constructor(opts){
super(opts);
var icon_options = this.icon_options || {};
icon_options.url = this.start_icon || bricks_resource('imgs/start_recording.png');
this.icon = new bricks.Icon(icon_options);
this.status = 'stop';
this.icon.bind('click', this.toggle_button.bind(this));
this.add_widget(this.icon);
this.socket = new WebSocket(this.ws_url);
this.socket.onmessage = this.response_data.bind(this);
this.bind('transtext', this.response_log.bind(this));
}
response_log(event){
console.log('response data=', event.params);
}
toggle_button(){
if (this.status == 'stop'){
this.icon.set_url(this.start_icon||bricks_resource('imgs/stop_recording.png'));
this.status = 'start';
this.start_recording();
} else {
this.icon.set_url(this.stop_icon||bricks_resource('imgs/start_recording.png'));
this.status = 'stop';
this.stop_recording();
}
}
async start_recording() {
this.stream = await navigator.mediaDevices.getUserMedia({ audio: true });
this.mediaRecorder = new MediaRecorder(this.stream);
this.mediaRecorder.ondataavailable = (event) => {
if (event.data.size > 0) {
// 将音频数据通过 WebSocket 发送到服务器
blobToBase64(event.data).then((b64str) => {
var d = objcopy(this.ws_params);
d.type = 'audiobuffer';
d.data = b64str;
this.socket.send(JSON.stringify(d));
}).catch((error) => {
console.log('Error', error);
});
}
}
this.mediaRecorder.start(1000); // 每 1 秒发送一次数据
}
stop_recording(){
this.mediaRecorder.stop();
}
response_data(event){
var d = JSON.parse(event.data);
this.dispatch('transtext', d);
}
}
bricks.Factory.register('ASRClient', bricks.ASRClient);

71
bricks/audio.js Executable file → Normal file
View File

@ -26,7 +26,7 @@ bricks.AudioPlayer = class extends bricks.JsWidget {
this.audio = this._create('audio');
this.audio.controls = true;
if (this.opts.autoplay){
this.audio.addEventListener('canplay', this.play_audio.bind(this));
this.audio.addEventListener('canplay', this.play.bind(this));
}
this.audio.style.width = "100%"
this.dom_element.appendChild(this.audio);
@ -34,6 +34,58 @@ bricks.AudioPlayer = class extends bricks.JsWidget {
this.set_source(this.url);
}
}
set_stream_urls(response){
async function* dyn_urls(response) {
const reader = response.body.getReader();
var value;
var done;
while (true){
done, value = await reader.read();
if (value.done){
console.log('done=', done, 'value=', value);
break;
}
let result = '';
for (let i = 0; i < value.value.length; i++) {
result += String.fromCharCode(value.value[i]);
}
console.log('audio set url=', result);
yield result;
}
}
this.url_generator = dyn_urls(response);
this.srcList = [];
schedule_once(this.load_queue_url.bind(this), 0.1);
}
async load_queue_url(){
while (true){
var d = await this.url_generator.next();
if (d.done){
return;
}
this.srcList.push({played:false, url:d.value});
if (this.srcList.length < 2 ){
await this.play_srclist();
this.audio.addEventListener('ended',
this.play_srclist.bind(this));
}
}
}
async play_srclist(evnet){
if (event && ! this.audio.ended){
return;
}
for (var i=0;i<this.srcList.length;i++){
if (!this.srcList[i].played){
console.log('playit', i, this.srcList[i]);
this.set_url(this.srcList[i].url);
this.srcList[i].played = true;
await this.play();
return;
}
}
}
set_source(url){
if (! this.source){
this.source = this._create('source');
@ -49,14 +101,14 @@ bricks.AudioPlayer = class extends bricks.JsWidget {
// 播放音频
this.audio.play();
}
play(){
this.audio.play();
async play(){
await this.audio.play();
}
toggle_play(){
async toggle_play(){
if (this.audio.paused){
this.audio.play();
await this.audio.play();
} else {
this.audio.pause();
await this.audio.pause();
}
}
set_url(url){
@ -81,6 +133,7 @@ bricks.AudioRecorder = class extends bricks.HBox {
*/
constructor(opts){
super(opts);
this.set_css('clickable');
this.start_icon = opts.start_icon || bricks_resource('imgs/start_recording.png');
this.stop_icon = opts.stop_icon || bricks_resource('imgs/stop_recording.png');
this.rec_btn = new bricks.Icon({
@ -97,7 +150,7 @@ bricks.AudioRecorder = class extends bricks.HBox {
width:'120px'
});
this.upload_url = opts.upload_url;
this.rec_btn.bind('click', this.rec_btn_pressed.bind(this))
this.bind('click', this.rec_btn_pressed.bind(this))
this.URL = window.URL||webkitURL;
this.rec = null;
this.wave = null;
@ -105,7 +158,9 @@ bricks.AudioRecorder = class extends bricks.HBox {
this.add_widget(this.rec_btn);
this.add_widget(this.rec_time);
this.recordData = null;
this.bind('record_ended', this.upload.bind(this));
if (this.upload_url){
this.bind('record_ended', this.upload.bind(this));
}
}
rec_btn_pressed(){
bricks.debug(this.rec_btn.url, ':url:', this.start_icon, this.stop_icon);

View File

@ -7,10 +7,8 @@ bricks.ChartBar = class extends bricks.EchartsExt {
data_params,
method,
data,
line_options,
nameField,
valueField
serieField:
valueFields
}
*/
values_from_data(data, name){

0
bricks/baseKnowledge.txt Executable file → Normal file
View File

View File

@ -32,3 +32,25 @@ bricks.UpStreaming = class extends bricks.JsWidget {
this.stream_ctlr = controller;
}
}
bricks.down_streaming = async function*(response) {
if (! response){
return;
}
const reader = response.body.getReader();
var value;
var t = 0;
while (true){
done, value = await reader.read();
if (value.done){
break;
}
let result = '';
for (let i = 0; i < value.value.length; i++) {
result += String.fromCharCode(value.value[i]);
}
console.log('audio set url=', result);
yield result;
}
}

230
bricks/bricks.js Executable file → Normal file
View File

@ -48,9 +48,20 @@ params:
*/
bricks.uuid = function(){
var d = crypto.randomUUID();
var lst = d.split('-');
return lst.join('');
try{
var d = crypto.randomUUID();
var lst = d.split('-');
return lst.join('');
} catch(e) {
const vv = '1234567890qwertyuiopasdfghjklzxcvbnm';
var ret = '';
for (var i=0;i<30;i++){
var j = parseInt(Math.random() * vv.length);
ret = ret + vv[j];
}
console.log('uuid() return', ret);
return ret;
}
}
bricks.deviceid = function(appname){
@ -124,26 +135,40 @@ bricks.widgetBuild = async function(desc, widget, data){
var klassname = desc.widgettype;
var base_url = widget.baseURI;
while (klassname == 'urlwidget'){
if (data){
desc = bricks.apply_data(desc, data);
}
let url = bricks.absurl(desc.options.url, widget);
base_url = url;
let method = desc.options.method || 'GET';
let opts = desc.options.params || {};
var jc = new bricks.HttpJson();
var desc1 = await jc.httpcall(url, { "method":method, "params":opts});
if (!desc1) return;
desc = desc1;
klassname = desc.widgettype;
}
if (data){
desc = bricks.apply_data(desc, data);
}
if (!desc.widgettype){
console.log('widgettype is null', desc);
return null;
}
let klass = bricks.Factory.get(desc.widgettype);
if (! klass){
bricks.debug('widgetBuild():',desc.widgettype, 'not registered', bricks.Factory.widgets_kw);
console.log('widgetBuild():',
desc.widgettype,
'not registered',
bricks.Factory.widgets_kw);
return null;
}
var options = desc.options || {};
options.baseURI = base_url;
let w = new klass(options);
if (! w){
console.log('w is null');
}
if (desc.id){
w.set_id(desc.id);
}
@ -203,14 +228,34 @@ bricks.universal_handler = async function(from_widget, widget, desc, event){
'desc=', desc,
event);
}
bricks.default_popup = function(opts){
var popts = bricks.get_popup_default_options();
bricks.extend(popts, opts);
return new bricks.Popup(popts);
}
bricks.default_popupwindow = function(opts){
var popts = bricks.get_popupwindow_default_options();
bricks.extend(popts, opts);
return new bricks.PopupWindow(popts);
}
bricks.buildEventHandler = async function(w, desc, event){
var target = bricks.getWidgetById(desc.target, w);
var target;
if (desc.target == 'Popup'){
target = bricks.default_popup(desc.popup_options || {});
} else if (desc.target == 'PopupWindow') {
target = bricks.default_popupwindow(desc.popup_options || {});
} else if ( desc.target instanceof bricks.JsWidget ) {
target = desc.target;
} else {
target = bricks.getWidgetById(desc.target, w);
}
if (! target){
bricks.debug('target miss desc=', desc, 'w=', w);
return null
}
var rtdata = {};
desc.event_params = event.params || {} ;
desc.event = event;
if (desc.rtdata) rtdata = desc.rtdata;
else if (desc.datawidget){
var data_desc = {
@ -221,9 +266,6 @@ bricks.buildEventHandler = async function(w, desc, event){
}
rtdata = await bricks.getRealtimeData(w, data_desc);
}
if (typeof event.params == typeof {}){
rtdata = bricks.extend(rtdata, event.params);
}
switch (desc.actiontype){
case 'urlwidget':
return bricks.buildUrlwidgetHandler(w, target, rtdata, desc);
@ -269,70 +311,80 @@ bricks.getRealtimeData = async function(w, desc){
return null;
}
var _buildWidget = async function(from_widget, target, mode, options){
bricks.debug('target=', target, 'mode=', mode, 'options=', options);
var w = await (bricks.widgetBuild(options, from_widget));
if (!w){
bricks.debug('options=', options, 'widgetBuild() failed');
return;
}
var _buildWidget = async function(from_widget, target, mode, options, desc){
bricks.debug('target=', target, 'mode=', mode, 'options=', options);
var w = await (bricks.widgetBuild(options, from_widget));
if (!w){
bricks.debug('options=', options, 'widgetBuild() failed');
return;
}
if (w.parent) return;
if (mode == 'replace'){
target.clear_widgets();
target.add_widget(w);
} else if (mode == 'insert'){
target.add_widget(w, 0);
} else {
target.add_widget(w);
if (w.parent) {
if (target instanceof bricks.Popup || target instanceof bricks.PopupWindow){
target.destroy();
}
return;
}
if (target instanceof bricks.Popup || target instanceof bricks.PopupWindow) {
if (! target.parent){
bricks.app.add_widget(target);
}
console.log('target=', target);
if (desc.popup_options.eventpos){
target.bind('opened', bricks.relocate_by_eventpos.bind(null, desc.event, target));
}
}
if (mode == 'replace'){
target.clear_widgets();
target.add_widget(w);
} else if (mode == 'insert'){
target.add_widget(w, 0);
} else {
target.add_widget(w);
}
if (target instanceof bricks.Popup || target instanceof bricks.PopupWindow) {
target.open();
}
}
bricks.buildUrlwidgetHandler = function(w, target, rtdata, desc){
var f = async function(target, mode, options){
bricks.debug('target=', target, 'mode=', mode, 'options=', options);
var w = await (bricks.widgetBuild(options, w));
if (!w){
bricks.debug('options=', options, 'widgetBuild() failed');
return;
}
if (mode == 'replace'){
target.clear_widgets();
target.add_widget(w);
} else if (mode == 'insert'){
target.add_widget(w, 0);
} else {
target.add_widget(w);
}
}
var options = objcopy(desc.options||{});
var params = options.params || {};
options = bricks.apply_data(options, rtdata);
options.params = bricks.extend(params, rtdata);
if (desc.event_params instanceof FormData){
var params = desc.event_params;
for ( const [key, value] of Object.entries(rtdata)){
params.append(key, value);
}
options = bricks.apply_data(options, rtdata);
for ( const [key, value] of Object.entries(options.params||{})){
params.append(key, value);
}
options.params = params;
options.method = "POST";
} else {
rtdata = bricks.extend(rtdata, desc.event_params);
options = bricks.apply_data(options, rtdata);
if (desc.params_mapping){
rtdata = bricks.map(rtdata, desc.params_mapping.mapping, desc.params_mapping.need_others);
}
options.params = bricks.extend(params, rtdata);
}
var opts = {
"widgettype":"urlwidget",
"options":options
}
return _buildWidget.bind(w, target, target, desc.mode || 'replace', opts);
return _buildWidget.bind(null, w, target, desc.mode || 'replace', opts, desc);
}
bricks.buildBricksHandler = function(w, target, rtdata, desc){
var f = async function(target, mode, options){
bricks.debug('target=', target, 'mode=', mode, 'options=', options);
var w = await (bricks.widgetBuild(options, wa));
if (!w){
bricks.debug('options=', options, 'widgetBuild() failed');
return;
}
if (mode == 'replace'){
target.clear_widgets();
}
target.add_widget(w);
}
var options = objcopy(desc.options||{});
rtdata = bricks.extend(rtdata, inputdata2dic(desc.event_params));
if (desc.params_mapping){
rtdata = bricks.map(rtdata, desc.params_mapping.mapping, desc.params_mapping.need_others);
}
options = bricks.apply_data(options, rtdata);
return _buildWidget.bind(w, target, target, desc.mode || 'replace', options);
return _buildWidget.bind(w, target, target, desc.mode || 'replace', options, desc);
}
bricks.buildRegisterFunctionHandler = function(w, target, rtdata, desc){
var f = bricks.RF.get(desc.rfname);
@ -340,6 +392,9 @@ bricks.buildRegisterFunctionHandler = function(w, target, rtdata, desc){
bricks.debug('rfname:', desc.rfname, 'not registed', desc);
return null;
}
if (desc.params_mapping){
rtdata = bricks.map(rtdata, desc.params_mapping.mapping, desc.params_mapping.need_others);
}
var params = {};
if (desc.params){
bricks.extend(params, desc.params);
@ -347,6 +402,7 @@ bricks.buildRegisterFunctionHandler = function(w, target, rtdata, desc){
if (rtdata){
bricks.extend(params, rtdata);
}
bricks.extend(params, inputdata2dic(desc.event_params));
params = bricks.apply_data(params, rtdata);
return f.bind(target, params);
}
@ -359,6 +415,10 @@ bricks.buildMethodHandler = function(w, target, rtdata, desc){
var params = {};
bricks.extend(params, desc.params)
bricks.extend(params, rtdata);
bricks.extend(params, inputdata2dic(desc.event_params));
if (desc.params_mapping){
rtdata = bricks.map(rtdata, desc.params_mapping.mapping, desc.params_mapping.need_others);
}
params = bricks.apply_data(params, rtdata);
return f.bind(target, params);
}
@ -366,6 +426,10 @@ bricks.buildScriptHandler = function(w, target, rtdata, desc){
var params = {};
bricks.extend(params, desc.params)
bricks.extend(params, rtdata);
bricks.extend(params, inputdata2dic(desc.event_params));
if (desc.params_mapping){
rtdata = bricks.map(rtdata, desc.params_mapping.mapping, desc.params_mapping.need_others);
}
params = bricks.apply_data(params, rtdata);
var AsyncFunction = Object.getPrototypeOf(async function(){}).constructor;
var f = new AsyncFunction('params', 'event', desc.script);
@ -379,6 +443,7 @@ bricks.buildDispatchEventHandler = function(w, target, rtdata, desc){
var params = {};
bricks.extend(params, desc.params)
bricks.extend(params, rtdata);
bricks.extend(params, inputdata2dic(desc.event_params));
params = bricks.apply_data(params, rtdata);
return f.bind(target, desc.dispatch_event, params);
}
@ -405,7 +470,7 @@ bricks.getWidgetById = function(id, from_widget){
el = bricks.app.root.dom_element;
continue;
}
if (ids[i]=='app'){
if (ids[i]=='app' || ids[i] == 'body'){
return bricks.app;
}
if (ids[i] == 'window'){
@ -444,6 +509,7 @@ bricks.App = class extends bricks.Layout {
/*
opts = {
appname:
debug:false, true, 'server'
login_url:
"charsize:
"language":
@ -460,10 +526,11 @@ bricks.App = class extends bricks.Layout {
*/
super(opts);
bricks.app = this;
this.docks = [];
bricks.bug = opts.debug || false;
bricks.Body = this;
this.deviceid = bricks.deviceid(opts.appname || 'appname');
this.login_url = opts.login_url;
this.login_url = opts.login_url || '/rbac/userpassword_login.ui';
this.charsize = this.opts.charsize || 20;
this.keyevent_blocked = false;
this.char_size = this.observable('charsize', this.charsize);
@ -482,8 +549,53 @@ bricks.App = class extends bricks.Layout {
this.add_widget(this.tooltip);
this._Width = this.dom_element.offsetWidth;
this._Height = this.dom_element.offsetHeight;
this.video_stream = null;
this.audio_stream = null;
this.video_devices = null
this.vpos = null;
document.addEventListener('keydown', this.key_down_action.bind(this));
this.screen_orient = window.screen.orientation.type;
window.screen.orientation.addEventListener('change', () => {
this.screen_orient = window.screen.orientation.type;
this.bind('orient_changed', this.screen_orient);
});
}
async getCameras() {
try {
const devices = await navigator.mediaDevices.enumerateDevices();
this.video_devices = devices.filter(device => device.kind === 'videoinput');
} catch (error) {
console.error('获取摄像头数量出错:', error);
}
}
async start_camera(vpos) {
if (typeof(vpos) == 'undefined') vpos = 0;
if (this.video_devices === null){
await this.getCameras();
}
if (vpos == this.vpos) return;
this.vpos = vpos;
if (this.video_stream){
this.video_stream.getTracks().forEach(track => {
track.stop();
});
}
if (navigator.mediaDevices.getUserMedia) {
var x = { deviceId: this.video_devices[vpos].deviceId };
this.video_stream = await navigator.mediaDevices.getUserMedia({ video: x });
} else {
console.log("Webcam access is not supported in this browser.");
}
}
async start_mic() {
if (this.audio_stream) return;
if (navigator.mediaDevices.getUserMedia) {
this.audio_stream = navigator.mediaDevices.getUserMedia({ audio: true });
} else {
console.log("mic access is not supported in this browser.");
this.stream = null;
}
}
new_zindex(){
const v = this.zindex;
this.zindex = v + 1;

View File

@ -1,14 +1,15 @@
SOURCES=" page_data_loader.js factory.js uitypesdef.js utils.js uitype.js \
i18n.js widget.js layout.js bricks.js image.js html.js \
jsoncall.js myoperator.js scroll.js menu.js popup.js modal.js running.js \
i18n.js widget.js layout.js bricks.js image.js html.js splitter.js \
jsoncall.js myoperator.js scroll.js menu.js popup.js camera.js modal.js running.js \
markdown_viewer.js video.js audio.js toolbar.js tab.js \
input.js registerfunction.js button.js accordion.js dataviewer.js \
tree.js multiple_state_image.js dynamiccolumn.js form.js message.js conform.js \
paging.js datagrid.js iframe.js cols.js echartsext.js \
floaticonbar.js miniform.js wterm.js dynamicaccordion.js \
binstreaming.js streaming_audio.js vadtext.js rtc.js \
binstreaming.js streaming_audio.js vadtext.js rtc.js docxviewer.js \
llm_dialog.js llm.js websocket.js datarow.js tabular.js \
line.js pie.js bar.js gobang.js "
line.js pie.js bar.js gobang.js period.js iconbarpage.js \
keypress.js asr.js webspeech.js countdown.js progressbar.js "
echo ${SOURCES}
cat ${SOURCES} > ../dist/bricks.js
# uglifyjs --compress --mangle -- ../dist/bricks.js > ../dist/bricks.min.js
@ -38,4 +39,5 @@ cp -a ../examples/* ../dist/examples
cp -a ../3parties/* ../dist/3parties
cp -a ../docs/* ../dist/docs
cp *.tmpl ../dist
cp -a ../dist /tmp
echo "Finished ..."

99
bricks/camera.js Normal file
View File

@ -0,0 +1,99 @@
var bricks = window.bricks || {}
bricks.Camera = class extends bricks.Popup {
/*
{
fps:60
}
*/
constructor(opts){
opts.fps = opts.fps || 60;
opts.auto_dismiss = false;
super(opts);
this.stream = null;
this.video = document.createElement('video');
var filler = new bricks.Filler({});
var hbox = new bricks.HBox({
cheight:3
});
this.cur_camera_id = 0;
this.add_widget(filler);
this.add_widget(hbox);
var shot_btn = new bricks.Icon({
url:bricks_resource('imgs/camera.png'),
margin: '10px',
tip:'Take a picture',
rate:2.5
});
var switch_btn = new bricks.Icon({
url:bricks_resource('imgs/switch-camera.png'),
tip:'switch camera',
margin: '10px',
rate:1.5
});
var del_btn = new bricks.Icon({
url:bricks_resource('imgs/delete.png'),
tip:'canel it',
margin: '10px',
rate:1.5
})
del_btn.bind('click', this.dismiss.bind(this));
shot_btn.bind('click', this.take_picture.bind(this));
switch_btn.bind('click', this.switch_camera.bind(this, switch_btn));
this.imgw = new bricks.Image({
width:'100%'
});
hbox.add_widget(switch_btn);
hbox.add_widget(shot_btn);
hbox.add_widget(new bricks.Filler({}));
hbox.add_widget(del_btn);
filler.add_widget(this.imgw);
this.task_period = 1 / this.fps;
schedule_once(this.startCamera.bind(this), 0.1);
}
async switch_camera(btn, event){
if (bricks.app.video_devices.length < 2){
btn.disabled(true);
return;
}
var vpos = bricks.app.vpos;
vpos += 1;
if (vpos >= bricks.app.video_devices.length){
vpos = 0;
}
this.startCamera(vpos);
}
async startCamera(vpos) {
await bricks.app.start_camera(vpos);
this.stream = bricks.app.video_stream;
this.video.srcObject = this.stream;
this.video.play();
this.show_cnt = 1;
this.task = schedule_once(this.show_picture.bind(this), this.task_period);
}
show_picture(){
if (this.task_period == 0){
return;
}
var canvas = document.createElement('canvas');
canvas.height = this.video.videoHeight;
canvas.width = this.video.videoWidth;
const context = canvas.getContext('2d');
context.drawImage(this.video, 0, 0);
this.imgw.set_url(canvas.toDataURL('image/jpeg'));
this.task = schedule_once(this.show_picture.bind(this), this.task_period);
this.show_cnt += 1;
}
take_picture(event){
event.stopPropagation();
if (this.task){
task.cancel();
this.task = null;
}
this.task_period = 0;
this.task = null;
var d = this.imgw.base64();
this.dispatch('shot', d);
}
}
bricks.Factory.register('Camera', bricks.Camera);

View File

@ -55,6 +55,7 @@ bricks.Cols = class extends bricks.VBox {
}
async handle_click(rw, event){
event.stopPropagation();
var orev = null;
if (this.select_record){
orev = this.select_record;
@ -64,6 +65,8 @@ bricks.Cols = class extends bricks.VBox {
}
this.select_record = rw;
this.select_record.set_css('selected_record');
console.log('record data=', rw.user_data);
this.dispatch('record_click', rw.user_data);
}
async dataHandle(d){
var data = d.rows;
@ -71,18 +74,20 @@ bricks.Cols = class extends bricks.VBox {
if (!data){
return;
}
if (! this.loader.is_max_page(page)){
var rev = ! this.loader.is_max_page(page);
if (rev){
data.reverse();
}
for (var i=0;i<data.length;i++){
var d = data[i];
var w = await bricks.widgetBuild(this.record_view, this.main, d);
var r = data[i];
var w = await bricks.widgetBuild(this.record_view, this.main, r);
w.user_data = r;
w.bind('click', this.handle_click.bind(this, w));
w.set_attribute('data-page', page);
if (this.loader.is_max_page(page)){
this.main.add_widget(w);
} else {
if (rev){
this.main.add_widget(w, 0);
} else {
this.main.add_widget(w);
}
}
if (d.delete_page){
@ -93,11 +98,11 @@ bricks.Cols = class extends bricks.VBox {
var items = this.dom_element.querySelectorAll('[data-page="' + page + '"]');
for (var i=0;i<items.length;i++) {
var w = items[i].bricks_widget;
this.container.remove_widget(w);
this.main.remove_widget(w);
}
}
async load_first_page(params){
var p = bricks.extend({}, this.params);
var p = bricks.extend({}, this.data_params);
if (params){
p = bricks.extend(p, params);
}
@ -112,14 +117,14 @@ bricks.Cols = class extends bricks.VBox {
this.main = new bricks.DynamicColumn({
width:"100%",
col_cwidth:this.col_cwidth,
mobile_cols:this.mobile_cols,
mobile_cols:2});
mobile_cols:this.mobile_cols || 2
});
this.container.add_widget(this.main);
var d = await this.loader.loadData(p);
if (d){
this.dataHandle(d);
var total = this.container.dom_element.scrollHeight - this.container.dom_element.clientHeight;
this.container.dom_element.scrollTop = d.pos_rate * total;
// this.container.dom_element.scrollTop = d.pos_rate * total;
} else {
bricks.debug(this.loader, 'load previous page error');
}
@ -163,12 +168,12 @@ bricks.Cols = class extends bricks.VBox {
if (d){
this.dataHandle(d);
var total = this.container.dom_element.scrollHeight - this.container.dom_element.clientHeight;
this.container.dom_element.scrollTop = d.pos_rate * total;
// this.container.dom_element.scrollTop = d.pos_rate * total;
} else {
bricks.debug(this.loader, 'load next page error');
console.log(this.loader, 'load next page error');
}
} catch (e){
bricks.debug('error happened', e);
console.log('error happened', e);
}
this.loading = false;
running.dismiss();

View File

@ -1,13 +1,29 @@
var bricks = window.bricks || {};
bricks.Conform = class extends bricks.Message {
bricks.Conform = class extends bricks.PopupWindow {
constructor(opts){
opts.auto_open = true;
opts.auto_close = false;
opts.timeout = 0;
opts.auto_open = true;
super(opts);
this.create_toolbar();
this.create_conform();
}
create_toolbar(){
create_conform(){
var w = new bricks.VBox({width:'100%', height: '100%'});
this.create_message(w);
this.create_toolbar(w);
this.add_widget(w);
}
create_message(widget){
var w = new bricks.Filler();
widget.add_widget(w);
var w1 = new bricks.VScrollPanel({});
w.add_widget(w1);
var t = new bricks.Text({otext:this.opts.message,
wrap:true,
halign:'middle',
i18n:true});
w1.add_widget(t);
}
create_toolbar(widget){
var desc = {
tools:[
bricks.extend({
@ -26,7 +42,7 @@ bricks.Conform = class extends bricks.Message {
w.bind('conform', this.conform_hndl.bind(this));
w.bind('discard', this.discard_hndl.bind(this));
if (!w) return;
this.message_w.add_widget(w);
widget.add_widget(w);
}
conform_hndl(event){
this.dismiss();

76
bricks/countdown.js Normal file
View File

@ -0,0 +1,76 @@
var bricks = window.bricks || {};
bricks.Countdown = class extends bricks.VBox {
/*
options:
limit_time: 01:00:00
text_rate:
event:
timeout
timeout event is fired after the countdown time is over.
method:
start
start method is to start the countdown, step is 1 secord
*/
constructor(opts){
super(opts);
var parts = opts.limit_time.split(':');
var hours, minutes, seconds;
switch(parts.length){
case 0:
hours = 0;
minutes = 0;
seconds = 0;
break;
case 1:
hours = 0;
minutes = 0;
seconds = parseInt(parts[0])
break;
case 2:
hours = 0;
minutes = 0;
seconds = parseInt(parts[0])
break;
case 3:
default:
hours = parseInt(parts[0]);
minutes = parseInt(parts[1]);
seconds = parseInt(parts[2])
break;
}
this.seconds = hours * 3600 + minutes * 60 + seconds;
this.text_w = new bricks.Text({
text:this.limit_time,
rate:this.text_rate
});
this.add_widget(this.text_w);
}
start(){
schedule_once(this.time_down_second.bind(this), 1)
}
formatTime(seconds) {
let hrs = Math.floor(seconds / 3600);
let mins = Math.floor((seconds % 3600) / 60);
let secs = seconds % 60;
return [
hrs.toString().padStart(2, '0'),
mins.toString().padStart(2, '0'),
secs.toString().padStart(2, '0')
].join(':');
}
time_down_second(){
if (this.seconds < 1){
this.dispatch('timeout');
return;
}
var h, m, s;
this.seconds -= 1;
var ts = formatTime(this.seconds);
this.text_w.set_text(ts);
schedule_once(this.time_down_second.bind(this), 1)
}
}
bricks.Factory.register('Countdown', bricks.Countdown);

View File

@ -6,10 +6,26 @@ body {
overflow: auto;
display: flex;
}
pre {
overflow-x: auto; /* 允许内容超出容器显示 */
background-color: #b5e5e5;
}
* {
box-sizing: border-box!important;
}
hr {
height: 1px;
background-color: #8a8a8a;
width: 80%;
}
.flexbox {
height: 100%;
width: 100%;
display: flex;
}
.curpos {
border-radius: 30%;
background-color: #f5f5f5;
@ -21,6 +37,14 @@ body {
background-color: #f5f5f5;
border: 1px solid #888888;
}
.subcard {
background-color: #eeeeee;
}
.clickable {
color: #40cc40;
cursor: pointer;
}
.video-in-video {
position: relative;
}
@ -46,6 +70,34 @@ body {
grid-gap: 1px;
}
.resizebox {
width: 10px;
height: 10px;
background-color: darkblue;
position: absolute;
bottom: 0;
right: 0;
cursor: se-resize; /* 改变鼠标指针样式 */
}
.popup {
display: none;
position: absolute;
box-sizing: border-box; /* 包括边框在内计算宽度和高度 */
color: #111111;
background-color: #f1f1f1;
border: 1px solid #c1c1c1;
border-radius: 5px;
padding: 4px;
}
.titlebar {
background-color: #d8d8c8;
}
.toppopup {
box-shadow: 10px 5px 10px #000, 0 -5px 5px #fff;
}
.modal {
display:none;
position: absolute;
@ -108,7 +160,7 @@ body {
}
.scroll {
overflow:scroll;
overflow: auto;
}
.vcontainer {
@ -138,7 +190,7 @@ body {
.filler, .hfiller, .vfiller {
flex: auto;
flex-grow: 1;
overflow:hidden;
overflow:hidden;
}
.vfiller .vbox:last-child {
@ -210,16 +262,8 @@ body {
.multicolumns {
column-width: 340px;
colomn-gap: 10px';
overflow-x:none;
}
.popup {
display: none;
position: absolution;
box-sizing: border-box; /* 包括边框在内计算宽度和高度 */
background-color: #f1f1f1;
border: 1px solid #c1c1c1;
colomn-gap: 10px;
overflow-x: none;
}
.selected_record {
@ -271,6 +315,11 @@ body {
height: 50px;
background-color: blue;
}
.childrensize {
display: flex;
flex-wrap: nowrap;
flex-shrink: 0;
}
.datagrid-row {
flex:0 0 150px;
display: flex;
@ -334,14 +383,22 @@ body {
overflow: auto;
}
.tabular-header-row {
background-color: #dddddd;
position: sticky;
display: flex;
top: 0;
width: auto;
position: sticky;
background-color: #dddddd;
min-width: 0;
min-width: fit-content;
flex-wrap: nowrap;
flex-shrink: 0;
}
.tabular-row {
width: auto;
display: flex;
margin-bottom: 5px;
min-width: 0;
min-width: fit-content;
flex-wrap: nowrap;
flex-shrink: 0;
}
.tabular-row:nth-child(odd) {
background-color: #5dfdfd;
@ -350,10 +407,10 @@ body {
background-color: #f9f9f9;
}
.tabular-row-selected {
background-color: #ef0000;
color: #ef0000;
}
.tabular-row-content {
padding: 10px;
padding: 2;
}
.tabular-cell {
border: 1px solid #ccc;
@ -365,6 +422,7 @@ body {
margin-left: 5px;
margin-right: auto;
margin-bottom: 10px;
padding: 3px;
background-color:#fefedd;
border-top-left-radius: 10px;
border-top-right-radius: 0;
@ -387,3 +445,19 @@ body {
.llm_title {
background-color:#eeeeee;
}
.progress-container {
width: 80%;
background-color: #ddd;
border-radius: 5px;
overflow: hidden;
margin-top: 20px;
}
.progress-bar {
height: 30px;
width: 0%;
background-color: #4CAF50;
text-align: center;
color: white;
line-height: 30px;
}

View File

@ -20,7 +20,6 @@ bricks.DataRow = class extends bricks.HBox {
*/
constructor(opts){
super(opts);
this.set_style('width', 'auto');
this.record_w = null;
}
render_header(){
@ -30,13 +29,17 @@ bricks.DataRow = class extends bricks.HBox {
this.render(false);
}
render(header){
this.build_toolbar(header);
// this.build_toolbar(header);
if (this.checkField){
var w;
if (header){
w = new bricks.BlankIcon({});
} else {
w = new bricks.UiCheck({name:this.checkField,value:this.user_data[this.checkField]});
var v = 0
if (this.user_data){
v = this.user_data[this.checkField];
}
w = new bricks.UiCheck({name:this.checkField,value:v});
w.bind('changed', this.get_check_state.bind(this));
}
this.add_widget(w);
@ -87,6 +90,7 @@ bricks.DataRow = class extends bricks.HBox {
}
build_fields(header, cw){
this.record_w = new bricks.HBox({height:'auto'});
this.record_w.set_css('childrensize');
this.add_widget(this.record_w);
this._build_fields(header, this.record_w);
}
@ -108,7 +112,7 @@ bricks.DataRow = class extends bricks.HBox {
continue;
}
var opts = bricks.extend({
margin:'3px'
margin:'1px'
}, f);
if (header || ! this.user_data){
opts.value = f.label || f.name;
@ -116,10 +120,7 @@ bricks.DataRow = class extends bricks.HBox {
opts.user_data = this.user_data;
opts.value = opts.tip = this.user_data[f.name];
}
var cwidth = cwidths[f.name];
if (cwidth){
opts.cwidth = cwidth;
}
opts.cwidth = cwidths[f.name] ||f.cwidth || 10;
var f = bricks.get_ViewBuilder(f.uitype);
if (!f) f = bricks.get_ViewBuilder('str');
var w = f(opts);

View File

@ -138,7 +138,7 @@ bricks.DataViewer = class extends bricks.VBox {
}
if (this.toolbar){
this.toolbar.tools.forEach(t => {
if (! edit_names.incloudes(t.name)){
if (! edit_names.includes(t.name)){
tbdesc.tools.push(t);
}
});
@ -168,12 +168,13 @@ bricks.DataViewer = class extends bricks.VBox {
this.delete_record(this.select_row);
return;
}
var d = {
tabular:this,
row:this.select_row,
data:row.user_data
var data = null;
if (this.select_row){
var r = this.select_row;
var data = r.user_data;
}
this.dispathc(t.name. d);
console.log(tdesc.name, 'clicked ==================', tdesc.name, data)
this.dispatch(tdesc.name, data);
}
get_edit_fields(){
var fs = this.row_options.fields;
@ -193,7 +194,8 @@ bricks.DataViewer = class extends bricks.VBox {
this.dispatch('row_check_changed', event.params.user_data);
}
async renew_record_view(form, row){
var d = form.getValue();
var d = form._getValue();
d = form._getValue();
var record = bricks.extend(row.user_data, d);
row.renew(record);
}
@ -215,6 +217,11 @@ bricks.DataViewer = class extends bricks.VBox {
fs.push(this.fields[i]);
}
var f = new bricks.ModalForm({
"widget":this,
"archor":"cc",
"movable":true,
"resizable":true,
"archor":"cc",
"width":"90%",
"height":"70%",
"submit_url":this.editable.new_data_url,
@ -225,10 +232,10 @@ bricks.DataViewer = class extends bricks.VBox {
}
async add_record_finish(f, event){
f.dismiss();
this.render();
var resp = event.params;
var desc = await resp.json();
var w = await bricks.widgetBuild(desc);
this.render();
}
update_record(){
var record = this.select_row.user_data;
@ -239,6 +246,10 @@ bricks.DataViewer = class extends bricks.VBox {
fields.push(f);
}
var f = new bricks.ModalForm({
"widget":this,
"movable":true,
"resizable":true,
"archor":"cc",
"width":"90%",
"height":"70%",
"submit_url":this.editable.update_data_url+'?id=' + record.id,
@ -251,15 +262,15 @@ bricks.DataViewer = class extends bricks.VBox {
f.dismiss();
}
async update_record_finish(form, event){
await this.renew_record_view(form, this.select_row);
var resp = event.params;
var desc = await resp.json();
var w = await bricks.widgetBuild(desc);
if (desc.widgettype == 'Message'){
await this.renew_record_view(form, this.select_row);
}
}
delete_record(row, record){
var conform_w = new bricks.Conform({
cwidth:16,
cheight:9,
target:this,
title:'Delete conform',
message:'Are you sure to delete is record?'
@ -315,8 +326,8 @@ bricks.DataViewer = class extends bricks.VBox {
var d = await this.loader.loadNextPage();
if (d){
this.dataHandle(d);
var total = this.scrollpanel.dom_element.scrollHeight - this.scrollpanel.dom_element.clientHeight;
this.scrollpanel.dom_element.scrollTop = d.pos_rate * total;
// var total = this.scrollpanel.dom_element.scrollHeight - this.scrollpanel.dom_element.clientHeight;
// this.scrollpanel.dom_element.scrollTop = d.pos_rate * total;
} else {
bricks.debug(this.loader, 'load next page error');
}

119
bricks/docxviewer.js Normal file
View File

@ -0,0 +1,119 @@
/* need mammoth module
<script src="https://cdnjs.cloudflare.com/ajax/libs/mammoth/1.4.2/mammoth.browser.min.js"></script>
*/
var bricks = window.bricks || {};
bricks.DOCXviewer = class extends bricks.VBox {
/*
url:
*/
constructor(opts){
super(opts);
this.bind('on_parent', this.set_url.bind(this));
// schedule_once(this.set_url.bind(this, this.url), 0.2);
}
async set_url(url){
var container = this.dom_element
var hab = new bricks.HttpArrayBuffer();
var ab = await hab.get(this.url);
var result = await mammoth.convertToHtml({ arrayBuffer: ab });
container.innerHTML = result.value;
}
}
function extractBodyContent(htmlString) {
// 正则表达式匹配<body>和</body>之间的内容
const regex = /<body[^>]*>([\s\S]*?)<\/body>/i;
const matches = htmlString.match(regex);
return matches ? matches[1] : null; // 如果匹配到返回匹配的内容否则返回null
}
bricks.EXCELviewer = class extends bricks.VBox {
constructor(opts){
opts.height = "100%",
super(opts);
this.sheets_w = new bricks.HBox({cheight:3, width:'100%'});
this.sheets_w.set_css('scroll');
this.cur_sheetname = null;
this.container = new bricks.Filler({});
this.add_widget(this.container);
this.add_widget(this.sheets_w);
this.bind('on_parent', this.set_url.bind(this));
}
async set_url(url){
this.sheets_w.clear_widgets();
var hab = new bricks.HttpArrayBuffer();
var ab = await hab.get(this.url);
const data = new Uint8Array(ab);
this.workbook = XLSX.read(data, {type: 'array'});
this.workbook.SheetNames.forEach((sheetname, index) => {
var w = new bricks.Text({text:sheetname, wrap:false});
w.set_css('clickable');
w.set_style('padding', '10px');
w.bind('click', this.show_sheet_by_name.bind(this, sheetname, w));
this.sheets_w.add_widget(w);
if (index==0){
this.show_sheet_by_name(this.workbook.SheetNames[0], w);
}
});
}
show_sheet_by_name(sheetname, tw){
if (this.cur_sheetname == sheetname) return;
this.sheets_w.children.forEach(c => c.set_css('selected', true));
tw.set_css('selected');
const x = new bricks.VScrollPanel({width: '100%', height: '100%'});
const sheet = this.workbook.Sheets[sheetname];
// const html = extractBodyContent(XLSX.utils.sheet_to_html(sheet));
const html = XLSX.utils.sheet_to_html(sheet);
x.dom_element.innerHTML = html;
this.container.clear_widgets();
this.container.add_widget(x);
this.cur_sheetname = sheetname;
}
}
bricks.PDFviewer = class extends bricks.VBox {
/*
url:
*/
constructor(opts){
opts.width = '100%';
super(opts);
this.bind('on_parent', this.set_url.bind(this));
}
async set_url(url){
var container = this.dom_element
var hab = new bricks.HttpArrayBuffer();
var ab = await hab.get(this.url);
const task = pdfjsLib.getDocument({ data: ab });
task.promise.then((pdf) => {
this.pdf = pdf;
for (let i = 1; i <= this.pdf.numPages; i++) {
this.pdf.getPage(i).then((page) => {
this.add_page_content(page);
});
}
}).catch((err) => {
console.log('error');
})
}
add_page_content(page){
const scale = 1.5;
const viewport = page.getViewport({ scale });
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
canvas.height = viewport.height;
canvas.width = viewport.width;
page.render({ canvasContext: context, viewport });
var w = new bricks.JsWidget();
w.dom_element.appendChild(canvas);
this.add_widget(w);
if (i < this.pdf.numPages){
w = new bricks.Splitter();
this.add_widget(w)
}
}
}
bricks.Factory.register('DOCXviewer', bricks.DOCXviewer);
bricks.Factory.register('EXCELviewer', bricks.EXCELviewer);
bricks.Factory.register('PDFviewer', bricks.PDFviewer);

View File

@ -30,21 +30,19 @@ bricks.DynamicColumn = class extends bricks.Layout {
var cw;
var cols;
var gap;
var width = this.get_width();
if (this.col_cwidth){
cw = bricks.app.charsize * this.col_cwidth;
} else {
cw = this.col_width;
}
gap = bricks.app.charsize * (this.col_cgap || 0.1);
if (width > 0){
if (bricks.is_mobile() && (width < this.get_height())){
cols = this.mobile_cols || 1;
} else {
cols = (width + gap) / (cw + gap)
}
cols = Math.floor(width/cw);
var width = bricks.app.screenWidth();
var height = bricks.app.screenHeight();
if (bricks.is_mobile() && width < height){
cols = this.mobile_cols;
cw = (width - (cols - 1) * gap) / cols;
console.log('====mobile==cols=', cols, '====');
} else {
if (this.col_cwidth){
cw = bricks.app.charsize * this.col_cwidth;
} else {
cw = this.col_width;
}
}
this.dom_element.style.gridTemplateColumns = "repeat(auto-fill, minmax(" + cw + "px, 1fr))";
this.set_style('gap', gap + 'px');

View File

@ -83,6 +83,7 @@ bricks.EchartsExt = class extends bricks.VBox {
this.valueFields = this.get_series(data);
data = this.pivotify(data);
}
this.chart = echarts.init(this.holder.dom_element);
var opts = this.setup_options(data);
opts.grid = {
left: '3%',

0
bricks/factory.js Executable file → Normal file
View File

View File

@ -20,10 +20,22 @@ bricks.IconBar = class extends bricks.HBox {
if (! opts.cheight){
opts.cheight = 2;
}
if (! opts.rate){
opts.rate = 1;
}
super(opts);
this.set_css('childrensize');
this.height_int = 0;
var tools = this.opts.tools;
for (var i=0;i<tools.length;i++){
var w = this.build_item(tools[i]);
var opts_i = bricks.extend({}, tools[i]);
if (!opts_i.rate){
opts_i.rate = this.rate;
}
opts_i.cheight = opts.cheight;
opts_i.cwidth = opts.cheight;
opts_i.dynsize = true;
var w = this.build_item(opts_i);
w.set_style('margin-left', this.opts.margin || '10px');
w.set_style('margin-right', this.opts.margin || '10px');
if (tools[i].name){
@ -37,27 +49,27 @@ bricks.IconBar = class extends bricks.HBox {
}
build_item(opts){
var rate = opts.rate || this.opts.rate || 1;
var h = bricks.app.charsize * rate;
var h = bricks.app.charsize * rate * opts.cheight;
if (this.height_int < h){
this.height_int = h;
}
if (opts.name == 'blankicon'){
return new bricks.BlankIcon({
rate:rate,
cheight:opts.cheight,
cwidth:opts.cwidth,
dynsize:opts.dynsize||true
});
}
var opts1 = bricks.extend({}, opts);
opts1.url = opts.icon;
opts1.rate = opts.rate || rate;
opts1.dynsize = opts.dynsize||true;
var w = new bricks.Icon(opts1);
w.bind('click', this.regen_event.bind(this, opts1));
opts.url = opts.icon;
var w = new bricks.Icon(opts);
w.bind('click', this.regen_event.bind(this, opts));
return w;
}
regen_event(desc, event){
this.dispatch(desc.name, desc);
this.dispatch('command', desc);
event.preventDefault();
event.stopPropagation();
}
}

View File

@ -176,7 +176,7 @@ bricks.FormBase = class extends bricks.Layout {
w.reset();
}
}
getValue(){
_getValue(){
var data = {};
for (var name in this.name_inputs){
if (! this.name_inputs.hasOwnProperty(name)){
@ -195,6 +195,14 @@ bricks.FormBase = class extends bricks.Layout {
}
return data;
}
getValue(){
if (this.data) {
var ret = this.data;
this.data = null;
return ret;
}
return this.get_formdata();
}
get_formdata(){
var data = new FormData();
for (var name in this.name_inputs){
@ -204,37 +212,35 @@ bricks.FormBase = class extends bricks.Layout {
var w = this.name_inputs[name];
var d = w.getValue();
if (w.required && ( d[name] == '' || d[name] === null)){
bricks.debug('data=', data, 'd=', d);
new bricks.Error({title:'Requirement', message:'required field must input"' + w.label + '"'})
w.focus();
return;
}
if (bricks.need_formdata_fields.includes(w.uitype)){
var files = w.get_files();
for (var i=0;i<files.length;i++){
// data.append(name, w.fileContents[i].body, w.fileContents[i].filename);
data.append(name, files[i]);
}
} else {
data.append(name, d[name]);
if (d[name] === null){
continue;
}
w.set_formdata(data);
}
this.data = data;
return data;
}
async validation(){
var running = new bricks.Running({target:this});
try {
var data;
data = this.get_formdata();
/*
if (this.need_formdata){
data = this.get_formdata();
} else {
data = this.getValue();
}
*/
if (! data) {
running.dismiss();
return;
}
data = bricks.delete_null_values(data);
// data = bricks.delete_null_values(data);
this.dispatch('submit', data);
if (this.submit_url){
var rc = new bricks.HttpResponse();

View File

@ -4,34 +4,37 @@
<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/3parties/xterm.css" />
<link href="/bricks/3parties/video-js.css" rel="stylesheet" />
<link rel="stylesheet" href="/bricks/css/bricks.css">
<link rel="stylesheet" href="{{entire_url('/bricks/3parties/xterm.css')}}" />
<link href="{{entire_url('/bricks/3parties/video-js.css')}}" rel="stylesheet" />
<link rel="stylesheet" href="{{entire_url('/bricks/css/bricks.css')}}">
<link rel="stylesheet", href="{{entire_url('/css/myapp.css')}}">
</head>
<body>
<!--
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/js-docx-viewer/dist/css/docxviewer.min.css">
<script src="//cdn.bootcdn.net/ajax/libs/eruda/2.3.3/eruda.js"></script>
<script>eruda.init();</script>
<script src="https://cdn.jsdelivr.net/npm/onnxruntime-web/dist/ort.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@ricky0123/vad-web@0.0.7/dist/bundle.min.js"></script>
<script src="https://unpkg.com/docx-js@3.2.0/lib/docx.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.14.305/pdf.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mammoth/1.4.2/mammoth.browser.min.js"></script>
-->
<script type="text/javascript" src="https://registry.npmmirror.com/echarts/5.5.1/files/dist/echarts.min.js"></script>
<script src="/bricks/3parties/marked.min.js"></script>
<script src="/bricks/3parties/xterm.js"></script>
<script src="/bricks/3parties/video.min.js"></script>
<script src="{{entire_url('/bricks/3parties/marked.min.js')}}"></script>
<script src="{{entire_url('/bricks/3parties/xterm.js')}}"></script>
<script src="{{entire_url('/bricks/3parties/xterm-addon-fit.js')}}"></script>
<script src="{{entire_url('/bricks/3parties/video.min.js')}}"></script>
<!---
<link href="https://unpkg.com/video.js@6/dist/video-js.css" rel="stylesheet">
<script src="https://unpkg.com/video.js@6/dist/video.js"></script></head>
--->
<script src="/bricks/3parties/recorder.wav.min.js"></script>
<script src="/bricks/bricks.js"></script>
<script src="{{entire_url('/bricks/3parties/recorder.wav.min.js')}}"></script>
<script src="{{entire_url('/bricks/bricks.js')}}"></script>
<script src="{{entire_url('/js/myapp.js')}}"></script>
<script>
/*
if (bricks.is_mobile()){
alert('wh=' + window.innerWidth + ':' + window.innerHeight);
}
*/
const opts = {
"widget":

0
bricks/i18n.js Executable file → Normal file
View File

55
bricks/iconbarpage.js Normal file
View File

@ -0,0 +1,55 @@
var bricks = window.bricks || {};
bricks.IconbarPage = class extends bricks.VBox {
/*
opts={
bar_opts:
bar_at: top or bottom
}
bar_opts:{
margin:
rate:
tools:
}
tools = [
tool, ...
]
tool = {
name:
icon:
label: optional
tip,
dynsize
rate:
context:
}
*/
constructor(opts){
opts.height = '100%'
opts.bar_at = opts.bar_at || 'top';
super(opts);
var bar = new bricks.IconTextBar(this.bar_opts);
this.content = new bricks.Filler({});
if (this.bar_at == 'top'){
this.add_widget(bar);
this.add_widget(this.content);
} else {
this.add_widget(this.content);
this.add_widget(bar);
}
bar.bind('command', this.command_handle.bind(this))
schedule_once(this.show_content.bind(this, this.bar_opts.tools[0]), 0.1);
}
async command_handle(event){
var tool = event.params;
await this.show_content(tool);
}
async show_content(tool){
var w = await bricks.widgetBuild(tool.content, this);
if (w && ! w.parent) {
this.content.add_widget(w);
}
}
}
bricks.Factory.register('IconbarPage', bricks.IconbarPage);

View File

@ -1,6 +1,7 @@
var bricks = window.bricks || {};
bricks.Iframe = class extends bricks.Layout {
constructor(opts){
opts.height = opts.height || '100%';
super(opts);
this.dom_element.src = opts.url;
}
@ -9,4 +10,12 @@ bricks.Iframe = class extends bricks.Layout {
}
}
bricks.NewWindow = class extends bricks.JsWidget {
constructor(opts){
super(opts);
window.open(opts.url);
}
}
bricks.Factory.register('NewWindow', bricks.NewWindow);
bricks.Factory.register('Iframe', bricks.Iframe);

3
bricks/image.js Executable file → Normal file
View File

@ -46,8 +46,7 @@ bricks.Image = class extends bricks.JsWidget {
// 获取画布数据并转换为 base64
var dataURL = canvas.toDataURL('image/png'); // 可以根据需要修改图像格式
dataURL = this.removeBase64Header(dataURL);
console.log(dataURL);
// dataURL = this.removeBase64Header(dataURL);
return dataURL;
}
set_url(url){

BIN
bricks/imgs/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
bricks/imgs/app_delete.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
bricks/imgs/app_minimax.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
bricks/imgs/camera.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
bricks/imgs/dislike.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
bricks/imgs/input.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

BIN
bricks/imgs/search.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
bricks/imgs/tag.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

203
bricks/input.js Executable file → Normal file
View File

@ -17,6 +17,9 @@ bricks.UiType =class extends bricks.Layout {
}
return o;
}
set_formdata(formdata){
formdata.append(this.name, this.resultValue());
}
resultValue(){
return this.value;
}
@ -46,6 +49,47 @@ bricks.UiType =class extends bricks.Layout {
}
}
bricks.UiAudioRecorder = class extends bricks.AudioRecorder {
constructor(opts){
super(opts);
this.uitype = 'audiorecorder';
this.name = this.opts.name;
this.required = opts.required || false;
this.value = opts.value || opts.defaultvalue||'';
this.bind('record_ended', this.getValue.bind(this));
}
getValue(){
var o = {}
this.value = this.recordData.data;
o[this.name] = this.value;
return o;
}
set_formdata(formdata){
formdata.append(this.name, this.value, 'record.wav');
}
resultValue(){
return this.value;
}
focus(){
this.dom_element.focus();
}
reset(){
}
setValue(v){
return;
}
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;
}
}
bricks.UiAudioText = class extends bricks.UiType {
/*
{
@ -70,10 +114,12 @@ bricks.UiAudioText = class extends bricks.UiType {
clear_text(){
this.text_w.setValue('');
}
getValue(){
return this.text_w.getValue();
resultValue(){
this.value = this.text_w.resultValue();
return this.value;
}
setValue(v){
this.value = v;
this.text_w.setValue(v);
}
set_result_text(event){
@ -203,6 +249,105 @@ bricks.UiStr =class extends bricks.UiType {
}
}
bricks.UiSearch = class extends bricks.HBox {
/*
search_url:
valueField
textField
select_event
popup_options
value,
text
*/
constructor(opts){
super(opts);
this.uitype = 'search';
var inputdesc = {
name:this.name,
value:this.text,
readonly:true
}
this.str_in = new bricks.UiStr(inputdesc);
this.search_icon = new bricks.Icon({
url:bricks_resource('imgs/search.png'),
rate:1.5
});
this.str_in.set_css('filler');
this.search_icon.set_css('clickable');
this.search_icon.bind('click', this.open_search_window.bind(this));
this.add_widget(this.str_in);
this.add_widget(this.search_icon);
}
async open_search_window(event){
var desc = {
"widgettype":"urlwidget",
"options":{
"url":this.search_url
}
}
var w = await bricks.widgetBuild(desc, this);
var tabular = null;
for (var i=0;i<w.children.length;i++){
if (w.children[i] instanceof bricks.Tabular){
tabular = w.children[i];
}
}
if (!tabular){
console.log('tubular not found is w');
return;
}
var win = bricks.default_popupwindow(this.popup_options||{});
win.add_widget(w);
win.open();
tabular.bind(this.search_event, this.set_data.bind(this));
tabular.bind(this.search_event, win.dismiss.bind(win));
}
set_data(event){
var d = event.params;
this.value = d[this.valueField];
this.str_in.setValue(d[this.textField])
}
getValue(){
var o = {}
o[this.name] = this.resultValue();
var d = this.getUserData();
if (d){
o = bricks.extend(o, d);
}
return o;
}
set_formdata(formdata){
formdata.append(this.name, this.resultValue());
}
resultValue(){
return this.value;
}
focus(){
this.str_in.dom_element.focus();
}
reset(){
var v = this.opts.value||this.opts.defaultvalue|| '';
this.setValue(v);
this.str_in.setValue(this.opts.text||'');
}
setValue(v, t){
if (! v)
v = '';
this.vlaue = v;
this.str_in.setValue(t);
}
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;
}
}
bricks.UiPassword =class extends bricks.UiStr {
/*
{
@ -265,6 +410,7 @@ bricks.UiFloat =class extends bricks.UiInt {
this.dom_element.step = step;
}
resultValue(){
super.resultValue();
return parseFloat(this.value);
}
setValue(v){
@ -331,7 +477,7 @@ bricks.UiFile =class extends bricks.UiStr {
return this.dom_element.files;
}
resultValue(){
return this.get_files();
return this.get_files()[0];
}
setValue(v){
return;
@ -344,6 +490,12 @@ bricks.UiImage =class extends bricks.VBox {
opts.name = opts.name || 'image';
super(opts);
this.uitype='image';
this.camera_w = new bricks.Icon({
url:bricks_resource('imgs/camera.png'),
tip:'use cemera to take a picture',
rate:2});
this.add_widget(this.camera_w);
this.camera_w.bind('click', this.take_photo.bind(this));
this.bind('drop', this.dropHandle.bind(this));
this.input = document.createElement('input');
this.input.type = 'file';
@ -354,6 +506,27 @@ bricks.UiImage =class extends bricks.VBox {
this.dom_element.appendChild(this.input);
this.imgw = null;
}
take_photo(event){
event.stopPropagation();
var camera = new bricks.Camera({
"archor":"cc",
"auto_open":true,
"height":"90%",
"width":"90%"
});
camera.bind('shot', this.accept_photo.bind(this, camera));
}
accept_photo(camera, event){
camera.dismiss();
if (this.imgw){
this.remove_widget(this.imgw);
}
this.imgw = new bricks.Image({
url:event.params,
width:'100%'
});
this.add_widget(this.imgw);
}
handleFileSelect(event){
const file = event.target.files[0];
this.show_image(file);
@ -367,7 +540,11 @@ bricks.UiImage =class extends bricks.VBox {
show_image(file) {
const reader = new FileReader();
reader.onload = (e) => {
this.clear_widgets();
if (this.imgw){
this.remove_widget(this.imgw);
}
this.value = e.target.result;
console.log('this.value=', this.value);
this.imgw = new bricks.Image({
url:e.target.result,
width:'100%'
@ -376,10 +553,22 @@ bricks.UiImage =class extends bricks.VBox {
};
reader.readAsDataURL(file);
}
set_formdata(fd){
// fd.append(this.name, this.resultValue(), 'test.png');
fd.append(this.name, this.resultValue());
}
resultValue(){
if (this.imgw){
this.value = this.imgw.base64();
return this.value;
}
return null;
}
getValue(){
var ret = {}
if (this.imgw){
ret[this.name] = this.imgw.base64()
// ret[this.name] = this.imgw.base64()
ret[this.name] = this.value;
} else {
ret[this.name] = null;
}
@ -718,7 +907,7 @@ bricks.UiCode =class extends bricks.UiType {
e.removeChild(e.firstChild);
}
var v = this.opts.value || this.opts.defaultvalue || null;
if (!v && ! this.opts.nullable){
if (!v && ! this.opts.nullable && data.length > 0){
v = data[0][this.opts.valueField]
}
if (this.opts.nullable){
@ -861,6 +1050,7 @@ bricks.UiVideo =class extends bricks.UiStr {
}
}
var Input = new bricks._Input();
Input.register('UiAudioRecorder', 'audiorecorder', bricks.UiAudioRecorder);
Input.register('UiStr', 'str', bricks.UiStr);
Input.register('UiHide', 'hide', bricks.UiHide);
Input.register('UiTel', 'tel', bricks.UiTel);
@ -878,3 +1068,4 @@ Input.register('UiPassword', 'password', bricks.UiPassword);
Input.register('UiAudio', 'audio', bricks.UiAudio);
Input.register('UiVideo', 'video', bricks.UiVideo);
Input.register('UiAudioText', 'audiotext', bricks.UiAudioText);
Input.register('UiSearch', 'search', bricks.UiSearch);

131
bricks/jsoncall.js Executable file → Normal file
View File

@ -44,9 +44,9 @@ bricks.HttpText = class {
};
bricks.extend(this.headers, headers);
var width=0, height=0;
var is_mobile = 0
var is_mobile = '0'
if (bricks.is_mobile()){
is_mobile = 1;
is_mobile = '1';
}
if (bricks.app) {
width = bricks.app.screenWidth();
@ -77,8 +77,6 @@ bricks.HttpText = class {
return await resp.text();
}
add_own_params(params){
if (! params)
params = {};
var session = bricks.app.get_session();
if (params instanceof FormData){
for ( const [key, value] of Object.entries(this.params)){
@ -89,6 +87,8 @@ bricks.HttpText = class {
}
return params;
}
if (! params)
params = {};
var p = bricks.extend({}, this.params);
p = bricks.extend(p, params);
if (session){
@ -102,46 +102,66 @@ bricks.HttpText = class {
}
return Object.assign(this.headers, headers);
}
async httpcall(url, {method='GET', headers=null, params=null}={}){
async bricks_fetch(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;
if (data instanceof FormData){
method = 'POST';
_params.body = data;
} else {
if (data instanceof FormData){
_params.body = data;
if (method == 'GET' || method == 'HEAD') {
let pstr = url_params(data);
url = url + '?' + pstr;
} 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;
}
return await fetch(url, _params);
}
async httpcall(url, opts){
const fetchResult = await this.bricks_fetch(url, {
method: opts.method,
headers: opts.headers,
params: opts.params});
if (fetchResult.status == 401 && bricks.app.login_url){
console.log('go to login')
return await this.withLoginInfo(url, _params);
}
const resp_error = {
"type":"Error",
"message":result.message || 'Something went wrong',
"data":result.data || '',
"code":result.code || ''
};
error.info = resp_error;
return error;
if (fetchResult.status == 403){
var w = new bricks.Error({
"title":"access error",
"auto_open":true,
"message":"you are forbidden to access it",
"archor":"cc",
"cheight":10,
"cwidth":16,
"timeout":5
});
return null;
}
if (! fetchResult.ok){
var w = new bricks.Error({
"title":"access error",
"auto_open":true,
"message":"server return error with error code" + fetchResult.status,
"archor":"cc",
"cheight":10,
"cwidth":16,
"timeout":5
});
return null;
}
var result = await this.get_result_data(fetchResult);
var ck = objget(fetchResult.headers, 'Set-Cookie');
if (ck){
var session = ck.split(';')[0];
bricks.app.save_session(session);
}
return result;
}
async withLoginInfo(url, params){
var get_login_info = function(e){
@ -149,50 +169,13 @@ bricks.HttpText = class {
return e.target.getValue();
}
var w = await bricks.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;
return null;
}
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',
@ -209,6 +192,18 @@ bricks.HttpText = class {
}
}
bricks.HttpArrayBuffer = class extends bricks.HttpText {
async get_result_data(resp){
return await resp.arrayBuffer();
}
}
bricks.HttpBin = class extends bricks.HttpText {
async get_result_data(resp){
return await resp.blob();
}
}
bricks.HttpResponse = class extends bricks.HttpText {
async get_result_data(resp){
return resp;

18
bricks/keypress.js Normal file
View File

@ -0,0 +1,18 @@
var bricks = window.bricks || {};
bricks.KeyPress = class extends bricks.VBox {
constructor(opts){
super(opts);
bricks.app.bind('keydown', this.key_handler.bind(this));
}
key_handler(event){
var key = event.key;
if (!key) return;
this.clear_widgets();
var w = new bricks.Text({text:'key press is:' + key});
this.add_widget(w)
}
}
bricks.Factory.register('KeyPress', bricks.KeyPress);

67
bricks/layout.js Executable file → Normal file
View File

@ -4,17 +4,37 @@ bricks.key_selectable_stack = [];
bricks.Layout = class extends bricks.JsWidget {
constructor(options){
if (! options){
options = {};
}
super(options);
this._container = true;
this.keyselectable = options.keyselectable || false;
this.children = [];
if (this.use_key_select){
this.enable_key_select();
}
}
build_title(){
if (this.title){
this.title_w = new bricks.Title3({otext:this.title, i18n:true, dynsize:true});
this.add_widget(this.title_w);
}
}
build_description(){
if (this.description){
this.description_w = new bricks.Text({otext:this.description,
i18n:true, dynsize:true
});
this.add_widget(this.description_w);
}
}
set_key_select_items(){
this.key_select_items = this.children;
}
enable_key_select(){
if (!this.keyselectable) return;
this.keyselectable = true;
this.set_key_select_items();
this.selected_children = null;
bricks.app.bind('keydown', this.key_handler.bind(this));
bricks.key_selectable_stack.push(this)
this.select_default_item();
@ -25,9 +45,9 @@ bricks.Layout = class extends bricks.JsWidget {
return bricks.key_selectable_stack[p] == this;
}
disable_key_select(){
if (!this.keyselectable) return;
this.keyselectable = false;
bricks.app.unbind('keydown', this.key_handler.bind(this));
if (this.is_currkeyselectable()){
bricks.app.unbind('keydown', this.key_handler.bind(this));
this.select_item.selected(false);
this.select_item = null;
bricks.key_selectable_stack.pop();
@ -35,7 +55,8 @@ bricks.Layout = class extends bricks.JsWidget {
return;
}
select_item(w){
if (!this.keyselectable) return;
if (!w) return;
// if (!this.keyselectable) return;
if (this.selected_item){
this.selected_item.selected(false);
}
@ -86,25 +107,31 @@ bricks.Layout = class extends bricks.JsWidget {
}
down_level(){
this.set_key_select_items();
for (var i=0;i<this.key_select_items.length;i++){
var w = this.key_select_items[i];
if(w.keyselectable){
w.enable_key_select();
return true;
}
try {
if (w.down_level()){
return true;
}
} catch (e){
;
}
var w = this.selected_item;
if (! w) return;
if (! w.keyselectable){
w = w.find_first_keyselectable_child();
if (! w) return;
}
return false;
bricks.key_selectable_stack.push(this);
this.disable_key_select();
w.enable_key_select();
}
find_first_keyselectable_child(){
for (var i=0;i<this.children;i++){
if (this.children[i].keyselectable){
return this.children[i];
}
var sw = this.children[i].find_first_keyselectable_child();
if (sw) return sw;
}
return null
}
enter_handler(){
if (! this.selected_item) {
return;
}
this.selected_item.dispatch('click');
this.down_level();
}
key_handler(event){
if (!this.is_currkeyselectable()){

View File

@ -41,7 +41,7 @@ bricks.LlmMsgAudio = class extends bricks.UpStreaming {
return resp;
}
}
bricks.ModelOutput = class extends bricks.HBox {
bricks.ModelOutput = class extends bricks.VBox {
/* {
icon:
output_view:
@ -53,16 +53,29 @@ bricks.ModelOutput = class extends bricks.HBox {
opts = {};
}
opts.width = '100%';
opts.height = 'auto';
super(opts);
var hb = new bricks.HBox({width:'100%', cheight:2});
this.img = new bricks.Icon({
rate:2,
tip:this.opts.model,
url:this.icon||bricks_resource('imgs/llm.png')
});
hb.add_widget(this.img);
var mname = new bricks.Text({text:this.opts.model});
hb.add_widget(mname);
this.add_widget(hb);
this.content = new bricks.HBox({width:'100%'});
this.add_widget(this.content);
this.logid = null;
this.img = new bricks.Icon({rate:2,url:this.icon||bricks_resource('imgs/llm.png')});
this.run = new bricks.BaseRunning({target:this});
this.add_widget(this.img);
this.add_widget(this.run);
this.content.add_widget(this.run);
this.filler = new bricks.VBox({});
this.filler.set_css('filler');
this.add_widget(this.filler);
this.add_widget(new bricks.BlankIcon({rate:2, flexShrink:0}));
this.content.add_widget(new bricks.BlankIcon({rate:2, flexShrink:0}));
this.content.add_widget(this.filler);
this.content.add_widget(new bricks.BlankIcon({rate:2, flexShrink:0}));
this.build_estimate_widgets();
}
build_estimate_widgets(){
@ -104,7 +117,7 @@ bricks.ModelOutput = class extends bricks.HBox {
async update_data(data){
if (this.run) {
this.run.stop_timepass();
this.remove_widget(this.run);
this.content.remove_widget(this.run);
if(this.textvoice){
this.upstreaming = new bricks.UpStreaming({
url:this.tts_url
@ -116,9 +129,18 @@ bricks.ModelOutput = class extends bricks.HBox {
this.upstreaming.send(data.content);
}
this.run = null;
var w = await bricks.widgetBuild(this.output_view, this.llmio, data);
w.set_css('llm_msg');
this.filler.clear_widgets();
if (typeof this.output_view === 'string'){
this.output_view = JSON.parse(this.output_view);
}
var desc = bricks.apply_data(this.output_view, data);
var w = await bricks.widgetBuild(desc, this.llmio);
if (! w){
console.log('widgetBuild() return null, desc=', this.output_view, desc, 'data=', data);
return;
}
w.set_css('llm_msg');
w.set_style('width', '100%');
this.filler.add_widget(w);
this.filler.add_widget(this.estimate_w);
if (data.logid){
@ -157,57 +179,74 @@ bricks.LlmModel = class extends bricks.JsWidget {
constructor(llmio, opts){
super(opts);
this.llmio = llmio;
if (opts.use_session){
this.messages = [];
}
this.messages = [];
}
render_title(){
var w = new bricks.HBox({});
var w = new bricks.HBox({padding:'15px'});
w.bind('click', this.show_setup_panel.bind(this))
var img = new bricks.Icon({rate:2,url:this.opts.icon||bricks_resource('imgs/llm.png')});
var txt = new bricks.Text({text:this.opts.model});
var img = new bricks.Icon({
rate:2,
tip:this.opts.model,
url:this.opts.icon||bricks_resource('imgs/llm.png')
});
// var txt = new bricks.Text({text:this.opts.model});
w.add_widget(img);
w.add_widget(txt);
// w.add_widget(txt);
return w;
}
show_setup_panel(event){
}
async model_inputed(data){
var mout = new bricks.ModelOutput({
textvoice:this.textvoice,
tts_url:this.tts_url,
icon:this.opts.icon,
estimate_url:this.llmio.estimate_url,
output_view:this.opts.output_view});
this.llmio.o_w.add_widget(mout);
var fmt = this.opts.user_message_format || { role:'user', content:'${prompt}'};
var umsg = bricks.apply_data(fmt, data);
var d = data;
if (this.messages){
this.messages.push(umsg);
d = bricks.extend({}, data);
d.messages = this.messages;
d.model = this.opts.model;
d.modelinstanceid = this.opts.modelinstanceid;
d.modeltypeid = this.opts.modeltypeid;
inputdata2uploaddata(data){
var d;
if (data instanceof FormData){
d = bricks.formdata_copy(data);
} else {
d = bricks.extend({}, data);
d = objcopy(data);
}
var fmt = this.opts.user_message_format;
if (fmt){
var umsg = bricks.apply_data(fmt, inputdata2dic(data));
this.messages.push(umsg);
}
if (data instanceof FormData){
d.append('model', this.opts.model)
d.append('modelinstanceid', this.opts.modelinstanceid)
d.append('modeltypeid', this.opts.modeltypeid)
d.append('messages', JSON.stringify(this.messages));
} else {
d.messages = JSON.stringify(this.messages);
d.model = this.opts.model;
d.mapi = this.mapi;
d.modelinstanceid = this.opts.modelinstanceid;
d.modeltypeid = this.opts.modeltypeid;
}
console.log('upload data=', d, this.options);
return d;
}
async model_inputed(data){
if (!opts.use_session){
this.messages = [];
}
var mout = new bricks.ModelOutput({
textvoice:this.textvoice,
tts_url:this.tts_url,
icon:this.opts.icon,
model:this.opts.model,
estimate_url:this.llmio.estimate_url,
output_view:this.opts.output_view});
this.llmio.o_w.add_widget(mout);
if (this.response_mode == 'stream' || this.response_mode == 'async') {
var d = this.inputdata2uploaddata(data);
console.log('data_inouted=', data, 'upload_data=', d);
var hr = new bricks.HttpResponseStream();
var resp = await hr.post(this.opts.url, {params:d});
await hr.handle_chunk(resp, this.chunk_response.bind(this, mout));
this.chunk_ended();
} else {
var d = this.inputdata2uploaddata(data);
var hj = new bricks.HttpJson()
var resp = await hj.post(this.opts.url, {params:d});
if (this.response_mode == 'sync'){
resp.content = bricks.escapeSpecialChars(resp.content)
mout.update_data(resp);
if (this.messages){
var msg = this.llm_msg_format();
@ -236,7 +275,7 @@ bricks.LlmModel = class extends bricks.JsWidget {
d.content = bricks.escapeSpecialChars(d.content);
this.resp_data = d;
mout.update_data(d);
console.log('stream data=', d);
// console.log('stream data=', d);
}
chunk_ended(){
if (! this.messages) {
@ -255,6 +294,7 @@ bricks.LlmIO = class extends bricks.VBox {
options:
{
user_icon:
list_models_url:
input_fields:
input_view:
output_view:
@ -278,31 +318,158 @@ bricks.LlmIO = class extends bricks.VBox {
constructor(opts){
super(opts);
this.llmmodels = [];
this.title_w = new bricks.HBox({cheight:2});
this.i_w = new bricks.VBox({cheight:5});
this.title_w = new bricks.HBox({cheight:3});
var bottom_box = new bricks.HBox({cheight:3});
this.i_w = new bricks.Icon({
rate:2,
url:bricks_resource('imgs/input.png'),
margin:'14px',
tip:'input data',
css:'clickable'
});
this.nm_w = new bricks.Icon({
rate:2,
url:bricks_resource('imgs/add.png'),
margin:'14px',
tip:'add new model',
css:'clickable'
});
bottom_box.add_widget(this.i_w);
bottom_box.add_widget(this.nm_w);
this.nm_w.bind('click', this.open_search_models.bind(this));
this.i_w.bind('click', this.open_input_widget.bind(this));
this.o_w = new bricks.Filler({overflow:'auto'});
this.add_widget(this.title_w);
this.add_widget(this.o_w);
if (this.models.length < 2 && this.tts_url){
this.textvoice = true;
}
this.add_widget(this.i_w);
this.add_widget(bottom_box);
this.models.forEach( m =>{
if (this.textvoice){
m.textvoice = true;
m.tts_url = this.tts_url;
}
var lm = new bricks.LlmModel(this, m);
this.llmmodels.push(lm);
var tw = lm.render_title();
this.title_w.add_widget(tw);
this.show_added_model(m);
});
this.build_input();
}
show_added_model(m){
if (this.textvoice){
m.textvoice = true;
m.tts_url = this.tts_url;
}
var lm = new bricks.LlmModel(this, m);
this.llmmodels.push(lm);
var tw = lm.render_title();
this.title_w.add_widget(tw);
}
async open_search_models(event){
event.preventDefault();
event.stopPropagation();
var rect = this.showRectage();
var opts = {
title:"select model",
icon:bricks_resource('imgs/search.png'),
auto_destroy:true,
auto_open:true,
auto_dismiss:false,
movable:true,
top:rect.top + 'px',
left:rect.left + 'px',
width: rect.right - rect.left + 'px',
height: rect.bottom - rect.top + 'px'
}
var w = new bricks.PopupWindow(opts);
var sopts = {
data_url:this.list_models_url,
data_params:{
mii:this.models[0].modelinstanceid,
mti:this.models[0].modeltypeid
},
data_method:'POST',
col_cwidth: 24,
record_view:{
widgettype:"VBox",
options:{
cheight:20,
css:"card"
},
subwidgets:[
{
widgettype:"Title4",
options:{
text:"${name}"
}
},
{
widgettype:"Filler",
options:{
css:"scroll"
},
subwidgets:[
{
widgettype:"VBox",
options:{
css:"subcard"
},
subwidgets:[
{
widgettype:"Text",
options:{
text:"模型描述:${description}",
wrap:true
}
},
{
widgettype:"Text",
options:{
text:"启用日期:${enable_date}"
}
}
]
}
]
}
]
}
};
var cols = new bricks.Cols(sopts);
cols.bind('record_click', this.add_new_model.bind(this));
cols.bind('record_click', w.dismiss.bind(w));
w.add_widget(cols);
w.open();
}
async add_new_model(event){
event.preventDefault();
event.stopPropagation();
this.models.push(event.params);
this.show_added_model(event.params);
}
async open_input_widget(event){
event.preventDefault();
event.stopPropagation();
var rect = this.showRectage();
var opts = {
title:"input data",
icon:bricks_resource('imgs/input.png'),
auto_destroy:true,
auto_open:true,
auto_dismiss:false,
movable:true,
top:rect.top + 'px',
left:rect.left + 'px',
width: rect.right - rect.left + 'px',
height: rect.bottom - rect.top + 'px'
}
var w = new bricks.PopupWindow(opts);
var fopts = {
fields:this.input_fields
}
var fw = new bricks.Form(fopts);
fw.bind('submit', this.handle_input.bind(this));
fw.bind('submit', w.destroy.bind(w));
w.add_widget(fw);
w.open();
}
async handle_input(event){
var params = event.params;
if (params.prompt)
params.prompt = bricks.escapeSpecialChars(params.prompt);
await this.show_input(params);
for(var i=0;i<this.llmmodels.length;i++){
var lm = this.llmmodels[i];
@ -314,7 +481,9 @@ bricks.LlmIO = class extends bricks.VBox {
}
async show_input(params){
var box = new bricks.HBox({width:'100%'});
var w = await bricks.widgetBuild(this.input_view, this.o_w, params);
var data = inputdata2dic(params);
console.log('data=', data, 'input_view=', this.input_view);
var w = await bricks.widgetBuild(this.input_view, this.o_w, data);
w.set_css(this.msg_css||'user_msg');
w.set_css('filler');
var img = new bricks.Icon({rate:2,url:this.user_icon||bricks_resource('imgs/user.png')});
@ -323,14 +492,6 @@ bricks.LlmIO = class extends bricks.VBox {
box.add_widget(img);
this.o_w.add_widget(box);
}
build_input(){
var fopts = {
fields:this.input_fields
};
var fw = new bricks.InlineForm(fopts);
fw.bind('submit', this.handle_input.bind(this));
this.i_w.add_widget(fw);
}
}
bricks.Factory.register('LlmIO', bricks.LlmIO);

View File

@ -96,13 +96,12 @@ bricks.LlmMsgBox = class extends bricks.HBox {
var d = {
messages:this.messages,
mapi:this.mapi,
prompt:prompt,
model:this.model,
}
// console.log('messages=', this.messages, 'msg=', msg, 'umsg=', umsg);
if (this.response_mode == 'stream') {
var hr = new bricks.HttpResponseStream();
var resp = await hr.post(this.url, {params:d});
var resp = await hr.post(this.url, {data:d});
this.responsed();
await hr.handle_chunk(resp, this.chunk_response.bind(this));
this.chunk_ended();

0
bricks/markdown_viewer.js Executable file → Normal file
View File

28
bricks/menu.js Executable file → Normal file
View File

@ -11,6 +11,8 @@ bricks.Menu = class extends bricks.VBox {
super(options);
this.dom_element.style.display = "";
this.dom_element.style.backgroundColor = options.bgcolor || "white";
this.build_title();
this.build_description();
this.create_children(this, this.opts.items);
this.bind('item_click', this.menu_clicked.bind(this));
}
@ -24,12 +26,19 @@ bricks.Menu = class extends bricks.VBox {
console.log(event);
let e = event.target;
let opts = event.params;
if (! opts.url){
console.log('itme.url is null');
this.dispatch('command', opts);
return;
var t;
var popts;
if (this.target == 'PopupWindow'){
popts = bricks.get_popupwindow_default_options();
bricks.extend(popts, this.popup_options || {});
t = new bricks.PopupWindow(popts);
} else if (this.target == 'Popup'){
popts = bricks.get_popup_default_options();
bricks.extend(popts, this.popup_options || {});
t = new bricks.Popup(popts);
} else {
t = bricks.getWidgetById(this.target);
}
var t = bricks.getWidgetById(this.target);
if (t){
var desc = {
"widgettype":"urlwidget",
@ -38,7 +47,7 @@ bricks.Menu = class extends bricks.VBox {
}
}
var w = await bricks.widgetBuild(desc, this);
if (w){
if (w && ! w.parent){
t.clear_widgets();
t.add_widget(w);
} else {
@ -47,6 +56,7 @@ bricks.Menu = class extends bricks.VBox {
} else {
console.log('menu_clicked():', this.target, 'not found')
}
this.dispatch('command', opts);
}
create_children(w, items){
for (let i=0;i<items.length;i++){
@ -59,13 +69,17 @@ bricks.Menu = class extends bricks.VBox {
itw.add_widget(subw);
itw.add_widget(w1);
this.create_children(w1, item.items);
subw.bind('click', w1.toggle_hide.bind(w1));
subw.bind('click', this.items_toggle_hide.bind(this, w1));
} else {
subw.bind('click', this.regen_menuitem_event.bind(this, item))
w.add_widget(subw);
}
}
}
items_toggle_hide(w, event){
w.toggle_hide();
event.stopPropagation();
}
create_menuitem(item){
var w = new bricks.HBox({cheight:this.item_cheight||2});
var iw, tw;

View File

@ -1,6 +1,6 @@
var bricks = window.bricks || {};
bricks.Message = class extends bricks.Modal {
bricks.Message = class extends bricks.PopupWindow {
/*
{
title:
@ -11,55 +11,41 @@ bricks.Message = class extends bricks.Modal {
opts.auto_open = true;
super(opts);
this.create_message_widget();
this.panel.set_css('message');
this.set_css('message');
}
create_message_widget(){
this.message_w = new bricks.VBox({width:'100%',height:'100%'});
var w = new bricks.Filler();
this.message_w.add_widget(w);
this.add_widget(w);
var w1 = new bricks.VScrollPanel({});
w.add_widget(w1);
var t = new bricks.Text({otext:this.opts.message,
wrap:true,
halign:'middle',
i18n:true});
w.add_widget(t);
this.add_widget(this.message_w);
w1.add_widget(t);
}
}
bricks.Error = class extends bricks.Message {
constructor(opts){
super(opts);
this.panel.set_css('error');
this.set_css('error');
}
}
bricks.show_error = function(opts){
opts.cheight = opts.cheight || 9;
opts.cwidth = opts.cwidth || 16;
var w = new bricks.Error(opts);
w.open();
}
bricks.show_message = function(opts){
opts.cheight = opts.cheight || 9;
opts.cwidth = opts.cwidth || 16;
var w = new bricks.Message(opts);
w.open();
}
bricks.PopupForm = class extends bricks.VBox {
/*
{
form:{
}
}
*/
constructor(options){
super(options);
this.form = new bricks.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();
}
}
bricks.Factory.register('Message', bricks.Message);
bricks.Factory.register('Error', bricks.Error);

0
bricks/minify_tools.txt Executable file → Normal file
View File

5
bricks/modal.js Executable file → Normal file
View File

@ -167,7 +167,7 @@ bricks.Modal = class extends bricks.BaseModal {
*/
}
bricks.ModalForm = class extends bricks.Modal {
bricks.ModalForm = class extends bricks.PopupWindow {
/*
{
auto_open:
@ -189,6 +189,9 @@ bricks.ModalForm = class extends bricks.Modal {
super(opts);
this.build_form();
}
_getValue(){
return this.form._getValue();
}
getValue(){
return this.form.getValue();
}

0
bricks/myoperator.js Executable file → Normal file
View File

99
bricks/period.js Normal file
View File

@ -0,0 +1,99 @@
var bricks = window.bricks || {};
bricks.str2date = function(sdate){
let [year, month, day] = sdate.split("-");
var dateObj = new Date(year, month - 1, day);
return dateObj;
}
bricks.date2str = function(date){
let year = date.getFullYear();
let month = String(date.getMonth() + 1).padStart(2, '0');
let day = String(date.getDate()).padStart(2, '0');
let formattedDate = `${year}-${month}-${day}`;
return formattedDate;
}
bricks.addMonths = function(dateObj, months){
var newDate = new Date(dateObj);
newDate.setMonth(newDate.getMonth() + months);
return newDate;
}
bricks.addYears = function(dateObj, years){
const newDate = new Date(dateObj);
newDate.setYear(newDate.getYear() + years);
return newDate;
}
bricks.addDays = function(dateObj, days){
var newdate = new Date(dateObj);
newdate.setDate(newdate.getDate() + days);
return newdate;
}
bricks.PeriodDays = class extends bricks.HBox {
/*
{
start_date:
end_date:
step_type: 'days', 'months', 'years'
step_cnt:
title:'',
splitter:' -- '
}
event: 'changed';
*/
constructor(opts){
opts.splitter = opts.splitter || ' 至 ';
opts.step_cnt = opts.step_cnt || 1;
super(opts);
this.start_w = new bricks.Text({
text:opts.start_date
});
this.end_w = new bricks.Text({
text:opts.end_date
});
this.start_w.set_css('clickable');
this.end_w.set_css('clickable');
this.start_w.bind('click', this.step_back.bind(this));
this.end_w.bind('click', this.step_forward.bind(this));
if (this.title){
this.add_widget(new bricks.Text({otext:this.title, i18n:true}));
}
this.add_widget(this.start_w);
this.add_widget(new bricks.Text({
otext:this.splitter,
i18n:true
}));
this.add_widget(this.end_w);
}
date_add(strdate, step_cnt, step_type){
var date = bricks.str2date(strdate);
switch(step_type){
case 'years':
var nd = bricks.addYears(date, step_cnt);
return bricks.date2str(nd);
break;
case 'months':
var nd = bricks.addMonths(date, step_cnt);
return bricks.date2str(nd);
break;
default:
var nd = bricks.addDays(date, step_cnt);
return bricks.date2str(nd);
break;
}
}
step_back(){
this.start_date = this.date_add(this.start_date, -this.step_cnt, this.step_type);
this.end_date = this.date_add(this.end_date, -this.step_cnt, this.step_type);
this.start_w.set_text(this.start_date);
this.end_w.set_text(this.end_date);
this.dispatch('changed', {start_date:this.start_date, end_date:this.end_date});
}
step_forward(){
this.start_date = this.date_add(this.start_date, this.step_cnt, this.step_type);
this.end_date = this.date_add(this.end_date, this.step_cnt, this.step_type);
this.start_w.set_text(this.start_date);
this.end_w.set_text(this.end_date);
this.dispatch('changed', {start_date:this.start_date, end_date:this.end_date});
}
}
bricks.Factory.register('PeriodDays', bricks.PeriodDays);

View File

@ -1,87 +1,482 @@
var bricks = window.bricks || {};
bricks.get_popup_default_options = function(){
return {
timeout:0,
archor:'cc',
auto_open:true,
auto_dismiss:true,
auto_destroy:true,
movable:true,
resizable:false,
modal:true
}
}
bricks.Popup = class extends bricks.VBox {
/*
{
timeout:0
auto_open:
archor:one of ['tl', 'tc', 'tr', 'cl', 'cc', 'cr', 'bl','bc', 'br']
widget:null for bricks.Body, string value for widget's id or a widget object;
auto_dismiss:
auto_open:boolean
auto_dismiss:boolean
auto_destroy:boolean
movable:boolean
dismiss_event:
resizable:boolean
modal:boolean
content:{}
*/
constructor(opts){
super(opts);
this.no_opened = true;
this.auto_task = null;
this.issub = false;
this.set_css('modal');
const zindex = bricks.app.new_zindex();
this.set_style('zIndex', zindex);
this.opened = false;
this.set_css('popup');
this.bring_to_top();
this.content_box = new bricks.VBox({height:'100%',width:'100%'});
super.add_widget(this.content_box);
this.content_w = this.content_box;
this.moving_w = this;
if (this.auto_dismiss){
bricks.Body.bind('click', this.click_outside.bind(this));
}
this.target_w = bricks.Body;
this.moving_status = false;
if (this.movable){
this.setup_movable();
// console.log('movable ...');
}
if (this.resizable){
this.setup_resizable();
}
this.set_style('display', 'none');
bricks.Body.add_widget(this);
this.bind('click', this.bring_to_top.bind(this));
if (this.auto_open){
this.open();
}
if (this.content){
this.bind('opened', this.load_content.bind(this))
}
}
async load_content(){
var w = await bricks.widgetBuild(this.content, this);
if (w){
this.set_dismiss_events(w);
this.content_w.clear_widgets();
this.content_w.add_widget(w);
}
}
set_dismiss_events(w){
if (!this.dismiss_events) return;
this.dismiss_events.forEach(ename => {
w.bind(ename, this.destroy.bind(this));
});
}
bring_to_top(){
if (this == bricks.app.toppopup){
return;
}
if (bricks.app.toppopup)
bricks.app.toppopup.set_css('toppopup', true);
this.zindex = bricks.app.new_zindex();
this.set_style('zIndex', this.zindex);
console.log('this.zindex=', this.zindex, 'app.zindex=', bricks.app.zindex);
this.set_css('toppopup');
bricks.app.toppopup = this;
}
popup_from_widget(from_w){
var myrect = this.showRectage();
var rect = from_w.showRectage();
var x,y;
var ox, oy;
ox = (rect.right - rect.left) / 2 + rect.left;
oy = (rect.bottom - rect.top) / 2 + rect.top;
if (ox < bricks.app.screenWidth() / 2) {
x = rect.right + 3;
if (x + (myrect.right - myrect.left) > bricks.app.screenWidth()){
x = bricks.app.screenWidth() - (myrect.right - myrect.left);
}
} else {
x = rect.left - (myrect.right - myrect.left) - 3
if (x < 0) x = 0;
}
if (oy < bricks.app.screenHeight() / 2){
y = rect.bottom + 3;
if (y + (myrect.bottom - myrect.top) > bricks.app.screenHeight()){
y = bricks.app.screenHeight() - (myrect.bottom - myrect.top);
}
} else {
y = rect.bottom - (myrect.bottom - myrect.top) - 3
if (y < 0) y = 0;
}
this.set_style('top', y + 'px');
this.set_style('left', x + 'px');
}
setup_resizable(){
this.resizable_w = new bricks.Icon({rate:1.5, url:bricks_resource('imgs/right-bottom-triangle.png')});
super.add_widget(this.resizable_w);
this.resizable_w.set_css('resizebox');
this.resizable_w.bind('mousedown', this.resize_start_pos.bind(this));
bricks.Body.bind('mousemove', this.resizing.bind(this));
bricks.Body.bind('mouseup', this.stop_resizing.bind(this));
}
resize_start_pos(e){
if (e.target != this.resizable_w.dom_element)
{
// console.log('not event target');
return;
}
var rect = this.showRectage();
this.resize_status = true;
this.s_offsetX = e.clientX;
this.s_offsetY = e.clientY;
this.s_width = rect.width;
this.s_height = rect.height;
e.preventDefault();
// console.log('resize_start_pos():', this.s_width, this.s_height, this.s_offsetX, this.s_offsetY);
}
resizing(e){
if (e.target != this.resizable_w.dom_element){
this.stop_resizing();
// console.log('resizing(): not event target');
return;
}
if (!this.resize_status){
// console.log('resizing(): not in resize status');
return;
}
var cx, cy;
cx = this.s_width + e.clientX - this.s_offsetX;
cy = this.s_height + e.clientY - this.s_offsetY;
this.set_style('width', cx + 'px');
this.set_style('height', cy + 'px');
// console.log('resizing():', this.resize_status, cx, cy);
e.preventDefault();
}
positify_tl(){
var rect, w, h, t, l;
if (this.top && this.left){
this.set_style('top', this.top);
this.set_style('left', this.left);
return;
}
rect = bricks.app.showRectage();
if (this.cwidth && this.cwidth > 0){
w = this.cwidth * bricks.app.charsize;
} else if (this.width){
if (this.width.endsWith('px')){
w = parseFloat(this.width);
} else {
w = parseFloat(this.width) * rect.width / 100;
}
} else {
w = rect.width * 0.8;
}
if (this.cheight && this.cheight > 0){
h = this.cheight * bricks.app.charsize;
} else if (this.height){
if (this.height.endsWith('px')){
h = parseFloat(this.height);
} else {
h = parseFloat(this.height) * rect.height / 100;
}
} else {
h = rect.height * 0.8;
}
var archor = this.archor || 'cc';
switch(archor[0]){
case 't':
t = 0;
break;
case 'c':
t = (rect.height - h) / 2;
break;
case 'b':
t = rect.height - h;
break;
default:
t = (rect.height - h) / 2;
break;
}
switch(archor[1]){
case 'l':
l = 0;
break;
case 'c':
l = (rect.width - w) / 2;
break;
case 'r':
l = rect.width - w;
break;
default:
l = (rect.width - w) / 2;
break;
}
this.set_style('top', t + 'px');
this.set_style('left', l + 'px');
return {
top:t,
left:l
}
}
stop_resizing(e){
this.resize_status = false;
bricks.Body.unbind('mousemove', this.resizing.bind(this));
bricks.Body.unbind('mouseup', this.stop_resizing.bind(this));
// console.log('stop_resizing():', this.resize_status);
}
setup_movable(){
this.moving_w.bind('mousedown', this.rec_start_pos.bind(this));
this.moving_w.bind('touchstart', this.rec_start_pos.bind(this));
}
rec_start_pos(e){
if (e.target != this.moving_w.dom_element)
{
// console.log('moving star failed', e.target, this.moving_w.dom_element, 'difference ...');
return;
}
this.moving_status = true;
var rect = this.showRectage();
this.offsetX = e.clientX - rect.left;
this.offsetY = e.clientY - rect.top;
// console.log(rect, '========', this.offsetX, this.offsetY, e.clientX, e.clientY);
bricks.Body.bind('mouseup', this.stop_moving.bind(this));
bricks.Body.bind('touchend', this.stop_moving.bind(this));
this.moving_w.bind('mousemove', this.moving.bind(this));
this.moving_w.bind('touchmove', this.moving.bind(this));
e.preventDefault();
// console.log('moving started ...');
}
moving(e){
if (e.target != this.moving_w.dom_element){
// console.log('moving failed', e.target, this.moving_w.dom_element, 'difference ...');
this.stop_moving();
}
if (!this.moving_status){
// console.log('moving failed', 'not started ...');
return;
}
var cx, cy;
cx = e.clientX - this.offsetX;
cy = e.clientY - this.offsetY;
// console.log(cx, cy, e.clientX, e.clientY, this.offsetX, this.offsetY, '==========');
this.set_style('left', cx + 'px');
this.set_style('top', cy + 'px');
e.preventDefault();
}
stop_moving(e){
// console.log('stop moving ....');
this.moving_status = false;
this.moving_w.unbind('mousemove', this.moving.bind(this));
this.moving_w.unbind('touchmove', this.moving.bind(this));
bricks.Body.unbind('mouseup', this.stop_moving.bind(this));
bricks.Body.unbind('touchend', this.stop_moving.bind(this));
}
click_outside(event){
if (event.target != this.dom_element){
this.dismiss();
}
}
add_widget(w, index){
super.add_widget(w, index);
if (this.auto_open){
this.open();
}
}
transform2screen_at(rect, lt){
var screen_rect = bricks.Body.showRectage();
top = rect.top + parseInt(lt.y) / 100 * rect.height;
left = rect.left + parseInt(lt.x) / 100 * rect.width;
return {
top:top + 'px',
left:left + 'px'
}
}
convert_width(){
}
open(){
var rect;
if (this.widget instanceof bricks.JsWidget){
rect = this.widget.showRectage()
this.issub = true;
} else if (this.widget instanceof String){
var w = bricks.getWidgetById(this.widget, bricks.Body);
if (!w){
ithis.issub = true
rect = bricks.Body.showRectage();
}
} else {
rect = bricks.Body.showRectage();
if (!this.parent){
bricks.app.add_widget(this);
}
var lt = archor_at(this.archor);
if (this.issub){
lt = this.transform2screen_at(rect, lt);
if (this.width && this.width.endsWith('%')){
this.set_style('width', parseFloat(rect.width) * parseFloat(this.width) + 'px');
}
if (this.height && this.height.endsWith('%')){
this.set_style('height', parseFloat(rect.height) * parseFloat(this.height) + 'px');
}
var rect, w, h;
if (this.opened) {
return;
}
this.set_style('top',lt.top);
this.set_style('left',lt.left);
this.opened = true;
if (this.no_opened){
if (this.widget instanceof bricks.JsWidget){
this.target_w = this.widget;
this.issub = true;
} else {
var w = bricks.getWidgetById(this.widget, bricks.Body);
if (w){
this.issub = true
this.target_w = w;
}
}
this.positify_tl()
}
this.no_opened = false;
this.set_style('display', 'block');
this.dispatch('opened');
if (this.timeout > 0){
this.auto_task = schedule_once(this.auto_dismiss.bind(this), this.timeout)
this.auto_task = schedule_once(this.dismiss.bind(this), this.timeout)
}
if (this.opts.modal && this.opts.widget){
this.target_w.disabled(true);
}
this.bring_to_top();
}
dismiss(){
if (! this.opened) return;
if (this.opts.modal){
this.target_w.disabled(false);
}
this.opened = false;
if (this.auto_task){
this.auto_task.cancel();
this.auto_task = null;
}
this.set_style('display','none');
this.dispatch('dismissed');
if (this.auto_destroy){
this.destroy();
this.dispatch('destroy');
}
}
destroy(){
if (this.opened){
this.dismiss();
}
if (this.parent){
this.parent.remove_widget(this);
this.parent = null;
}
}
add_widget(w, i){
this.set_dismiss_events(w);
this.content_w.add_widget(w, i);
if (this.auto_open){
this.open();
}
}
remove_widget(w){
return this.content_w.remove_widget(w);
}
clear_widgets(){
return this.content_w.clear_widgets();
}
}
bricks.get_popupwindow_default_options = function(){
return {
timeout:0,
archor:'cc',
auto_open:true,
auto_dismiss:true,
auto_destroy:true,
movable:true,
resizable:true,
modal:true
}
}
bricks.PopupWindow = class extends bricks.Popup {
/*
{
title:
icon:
}
*/
constructor(opts){
opts.movable = true;
opts.resizable = true;
var ao = opts.auto_open;
opts.auto_open = false
opts.auto_dismiss = false;
opts.auto_destroy = false;
super(opts);
this.auto_open = ao;
this.title_bar = new bricks.HBox({css:'titlebar', cheight:2, width:'100%'});
this.moving_w = this.title_bar;
this.add_widget(this.title_bar);
this.build_title_bar();
var filler = new bricks.Filler({});
this.add_widget(filler)
this.content_w = new bricks.Layout({height:'100%', width:'100%'});
this.content_w.set_css('flexbox');
filler.add_widget(this.content_w);
if (this.auto_open){
this.open();
}
}
build_title_bar(){
var icon = new bricks.Icon({
rate:this.opts.rate,
url:this.opts.icon || bricks_resource('imgs/app.png')
});
this.title_bar.add_widget(icon);
this.tb_w = new bricks.IconBar( {
cheight:1,
margin:'5px',
rate:0.8,
tools:[
{
name:'delete',
icon:bricks_resource('imgs/app_delete.png'),
dynsize:true,
tip:'Destroy this window'
},
{
name:'minimax',
icon:bricks_resource('imgs/app_minimax.png'),
dynsize:true,
tip:'minimax this window'
},
{
name:'fullscreen',
icon:bricks_resource('imgs/app_fullscreen.png'),
dynsize:true,
tip:'fullscreen this window'
}
]
});
this.title_bar.add_widget(this.tb_w);
this.tb_w.bind('delete', this.destroy.bind(this));
this.tb_w.bind('minimax', this.dismiss.bind(this));
this.tb_w.bind('fullscreen', this.enter_fullscreen.bind(this));
if (this.title){
this.title_w = new bricks.Text({text:this.title});
this.title_bar.add_widget(this.title_w);
}
}
set_title(txt){
if (this.title_w){
this.title_w.set_text(txt);
}
}
open(){
var f = bricks.app.docks.find(o => {
if (o == this){
}
});
if (! f){
bricks.app.docks.push(this);
}
super.open();
}
dismiss(){
var f = bricks.app.docks.find(o=> o===this);
if (!f){
bricks.app.docks.push(this);
}
super.dismiss()
}
}
bricks.Dock = class extends bricks.HBox {
constructor(opts){
opts.cheight = opts.cheight || 2;
opts.width = opts.width || "80%";
super(opts);
this.set_css('scroll');
this.pw = [];
}
add_popupwindow(pw){
var info = pw.get_window_info();
}
del_popupwindow(pw){
}
}
bricks.Factory.register('Popup', bricks.Popup);
bricks.Factory.register('PopupWindow', bricks.PopupWindow);

24
bricks/progressbar.js Normal file
View File

@ -0,0 +1,24 @@
var bricks = window.bricks || {};
bricks.ProgressBar = class extends bricks.HBox {
/*
options:
total_value
bar_cwidth
event:
*/
constructor(opts){
super(opts);
this.set_css('progress-container');
this.text_w = new bricks.Text({text:'0%', cheight:this.bar_cwidth||2});
this.text_w.set_css('progress-bar')
this.add_widget(this.text_w);
}
set_value(v){
var pzt = (current / total) * 100;
pzt = Math.max(0, Math.min(100, percentage));
this.text_w.set_style('width', pzt + '%')
}
}
bricks.Factory.register('ProgressBar', bricks.ProgressBar);

0
bricks/registerfunction.js Executable file → Normal file
View File

11
bricks/splitter.js Normal file
View File

@ -0,0 +1,11 @@
var bricks = window.bricks || {};
bricks.Splitter = class extends bricks.JsWidget {
constructor(ops){
super({});
}
create(){
this.dom_element = this._create('hr')
}
}
bricks.Factory.register('Splitter', bricks.Splitter);

0
bricks/tab.js Executable file → Normal file
View File

View File

@ -26,7 +26,6 @@ bricks.Tabular = class extends bricks.DataViewer {
return r;
}
var row = new bricks.VBox({
css:'tabular-row'
});
row.add_widget(r);
var content = new bricks.VBox({
@ -60,13 +59,16 @@ bricks.Tabular = class extends bricks.DataViewer {
} else {
row.set_css('tabular-row-selected');
}
this.dispatch('row_selected', row.user_data);
}
}
async toggle_content(row, flag){
if (flag){
row.content_widget.show();
row.content_widget.clear_widgets();
var w = await bricks.widgetBuild(this.content_view, row.content_widget, row.user_data);
var desc = objcopy(this.content_view);
desc = bricks.apply_data(desc, row.user_data);
var w = await bricks.widgetBuild(desc, row.content_widget);
if (w){
row.content_widget.add_widget(w);
}
@ -97,10 +99,13 @@ bricks.Tabular = class extends bricks.DataViewer {
header = false;
}
var dr = new bricks.DataRow(options);
dr.set_css('tabular-row');
dr.render(header);
/*
dr.event_names.forEach(e => {
dr.toolbar_w.bind(e, this.record_event_handle.bind(this, e, record, dr));
});
*/
dr.bind('check_changed', this.record_check_changed.bind(this));
return dr;
}
@ -109,7 +114,7 @@ bricks.Tabular = class extends bricks.DataViewer {
this.dispatch('row_check_changed', event.params.user_data);
}
async renew_record_view(form, row){
var d = form.getValue();
var d = form._getValue();
var record = bricks.extend(row.user_data, d);
if (this.content_view){
row.rec_widget.renew(record);

1
bricks/toolbar.js Executable file → Normal file
View File

@ -26,6 +26,7 @@ bricks.Toolbar = class extends bricks.Layout {
this.bar = new bricks.HScrollPanel(options);
this.dom_element.classList.add('htoolbar')
}
this.bar.enable_key_select()
this.add_widget(this.bar);
this.clicked_btn = null;
this.preffix_css = this.opts.css || 'toolbar';

58
bricks/tree.js Executable file → Normal file
View File

@ -133,9 +133,8 @@ bricks.TreeNode = class extends bricks.VBox {
if (this.view_w){
widget.add_widget(this.view_w);
}
} else {
this.str_w.set_text(this.user_data[this.tree.opts.textField]);
}
this.str_w.set_text(this.user_data[this.tree.opts.textField]);
}
}
@ -171,6 +170,8 @@ bricks.Tree = class extends bricks.VScrollPanel {
this.row_height = this.opts.row_height || '35px';
this.multitype_tree = this.opts.multitype_tree||false;
this.selected_node = null;
this.build_title();
this.build_description();
this.create_toolbar();
this.checked_data = [];
this.container = new bricks.VBox({
@ -209,14 +210,14 @@ bricks.Tree = class extends bricks.VScrollPanel {
create_toolbar(){
var toolbar = bricks.extend({}, this.toolbar);
var tools = [];
if (toolbar.tools){
toolbar.tools.forEach(f => tools.push(f));
}
if (this.editable){
tools.push({icon:bricks_resource('imgs/add.png'), name:'add'});
tools.push({icon:bricks_resource('imgs/update.png'), name:'update'});
tools.push({icon:bricks_resource('imgs/delete.png'), name:'delete'});
}
if (toolbar.tools){
toolbar.tools.forEach(f => tools.push(f));
}
if (tools.length == 0){
return;
}
@ -230,7 +231,7 @@ bricks.Tree = class extends bricks.VScrollPanel {
switch (opts.name){
case 'add':
this.add_new_node();
breaks;
break;
case 'delete':
this.delete_node();
break;
@ -238,16 +239,17 @@ bricks.Tree = class extends bricks.VScrollPanel {
this.update_node();
break;
default:
if ((opts.selected_data || opts.checked_data) && ! this.selected_node){
w = new bricks.Error({title:'Error', message:'No selected node found'});
w.open();
return;
}
console.log('opts=', opts);
var d = null;
if (opts.checked_data){
d = {
checked_data:JSON.stringify(this.checked_data)
}
} else if (opts.selected_node){
d = {
selected_data:JSON.stringify(this.selected_node.user_data)
}
d = this.checked_data
} else if (opts.selected_data){
d = this.selected_node.user_data
}
this.dispatch(opts.name, d);
break;
@ -269,7 +271,11 @@ bricks.Tree = class extends bricks.VScrollPanel {
var node = this;
if (this.selected_node){
node = this.selected_node;
d[this.parentField] = node.get_id();
if (d instanceof FormData){
d.append(this.parentField, node.get_id());
} else {
d[this.parentField] = node.get_id();
}
}
if (this.editable.add_url){
var jc = new bricks.HttpJson()
@ -316,6 +322,8 @@ bricks.Tree = class extends bricks.VScrollPanel {
return;
}
var w = new bricks.Conform({
cwidth:16,
cheight:9,
title:'Delete node',
message:'Please conform delete selected node'
});
@ -383,24 +391,34 @@ bricks.Tree = class extends bricks.VScrollPanel {
async update_node_inputed(event){
var d = event.params;
var node = this.selected_node;
d[this.idField] = node.get_id();
if (d instanceof FormData){
d.append(this.idField, node.get_id());
} else {
d[this.idField] = node.get_id();
}
if(this.editable.update_url){
var jc = new bricks.HttpJson()
var desc = await jc.post(this.editable.update_url, {params:d});
if (desc.widgettype == 'Message'){
await this.update_node_data(node, d);
var o = formdata2object(d);
await this.update_node_data(node, o);
}
var w = await bricks.widgetBuild(desc, this);
w.open();
} else {
await this.update_node_data(node, d);
var o = formdata2object(d);
await this.update_node_data(node, o);
}
}
async update_node_data(node, data){
for (var name in Object.keys(data)){
node.user_data[name] = data[name];
}
var data_keys = Object.keys(node.user_data);
Object.keys(data).forEach(k => {
if (data_keys.includes(k)){
console.log(node.user_data[k], ':', k, ':', data[k]);
node.user_data[k] = data[k];
}
});
await node.update_content();
}

0
bricks/uitypesdef.js Executable file → Normal file
View File

291
bricks/utils.js Executable file → Normal file
View File

@ -1,22 +1,104 @@
var bricks = window.bricks || {};
bricks.bug = false;
bricks.formdata_copy = function(fd){
var cfd = new FormData();
// 遍历 originalFormData 中的所有条目并添加到 clonedFormData 中
for (var pair of fd.entries()) {
cfd.append(pair[0], pair[1]);
}
return cfd;
}
bricks.map = function(data_source, mapping, need_others){
ret = {};
Object.entries(data_source).forEach(([key, value]) => {
if (mapping.hasOwnProperty(key)){
ret[mapping[key]] = data_source[key];
} else if (need_others){
ret[key] = data_source[key];
}
});
return ret;
}
bricks.relocate_by_eventpos = function(event, widget){
var ex,ey;
var x,y;
var xsize = bricks.Body.dom_element.clientWidth;
var ysize = bricks.Body.dom_element.clientHeight;
ex = event.clientX;
ey = event.clientY;
var mxs = widget.dom_element.offsetWidth;
var mys = widget.dom_element.offsetHeight;
if (ex < (xsize / 2)) {
x = ex + bricks.app.charsize;
} else {
x = ex - mxs - bricks.app.charsize;
}
if (ey < (ysize / 2)) {
y = ey + bricks.app.charsize;
} else {
y = ey - mys - bricks.app.charsize;
}
widget.set_style('left', x + 'px');
widget.set_style('top', y + 'px');
}
var formdata2object = function(formdata){
let result = {};
formdata.forEach((value, key) => {
result[key] = value;
});
return result;
}
var inputdata2dic = function(data){
try {
var d = {}
for (let k of data.keys()){
var x = data.get(k);
if (k == 'prompt'){
x = bricks.escapeSpecialChars(x);
}
d[k] = x;
}
return d;
} catch (e){
return data;
}
}
bricks.delete_null_values = function(obj) {
for (let key in obj) {
if (obj[key] === null) {
delete obj[key];
}
}
return obj;
for (let key in obj) {
if (obj[key] === null) {
delete obj[key];
}
}
return obj;
}
bricks.is_empty = function(obj){
if (obj === null) return true;
return JSON.stringify(obj) === '{}';
}
bricks.serverdebug = async function(message){
var jc = new bricks.HttpJson();
await jc.post(url='/debug', {params:{
message:message
}});
return;
}
bricks.debug = function(...args){
if (! bricks.bug){
return;
}
if (bricks.bug == 'server'){
var message = args.join(" ");
f = bricks.serverdebug.bind(null, message);
schedule_once(f, 0.1);
return;
}
var callInfo;
try {
throw new Error();
@ -31,7 +113,7 @@ bricks.debug = function(...args){
}
bricks.is_mobile = function(){
var userAgent = navigator.userAgent;
var userAgent = navigator.userAgent;
if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(userAgent)) {
return true;
}
@ -77,9 +159,9 @@ var currentScriptPath = function () {
}
currentScript = scripts[ scripts.length - 1 ].src;
}
var currentScriptChunks = currentScript.split( '/' );
var currentScriptFile = currentScriptChunks[ currentScriptChunks.length - 1 ];
return currentScript.replace( currentScriptFile, '' );
var currentScriptChunks = currentScript.split( '/' );
var currentScriptFile = currentScriptChunks[ currentScriptChunks.length - 1 ];
return currentScript.replace( currentScriptFile, '' );
}
bricks.path = currentScriptPath();
@ -94,17 +176,17 @@ var bricks_resource = function(name){
* @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);
// recurse on childShadows
const childShadows = Array.from(el.querySelectorAll('*')).
map(el => el.shadowRoot).filter(Boolean);
bricks.debug('[querySelectorAllShadows]', selector, el, `(${childShadows.length} shadowRoots)`);
bricks.debug('[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();
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){
@ -182,7 +264,7 @@ bricks.obj_fmtstr = function(obj, fmt){
'${name:}, ${age=}'
*/
var s = fmt;
s = s.replace(/\${(\w+)([:=]*)}/g, (k, key, op) => {
s = s.replace(/\${(\w+)([:=]*)}/g, (k, key, op) => {
if (obj.hasOwnProperty(key)){
if (op == ''){
return obj[key];
@ -257,7 +339,7 @@ var archorize = function(ele,archor){
}
Array.prototype.insert = function ( index, ...items ) {
this.splice( index, 0, ...items );
this.splice( index, 0, ...items );
};
Array.prototype.remove = function(item){
@ -297,26 +379,26 @@ var convert2int = function(s){
}
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=/";
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;
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;';
function eraseCookie(name) {
document.cookie = name +'=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT;';
}
var set_max_height = function(w1, w2){
@ -330,13 +412,48 @@ var set_max_height = function(w1, w2){
}
}
var objcopy = function(obj){
var o = {}
for ( k in obj){
if (obj.hasOwnProperty(k)){
o[k] = obj[k];
}
}
return o;
var s = JSON.stringify(obj);
return JSON.parse(s);
}
bricks.set_stream_source = async function(target, url, params){
var widget;
if (typeof target == typeof ''){
widget = bricks.getWidgetById(target);
} else if (target instanceof bricks.JsWidget){
widget = target;
} else {
widget = await bricks.widgetBuild(target);
}
if (! widget){
bricks.debug('playResponseAudio():', target, 'can not found or build a widget');
return;
}
const mediaSource = new MediaSource();
mediaSource.addEventListener('sourceopen', handleSourceOpen);
widget.set_url(URL.createObjectURL(mediaSource));
function handleSourceOpen(){
const sourceBuffer = mediaSource.addSourceBuffer('audio/wav; codecs=1');
var ht = new bricks.HttpText();
ht.bricks_fetch(url, {params:params})
.then(response => response.body)
.then(body => {
const reader = body.getReader();
const read = () => {
reader.read().then(({ done, value }) => {
if (done) {
mediaSource.endOfStream();
return;
}
sourceBuffer.appendBuffer(value);
read();
}).catch(error => {
console.error('Error reading audio stream:', error);
});
};
read();
});
}
}
bricks.playResponseAudio = async function(response, target){
@ -354,8 +471,8 @@ bricks.playResponseAudio = async function(response, target){
bricks.debug('playResponseAudio():', target, 'can not found or build a widget');
return;
}
const blob = await response.blob();
const url = URL.createObjectURL(blob);
const blob = await response.blob();
const url = URL.createObjectURL(blob);
widget.set_url(url);
widget.play();
}
@ -382,12 +499,88 @@ bricks.Observable = class {
this.value = v;
}
set(v){
if (this.value != v){
var ov = this.value;
this.value = v;
if (this.value != ov){
this.owner.dispatch(this.name, v);
}
this.value = v;
}
get(){
return this.v;
}
}
bricks.Queue = class {
constructor() {
this.items = [];
this._done = false;
}
// 添加元素到队列尾部
enqueue(element) {
this.items.push(element);
}
done(){
this._done = true;
}
is_done(){
return this._done;
}
// 移除队列的第一个元素并返回
dequeue() {
if (this.isEmpty()) {
return null;
}
return this.items.shift();
}
// 查看队列的第一个元素
peek() {
if (this.isEmpty()) {
return null;
}
return this.items[0];
}
// 检查队列是否为空
isEmpty() {
return this.items.length === 0;
}
// 获取队列的大小
size() {
return this.items.length;
}
// 清空队列
clear() {
this.items = [];
}
// 打印队列元素
print() {
console.log(this.items.toString());
}
}
function blobToBase64(blob) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onloadend = function() {
resolve(reader.result);
};
reader.onerror = reject;
reader.readAsDataURL(blob);
});
}
/*
// 使用队列
const queue = new Queue();
queue.enqueue(1);
queue.enqueue(2);
queue.enqueue(3);
queue.print(); // 输出: 1,2,3
console.log(queue.dequeue()); // 输出: 1
queue.print(); // 输出: 2,3
*/

218
bricks/video.js Executable file → Normal file
View File

@ -26,6 +26,7 @@ bricks.VideoBody = class extends bricks.Layout {
bricks.Video = class extends bricks.Layout {
/*
{
title:
vtype:
url:
fullscreen:
@ -34,13 +35,22 @@ bricks.Video = class extends bricks.Layout {
*/
constructor(options){
super(options);
this.video_body = new bricks.VideoBody(options);
this.add_widget(this.video_body);
// this.video_body = new bricks.VideoBody(options);
this.set_css('video-js');
this.set_css('vjs-big-play-centered');
this.set_css('vjs-fluid');
this.set_style('width', '100%');
this.set_style('height', '100%');
this.set_style('object-fit', 'contain');
this.cur_url = opts.url;
this.cur_vtype = opts.type;
schedule_once(this.create_player.bind(this), 0.1);
this.hidedbtn = new bricks.Button({label:'click me'});
this.hidedbtn.bind('click', this.play.bind(this));
this.hidedbtn.hide();
this.add_widget(this.hidedbtn);
}
create(){
var e;
e = document.createElement('video');
this.dom_element = e;
e.controls = true;
}
destroy_resource(params, event){
@ -62,17 +72,46 @@ bricks.Video = class extends bricks.Layout {
}
}
}
findVideoButtonByClass(cls){
var e = this.dom_element;
return e.querySelector('.' + cls);
}
auto_play(){
schedule_once(this._auto_play.bind(this), 0.5);
schedule_once(this._auto_play.bind(this), 0.8);
}
_auto_play(){
this.set_url(this.opts.url);
return;
var play_btn = this.findVideoButtonByClass('vjs-big-play-button');
if (!play_btn){
console.log('vjs-big-play-button not found');
return;
}
if (play_btn.style.display == 'none'){
console.log('playing .............already');
return;
}
console.log('video ready, auto_playing ....');
schedule_once(this.disable_captions.bind(this), 2);
this.hidedbtn.dispatch('click');
var clickevent = new MouseEvent('click', {
'bubbles': true, // 事件是否冒泡
'cancelable': true // 事件是否可取消
});
play_btn.dispatchEvent(clickevent);
/*
if (this.autounmute && this.player.muted){
schedule_once(this.dispatch_mute.bind(this), 1);
}
*/
}
play(){
console.log('Video:play() called....');
this.player.play();
// this.player.muted(false);
}
unmuted(){
this.player.muted(false);
}
set_fullscreen(){
if (this.fullscreen){
@ -85,9 +124,31 @@ bricks.Video = class extends bricks.Layout {
}
}
}
dispatch_mute(){
var mute_btn = this.findVideoButtonByClass("vjs-mute-control.vjs-control.vjs-button");
if (!mute_btn){
var isMute = this.player.muted();
if (isMuted){
this.player.muted(False);
}
bricks.test_element = this;
console.log(this, 'there is not mute button found, isMuted=', isMuted);
return;
}
var clickevent = new MouseEvent('click', {
'bubbles': true, // 事件是否冒泡
'cancelable': true // 事件是否可取消
});
var isMuted = this.player.muted();
if (isMuted){
mute_btn.dispatchEvent(clickevent);
}
}
create_player(){
if(this.url){
this.player = videojs(this.video_body.dom_element, {
// this.player = videojs(this.video_body.dom_element, {
this.player = videojs(this.dom_element, {
controls:true,
autoplay:this.autoplay,
muted:true,
@ -98,37 +159,49 @@ bricks.Video = class extends bricks.Layout {
this.player.on('error',this.report_error.bind(this));
this.player.on('play', this.report_playok.bind(this));
this.player.on('ended', this.report_ended.bind(this));
this._set_source();
this.set_url(this.opts.url);
this.player.ready(this.set_fullscreen.bind(this));
/*
if (this.autoplay){
this.player.autoplay = true;
this.player.muted = true;
this.player.ready(this.auto_play.bind(this));
} else {
console.log('autoplay=', this.autoplay, this.auto_play);
// this.player.ready(this.auto_play.bind(this));
this.auto_play();
}
*/
}
}
report_ended(){
this.dispatch('play_end',{src:this.video_body.cur_url,type:this.video_body.cur_vtype});
if (this.play_status != 'playok'){
returnl
}
this.play_status = 'playend';
this.dispatch('play_end',{src:this.cur_url,type:this.cur_vtype});
}
report_playok(){
console.log(this.video_body.cur_url, 'play ok');
this.dispatch('play_ok', {src:this.video_body.cur_url,type:this.video_body.cur_vtype});
if (this.play_status != ''){
return;
}
this.play_status = 'playok';
console.log(this.cur_url, 'play ok');
if (this.autounmute && this.player.muted()){
schedule_once(this.dispatch_mute.bind(this), 2.5);
console.log('mute btn clicked');
} else {
console.log(this.autounmute, 'player.muted=', this.player.muted);
}
this.dispatch('play_ok', {src:this.cur_url,type:this.cur_vtype});
}
report_error(){
console.log(this.video_body.cur_url, 'play failed', this.err_cnt, 'times');
this.dispatch('play_failed', {'src':this.video_body.cur_url, type:this.video_body.cur_vtype});
if (this.play_status != ''){
return;
}
this.play_status = 'playfailed';
console.log(this.cur_url, 'play failed', this.err_cnt, 'times');
this.dispatch('play_failed', {'src':this.cur_url, type:this.cur_vtype});
}
_set_source(){
if (this.player){
this.player.src({
src:this.video_body.cur_url,
type:this.video_body.cur_vtype
src:this.cur_url,
type:this.cur_vtype
});
this.play_status = '';
}
}
set_source(url, vtype){
@ -137,17 +210,106 @@ bricks.Video = class extends bricks.Layout {
vtype = 'application/x-mpegURL';
} else if (t.endsWith('.mp4')){
vtype = 'video/mp4';
} else if (t.endsWith('.avi')){
vtype = 'video/avi';
} else if (t.endsWith('.webm')){
vtype = 'video/webm';
} else {
vtype = 'application/x-mpegURL';
}
if(this.player){
this.video_body.cur_url = url;
this.video_body.cur_vtype = vtype;
this.cur_url = url;
this.cur_vtype = vtype;
this._set_source();
this.play();
}
}
set_url(url){
this.set_source(url);
}
}
bricks.Iptv = class extends bricks.VBox {
/*
{
iptv_data_url:
playok_url:
playfailed_url:
}
*/
constructor(opts){
super(opts);
schedule_once(this.build_subwidgets.bind(this), 0.1);
}
async build_subwidgets(){
console.log('build_subwidgets called');
var jc = new bricks.HttpJson();
this.deviceid = bricks.deviceid('iptv')
this.user_data = await jc.httpcall(this.iptv_data_url, {
params:{
deviceid:this.deviceid
},
method:'GET'
});
console.log('this.user_data =', this.user_data);
this.video = new bricks.Video({
autoplay:true,
autounmute:this.autounmute,
url:this.user_data.url
});
this.title_w = new bricks.Text({text:this.user_data.tv_name, wrap:false});
this.add_widget(this.title_w);
this.add_widget(this.video);
this.video.bind('play_ok', this.report_play_ok.bind(this));
this.video.bind('play_failed', this.report_play_failed.bind(this));
}
async report_play_ok(){
console.log(this.user_data, 'channel playing ...', this.playok_url);
if (this.playok_url){
var ht = new bricks.HttpText();
var resp = ht.httpcall(this.playok_url,{
params:{
deviceid:this.deviceid,
channelid:this.user_data.id
},
method:"GET"
});
if (resp != 'Error'){
console.log('report playok ok');
} else {
console.log('report playok failed');
}
} else {
console.log('this.playok_url not defined', this.playok_url);
}
}
async report_play_failed(){
console.log(this.user_data, 'channel play failed ...');
if (this.playfailed_url){
var ht = new bricks.HttpText();
var resp = ht.httpcall(this.playfailed_url,{
params:{
deviceid:this.deviceid,
channelid:this.user_data.id
},
method:"GET"
});
if (resp != 'Error'){
console.log('report playfailed ok');
} else {
console.log('report playfailed failed');
}
} else {
console.log('this.playfailed_url not defined', this.playfailed_url);
}
}
setValue(data){
this.user_data = data;
this.title_w.set_text(data.tv_name);
this.video.set_url(data.url);
}
}
bricks.Factory.register('Video', bricks.Video);
bricks.Factory.register('Iptv', bricks.Iptv);

73
bricks/webspeech.js Normal file
View File

@ -0,0 +1,73 @@
var bricks = window.bricks || {};
bricks.WebTTS = class extends bricks.VBox {
constructor(opts){
super(opts);
}
speak(text){
// 检查浏览器是否支持SpeechSynthesis
if ('speechSynthesis' in window) {
var utterance = new SpeechSynthesisUtterance(text);
// 设置语音属性,例如语言
utterance.lang = bricks.app.lang;
utterance.pitch = 1;
utterance.rate = 1;
utterance.onstart = function(event) {
console.log('语音合成开始');
};
// 当语音合成结束时触发
utterance.onend = function(event) {
console.log('语音合成结束');
};
// 当语音合成出错时触发
utterance.onerror = function(event) {
console.error('语音合成出错:', event.error);
};
// 将utterance添加到语音合成队列
window.speechSynthesis.speak(utterance);
} else {
console.error('浏览器不支持SpeechSynthesis');
}
}
}
bricks.WebASR = class extends bricks.VBox {
constructor(opts){
super(opts);
this.reognition = None;
}
start_recording(){
// 检查浏览器是否支持SpeechRecognition
if ('SpeechRecognition' in window) {
// 创建SpeechRecognition实例
this.recognition = new SpeechRecognition();
// 处理识别错误
this.recognition.onerror = function(event) {
console.error('识别错误:', event.error);
};
// 处理语音识别结束事件
this.recognition.onend = function() {
console.log('语音识别已结束');
};
// 处理识别结果
this.recognition.onresult = function(event) {
var transcript = event.results[0][0].transcript;
this.dispatch('asr_result', {
content:transcript
});
console.log('识别结果:', transcript);
};
this.recognition.lang = bricks.app.lang;
this.recognition.start();
} else {
console.log('browser has not SpeechRecognition');
}
}
stop_recording(){
this.recognition.stop();
}
}
bricks.Factory.register('WebTTS', bricks.WebTTS);
bricks.Factory.register('WebASR', bricks.WebASR);

91
bricks/widget.js Executable file → Normal file
View File

@ -15,6 +15,13 @@ bricks.resize_observer = new ResizeObserver(entries => {
});
bricks.JsWidget = class {
/*
popup:{
popup_event:
popup_desc:
popupwindow:false or true
}
*/
constructor(options){
if (!options){
options = {}
@ -38,9 +45,36 @@ bricks.JsWidget = class {
var w = bricks.app.tooltip;
this.bind('mousemove', w.show.bind(w, this.opts.tip));
this.bind('mouseout', w.hide.bind(w));
this.bind('click', w.hide.bind(w));
}
if (this.popup){
this.bind(this.popup.popup_event, this.popup_action.bind(this));
}
bricks.resize_observer.observe(this.dom_element);
}
destroy_popup(){
this.popup_widget.destroy();
this.popup_widget = null;
}
async popup_action(){
if (this.popup_widget){
this.popup_widget.destroy();
this.popup_widget = null;
} else {
if (this.popup.popupwindow){
this.popup_widget = new bricks.PopupWindow(this.popup.optiosn);
} else {
this.popup_widget = new bricks.Popup(this.popup.options);
}
this.popup_widget.bind('dismissed', this.destroy_popup.bind(this));
var w = await bricks.widgetBuild(this.popup.popup_desc, this, this.user_data);
if (w){
this.popup_widget.add_widget(w);
this.popup_widget.open();
this.popup_widget.popup_from_widget(this);
}
}
}
showRectage(){
return this.dom_element.getBoundingClientRect();
}
@ -65,8 +99,10 @@ bricks.JsWidget = class {
disabled(flag){
if(flag){
this.dom_element.disabled = true;
this.set_style('pointerEvents', 'none');
} else {
this.dom_element.disabled = false;
this.set_style('pointerEvents', 'auto');
}
}
opts_set_style(){
@ -153,6 +189,23 @@ bricks.JsWidget = class {
}
}
}
enter_fullscreen(){
var e = this.dom_element;
// 获取要全屏显示的元素
// 检查浏览器是否支持Fullscreen API
if (e.requestFullscreen) {
e.requestFullscreen();
} else if (e.mozRequestFullScreen) { // Firefox
e.mozRequestFullScreen();
} else if (e.webkitRequestFullscreen) { // Chrome, Safari and Opera
e.webkitRequestFullscreen();
} else if (e.msRequestFullscreen) { // IE/Edge
e.msRequestFullscreen();
}
}
exit_fullscreen(){
document.exitFullscreen();
}
h_center(){
this.dom_element.style.marginLeft = 'auto';
this.dom_element.style.marginRight = 'auto';
@ -458,36 +511,22 @@ bricks.Tooltip = class extends bricks.Text {
this.set_otext(otext);
this.set_style('zIndex', 999999999);
this.set_style('display', 'block');
var ex,ey;
var x,y;
var xsize = bricks.Body.dom_element.clientWidth;
var ysize = bricks.Body.dom_element.clientHeight;
ex = event.clientX;
ey = event.clientY;
var mxs = this.dom_element.offsetWidth;
var mys = this.dom_element.offsetHeight;
if (ex < (xsize / 2)) {
x = ex + bricks.app.charsize;
} else {
x = ex - mxs - bricks.app.charsize;
}
if (ey < (ysize / 2)) {
y = ey + bricks.app.charsize;
} else {
y = ey - mys - bricks.app.charsize;
}
this.set_style('left', x + 'px');
this.set_style('top', y + 'px');
if (this.auto_task){
this.auto_task.cancel();
}
this.auto_task = schedule_once(this.hide.bind(this), 30);
}
hide(){
bricks.relocate_by_eventpos(event, this);
if (this.auto_task){
this.auto_task.cancel();
this.auto_task = null;
}
this.auto_task = schedule_once(this.hide.bind(this), 6);
}
hide(){
try {
if (this.auto_task){
this.auto_task.cancel();
this.auto_task = null;
}
} catch(e){
console.log('Exception:', e);
}
this.set_style('display', 'none');
}
}

View File

@ -6,29 +6,58 @@ bricks.Wterm = class extends bricks.JsWidget {
/*
{
ws_url:
host:
ssh_port:
user:
ping_timeout:19
}
*/
constructor(opts){
super(opts);
this.socket = null;
this.ping_timeout = opts.ping_timeout || 19;
schedule_once(this.open.bind(this), 0.5);
}
charsize_sizing(){
var cs = bricks.app.charsize;
this.term.setOption('fontSize', cs);
}
heartbeat(){
if (this.socket.readyState != 1) return;
this.socket.send('_#_heartbeat_#_');
this.heartbeat_task = schedule_once(this.heartbeat.bind(this),
this.ping_timeout);
}
async open(){
var term = new Terminal();
var term_options = this.term_options || {};
var term = new Terminal(term_options);
this.term = term;
term.open(this.dom_element);
var ws = new WebSocket(this.opts.ws_url);
bricks.debug('FitAddon=', FitAddon);
this.socket = ws;
this.fitAddon = new FitAddon.FitAddon()
term.loadAddon(this.fitAddon)
// this.fitAddon.fit()
this.fitAddon.fit();
this.charsize_sizing();
this.bind('resize', this.term_resize.bind(this))
ws.onmessage = msg => {
term.write(JSON.parse(msg.data).data);
ws.onmessage = event => {
var msg = JSON.parse(event.data);
console.log('ws msg=', msg);
if (msg.data == '_#_heartbeat_#_'){
console.log('connection alive');
} else {
term.write(msg.data);
}
};
ws.onclose = (event) => {
console.log('websocket closed:', event.code, '--', event.reason);
if (this.heartbeat_task) {
this.heartbeat_task.cancel();
this.heartbeat_task = null;
}
};
ws.onopen = () => {
this.heartbeat_task = schedule_once(this.heartbeat.bind(this),
this.ping_timeout);
};
term.onData(function(key) {
//Enter
let msg = {
@ -38,13 +67,12 @@ bricks.Wterm = class extends bricks.JsWidget {
ws.send(key);
});
term.focus();
term.paste("ls -l\n");
}
term_resize(){
try {
this.fitAddon.fit();
} catch(e){
bricks.debug('resize error', e);
console.log('resize error', e);
}
}
}

BIN
docs/.DS_Store vendored

Binary file not shown.

View File

@ -39,6 +39,14 @@ bricks的事件处理是在控件描述文件的binds区域中添加事件处理
### dataparams
获取动态参数时需给定的参数, 可选对象类型k,v形式给定参数
### popup_options
当target为“Popup“或“PopupWindow”时定义Popup或PopupWindow的参数
#### dismiss_events
字符串数组每个字符串定义一个Popup, PopupWindow的子控件的事件这些事件发生时将导致Popup, PopupWindow关闭
#### eventpos
Popup, PopupWindow窗体移动到鼠标位置
### 获取动态参数说明
绑定任务获取实时数据作为参数,需要给定以下属性:
@ -50,7 +58,15 @@ bricks的事件处理是在控件描述文件的binds区域中添加事件处理
datamethod 优先datascript从datawidget控件中通过datamethod
### target
字符串或控件实例目标控件实例如果是字符串使用”getWidgetById“函数获得目标控件实例关于target规则请查看[控件id](widgetid.md)
支持一下形式的target定义
* 目标控件实例对象
* 字符串且等于“Popup“
* 字符串且等于”PopupWindow“
* 其他字符串控件对象的id
当actiontype为"urlwidget"时target应该是一个容器控件所生成的控件将插入或替代到“target”所代表的对象中如果actiontype是其他类型,则在此对象中执行方法,脚本,定义函数,或定义事件
### conform
如果一个事件处理需要用户确认可以在事件处理中指定一个conform属性来引发当此事件发生时会弹出一个确认窗口用户确认后才会处理此事件否则不处理

View File

@ -0,0 +1,6 @@
{
"widgettype":"VBox",
"options":{
"height":"100%"
}
}

14
examples/asr.ui Normal file
View File

@ -0,0 +1,14 @@
{
"widgettype":"ASRClient",
"options":
{
"ws_url":"{{entire_url('/wss/ws/test.ws')}}?userid={{get_user()}}",
"icon_options":{
"rate":3
},
"ws_params":{
"kkk":1,
"brade":"123"
}
}
}

29
examples/camera.ui Normal file
View File

@ -0,0 +1,29 @@
{
"widgettype":"HBox",
"options":{
"widht":"100%",
"height":"100%"
},
"subwidgets":[
{
"widgettype":"Camera",
"options":{
"width":"50%",
"height":"90%",
"archor":"cl",
"auto_open":true,
"fps":60
}
},
{
"widgettype":"Camera",
"options":{
"width":"50%",
"height":"90%",
"archor":"cr",
"auto_open":true,
"fps":30
}
}
]
}

36
examples/recharge.ui Normal file
View File

@ -0,0 +1,36 @@
{
"widgettype":"IconbarPage",
"options":{
"bar_at":"top",
"bar_opts":{
"margin":"10px",
"rate":1.6,
"tools":[
{
"name":"wechat",
"icon":"{{entire_url('/imgs/weixinzhifu.png')}}",
"label":"微信充值",
"tip":"使用微信为账户充值",
"content":{
"widgettype":"urlwidget",
"options":{
"url":"{{entire_url('wechat_recharge.ui')}}"
}
}
},
{
"name":"alipay",
"icon":"{{entire_url('/imgs/zhifubao.png')}}",
"label":"支付宝充值",
"tip":"使用支付宝为账户充值",
"content":{
"widgettype":"urlwidget",
"options":{
"url":"{{entire_url('alipay_recharge.ui')}}"
}
}
}
]
}
}
}

View File

@ -0,0 +1,6 @@
{
"widgettype":"VBox",
"options":{
"height":"100%"
}
}