Task Form UI/UX design is extremely important for improving business processes. These are examples of Task Form decorations for workflow app administrators. Workflow app administrators are free to use them for free, commercial or non-commercial, without worrying about licensing restrictions. Please feel free to use these code examples. (Note that you do so at your own risk, low-code programming skills in JavaScript are required.)
タスク処理画面のUI/UXデザインは、ビジネスプロセス改善において極めて重要です。以下は、処理画面(タスクフォーム)のデコレーション例です。ワークフローアプリ管理者は、ライセンス上の制限を気にすることなく、商用非商用問わず、無料で自由に利用できます。(ただしデコレーションは自身の責任で行ってください。また JavaScript プログラミング知識が必要です)
- Overwrite Frequent Strings using Button Attribute
- Overwrite Frequent Strings using Button Content
- Append Frequent Strings using Button Attribute
- Enter Number using NumPad Buttons
- Confirm Selected Value
- Check All Checkboxes
- Increment Date using +/- Button
- Set Date Range and Datetime
- Copy to Clipboard
- Validate with Regular Expressions
- Generate QR Code
- Preview HTML Code in New Window
- Export Text as UTF8B-File
- Check Form History via REST API
- Switch between Edit and Read-Only
The following code examples are written in “Questetra Form JavaScript API
” and “W3C Selectors API
” (vanilla JavaScript). If necessary, refer to each manual. Please note that the HTML tag <script>
for decoration is only available in the Professional Edition. (C) Questetra, Inc. MIT License
- M213: Guidance Descriptions on the Operating Screen (HTML/JavaScript)
- R2131: HTML Tags Available for Operation Form
- R2132: Questetra Form JavaScript API (en)
- MDN Web Docs: Document.querySelector() (en)
- MDN Web Docs: Document.querySelector() (ja)
- Older examples
Overwrite Frequent Strings using Button Attribute

Input / Output
- → STRING (STRING_TEXTFIELD):
q_Departure
(update) - → STRING (STRING_TEXTFIELD):
q_Arrival
(update)
Code Example for DESCRIPTION of Workflow-App
Click to overwrite: <br>
<button type='button' onclick='qbpms.form.set( "q_Departure", "NRT" )'>
NRT: Narita 成田国際空港 (新東京)</button>
<button type='button' onclick='qbpms.form.set( "q_Departure", "HND" )'>
HND: Haneda 東京国際空港 羽田</button>
<button type='button' onclick='qbpms.form.set( "q_Departure", "KIX" )'>
KIX: Kansai 関西国際空港 大阪</button><br>
<button type='button' onclick='qbpms.form.set( "q_Departure", "LAX" )'>
LAX: Los Angeles ロサンゼルス国際空港</button>
<button type='button' onclick='qbpms.form.set( "q_Departure", "ORD" )'>
ORD: Chicago O'Hare シカゴ・オヘア</button>
<button type='button' onclick='qbpms.form.set( "q_Departure", "ATL" )'>
ATL: Hartsfield-Jackson Atlanta ハーツフィールド・ジャクソン・アトランタ</button><br>
<button type='button' onclick='qbpms.form.set( "q_Departure", "LHR" )'>
LHR: Heathrow ロンドン・ヒースロー</button>
<button type='button' onclick='qbpms.form.set( "q_Departure", "FRA" )'>
FRA: Flughafen Frankfurt フランクフルト</button>
<button type='button' onclick='qbpms.form.set( "q_Departure", "CDG" )'>
CDG: Aeroport de Paris-Charles-de-Gaulle パリ=シャルル・ド・ゴール</button>
Click to overwrite: <br>
<button type='button' onclick='qbpms.form.set( "q_Arrival", "LAX" )'>
LAX: Los Angeles ロサンゼルス国際空港</button>
<button type='button' onclick='qbpms.form.set( "q_Arrival", "ORD" )'>
ORD: Chicago O'Hare シカゴ・オヘア</button>
<button type='button' onclick='qbpms.form.set( "q_Arrival", "ATL" )'>
ATL: Hartsfield-Jackson Atlanta ハーツフィールド・ジャクソン・アトランタ</button><br>
<button type='button' onclick='qbpms.form.set( "q_Arrival", "LHR" )'>
LHR: Heathrow ロンドン・ヒースロー</button>
<button type='button' onclick='qbpms.form.set( "q_Arrival", "FRA" )'>
FRA: Flughafen Frankfurt フランクフルト</button>
<button type='button' onclick='qbpms.form.set( "q_Arrival", "CDG" )'>
CDG: Aeroport de Paris-Charles-de-Gaulle パリ=シャルル・ド・ゴール</button><br>
<button type='button' onclick='qbpms.form.set( "q_Arrival", "NRT" )'>
NRT: Narita 成田国際空港 (新東京)</button>
<button type='button' onclick='qbpms.form.set( "q_Arrival", "HND" )'>
HND: Haneda 東京国際空港 羽田</button>
<button type='button' onclick='qbpms.form.set( "q_Arrival", "KIX" )'>
KIX: Kansai 関西国際空港 大阪</button>
If to overwrite the process title, write “Title
” instead of “q_Departure
“.
Overwrite Frequent Strings using Button Content

Input / Output
- → STRING (STRING_TEXTFIELD):
q_AccountTitle
(update)
Code Example for DESCRIPTION of Workflow-App
<button type='button' class='user_AccountName'>(不明)</button> ((not clear))
<button type='button' class='user_AccountName'>広告宣伝費</button> (Advertising) <br>
<strong>Other Marketing & Admin.</strong>:
<button type='button' class='user_AccountName'>旅費交通費</button> (Traveling)
<button type='button' class='user_AccountName'>会議費</button> (Conference)
<button type='button' class='user_AccountName'>備品・消耗品費</button> (Supplies)
<button type='button' class='user_AccountName'>租税公課</button> (Taxes&Dues)
<button type='button' class='user_AccountName'>通信費</button> (Communication)<br>
<strong>Manufacturing costs</strong>:
<button type='button' class='user_AccountName'>旅費交通費(製)</button> (Traveling)
<button type='button' class='user_AccountName'>会議費(製)</button> (Conference)
<button type='button' class='user_AccountName'>備品・消耗品費(製)</button> (Supplies)
<button type='button' class='user_AccountName'>租税公課(製)</button> (Taxes&Dues)
<button type='button' class='user_AccountName'>通信費(製)</button> (Communication)
<script>
document.querySelectorAll('.user_AccountName').forEach( btn => {
btn.addEventListener('click', () => {
const strClicked = btn.textContent;
qbpms.form.set( "q_AccountTitle", strClicked );
});
});
</script>
Append Frequent Strings using Button Attribute

Input / Output
- ⇆ STRING (STRING_TEXTFIELD):
q_Email
(append)
Code Example for DESCRIPTION of Workflow-App
Click to append:
<button type='button' onclick='user_appendQStr("q_Email", "@questetra.com")'>@questetra.com</button>
<button type='button' onclick='user_appendQStr("q_Email", "@gmail.com")'>@gmail.com</button>
<button type='button' onclick='user_appendQStr("q_Email", "@outlook.com")'>@outlook.com</button>
<script>
function user_appendQStr( qfieldName, strArg ){ // Input Output same field
let strOriginal = "";
if( qbpms.form.get( qfieldName ) !== null ){
strOriginal = qbpms.form.get( qfieldName );
}
qbpms.form.set( qfieldName, (strOriginal + strArg) );
}
</script>
Enter Number using NumPad Buttons

Input / Output
- ⇆ NUMERIC (DECIMAL):
q_Cost
(update)
Code Example for DESCRIPTION of Workflow-App
<button type='button' class='user_numPad' onclick='qbpms.form.set( "q_Cost", "410" )'>410</button>
<button type='button' class='user_numPad' onclick='user_plusQNum( "q_Cost", 80 )'>+80</button><br />
<button type='button' class='user_numPad' onclick='user_appendQNum( "q_Cost", "1" )'>1</button>
<button type='button' class='user_numPad' onclick='user_appendQNum( "q_Cost", "2" )'>2</button>
<button type='button' class='user_numPad' onclick='user_appendQNum( "q_Cost", "3" )'>3</button><br />
<button type='button' class='user_numPad' onclick='user_appendQNum( "q_Cost", "4" )'>4</button>
<button type='button' class='user_numPad' onclick='user_appendQNum( "q_Cost", "5" )'>5</button>
<button type='button' class='user_numPad' onclick='user_appendQNum( "q_Cost", "6" )'>6</button><br />
<button type='button' class='user_numPad' onclick='user_appendQNum( "q_Cost", "7" )'>7</button>
<button type='button' class='user_numPad' onclick='user_appendQNum( "q_Cost", "8" )'>8</button>
<button type='button' class='user_numPad' onclick='user_appendQNum( "q_Cost", "9" )'>9</button><br />
<button type='button' class='user_numPad' onclick='user_appendQNum( "q_Cost", "0" )'>0</button>
<button type='button' class='user_numPad' onclick='user_timesQNum( "q_Cost", 10 )'>*10</button>
<button type='button' class='user_numPad' onclick='user_timesQNum( "q_Cost", 0.1 )'>/10</button>
<button type='button' class='user_numPad' onclick='user_deletePrevQNum( "q_Cost" )'>BS</button>
<button type='button' class='user_numPad' onclick='user_formatQNum( "q_Cost" )'>Format</button>
<style>
.user_numPad {
margin: 1px 2px;
padding: 0 5px;
border: 1px solid #009900;
background-color: #ffffff
}
.user_numPad:active{
background-color: #99ff99
}
</style>
<script>
function user_appendQNum( qfieldName, strArg ){ // Input Output same field
let strOriginal = "";
if( qbpms.form.get( qfieldName ) !== null && // like as qSTRING
qbpms.form.get( qfieldName ).toString() !== "0" ){ // no Thousands-separators
strOriginal = qbpms.form.get( qfieldName ).toString(); // exception: "1.", "1.0" as "1"
}
qbpms.form.set( qfieldName, ( strOriginal + strArg ) );
}
function user_deletePrevQNum( qfieldName ){ // Input Output same field
if( qbpms.form.get( qfieldName ) !== null ){ // like as qSTRING
let strOriginal = qbpms.form.get( qfieldName ).toString(); // no Thousands-separators
qbpms.form.set( qfieldName, strOriginal.slice( 0, -1 ) );
}
}
function user_timesQNum( qfieldName, numArg ){
let numOriginal = new Big(0);
if( qbpms.form.get( qfieldName ) !== null ){
numOriginal = qbpms.form.get( qfieldName );
}
qbpms.form.set( qfieldName, numOriginal.times( numArg ) );
// https://mikemcl.github.io/big.js/#times
}
function user_plusQNum( qfieldName, numArg ){
let numOriginal = new Big(0);
if( qbpms.form.get( qfieldName ) !== null ){
numOriginal = qbpms.form.get( qfieldName );
}
qbpms.form.set( qfieldName, numOriginal.plus( numArg ) );
// https://mikemcl.github.io/big.js/#plus
}
function user_formatQNum( qfieldName ){
if( qbpms.form.get( qfieldName ) !== null ){
qbpms.form.set( qfieldName, qbpms.form.get( qfieldName ) );
}
}
</script>
Confirm Selected Value

Input / Output
- ← SELECT (SELECT_SINGLE):
q_Currency
- →
span#user_notice01
Code Example for DESCRIPTION of Workflow-App
<button type='button' onclick='qbpms.form.set( "q_Currency", null )'>Clear</button>
<span id="user_notice01">(default)</span>
<script>
qbpms.form.on( 'change', 'q_Currency', user_updateNotice01 );
function user_updateNotice01( eventField ){
let arrOptions = eventField.value; // = qbpms.form.get( 'q_Currency' );
console.log( "Number of Selected: " + arrOptions.length );
let strInnerHtml = '';
if( arrOptions.length ){
if( arrOptions[0].value === 'jpy' ){
strInnerHtml =
'<span style="font-weight:bold; color:#C60F38"> Selected: ¥</span> ' +
'(' + arrOptions[0].value + ')';
}else if( arrOptions[0].value === 'usd' ){
strInnerHtml =
'<span style="font-weight:bold; color:#1E7E4B"> Selected: $</span> ' +
'(' + arrOptions[0].value + ')';
}else if( arrOptions[0].value === 'eur' ){
strInnerHtml =
'<span style="font-weight:bold; color:#003295"> Selected: €</span> ' +
'(' + arrOptions[0].value + ')';
}
}
document.querySelector( "#user_notice01" ).innerHTML = strInnerHtml;
}
</script>
Check All Checkboxes

Input / Output
- → SELECT (SELECT_CHECKBOX):
q_Regions
Code Example for DESCRIPTION of Workflow-App
<div style="text-align: right">
<button type='button' onclick='user_checkCheckboxes(
"q_Regions",
"CA", "FR", "DE", "IT", "JP", "GB", "US"
)'>Check All</button>
<button type='button' onclick='qbpms.form.set( "q_Regions", null )'>Clear All</button>
</div>
<script>
function user_checkCheckboxes( qfieldName, ...arrStrValues ){ // SELECT_CHECKBOX
// rest parameter syntax allows an indefinite number of arguments as an array
qbpms.form.set( qfieldName, arrStrValues );
}
</script>
Increment Date using +/- Button

Input / Output
- ⇆ DATE (DATE_YMD):
q_DueDate
- →
span#user_footnote_DueDate
Code Example for DESCRIPTION of Workflow-App
<button type='button' onclick='
user_addYmdQDate( "q_DueDate", 0, 0, -7 );
user_setFootnote_DueDate();
'>-7d</button>
<button type='button' onclick='
user_addYmdQDate( "q_DueDate", 0, 0, -1 );
user_setFootnote_DueDate();
'>-1d</button>
<button type='button' onclick='
user_addYmdQDate( "q_DueDate", 0, 0, 1 );
user_setFootnote_DueDate();
'>+1d</button>
<button type='button' onclick='
user_addYmdQDate( "q_DueDate", 0, 0, 7 );
user_setFootnote_DueDate();
'>+7d</button>
<span id="user_footnote_DueDate"></span>
<script>
qbpms.form.on( 'ready', user_setFootnote_DueDate ); // for Initial Value
qbpms.form.on( 'change', 'q_DueDate', user_setFootnote_DueDate );
function user_setFootnote_DueDate(){
let dateTarget = qbpms.form.get( "q_DueDate" );
if( dateTarget === null ){ return; }
let dateNow = new Date();
let bigDateDifMsec = new Big( dateTarget.getTime() - dateNow.getTime() );
let bigDateDifHours = bigDateDifMsec.div(1000).div(60).div(60).round(1, Big.roundDown);
let bigDateDifDays = bigDateDifMsec.div(1000).div(60).div(60).div(24).round(1, Big.roundDown);
// https://mikemcl.github.io/big.js/#round
let strInnerHtml = "";
if( bigDateDifDays.gt(1) ){
strInnerHtml =
'<span style="background-color:#00ffff">' +
bigDateDifDays.toString() + ' days' +
'</span>';
}else if( Big(1).gte(bigDateDifDays) && bigDateDifDays.gt(0) ){
strInnerHtml =
'<span style="background-color:#80ff00">' +
bigDateDifHours.toString() + ' h (tomorrow)' +
'</span>';
}else if( Big(0).gte(bigDateDifDays) && bigDateDifDays.gt(-1) ){
strInnerHtml =
'<span style="background-color:#ffff00">' +
bigDateDifHours.abs().toString() + ' h ago (today)' +
'</span>';
}else if( Big(-1).gte(bigDateDifDays) ){
strInnerHtml =
'<span style="background-color:#ff8000">' +
bigDateDifDays.abs().toString() + ' days ago (PastDate/過去)' +
'</span>';
}
document.querySelector( '#user_footnote_DueDate' ).innerHTML = strInnerHtml;
}
function user_addYmdQDate( qfieldName, numY, numM, numD, numHour = 0, numMin = 0 ){
// Supports QDate and QDatetime (Undefined parts will be completed 2000-01-01T00:00:00.)
let dateTmp = qbpms.form.get( qfieldName );
if( dateTmp === null ){ return; }
// console.log( dateTmp.toISOString() );
dateTmp.setFullYear( dateTmp.getFullYear() + numY );
dateTmp.setMonth ( dateTmp.getMonth() + numM );
dateTmp.setDate ( dateTmp.getDate() + numD );
dateTmp.setHours ( dateTmp.getHours() + numHour );
dateTmp.setMinutes ( dateTmp.getMinutes() + numMin );
qbpms.form.set( qfieldName, dateTmp );
}
</script>
Set Date Range and Datetime

Input / Output
- ⇆ DATE (DATE_YMD):
q_StartDate
- →
span#user_footnote_StartDate
- ⇆ DATE (DATE_YMD):
q_EndDate
- →
span#user_footnote_EndDate
- ⇆ DATETIME:
q_NotificationDatetime
- →
span#user_footnote_NotificationDatetime
Code Example for DESCRIPTION of Workflow-App
<span id="user_footnote_StartDate"></span><br>
<button type='button' onclick='
user_rewriteQDate_First( "q_StartDate" );
user_setFootnote_StartDate();
'>First Day</button>
<button type='button' onclick='
user_rewriteQDate_MonOfNext( "q_StartDate" );
user_setFootnote_StartDate();
'>Monday of Next Week</button>
<button type='button' onclick='
user_rewriteQDate_FirstOfNext( "q_StartDate" );
user_setFootnote_StartDate();
'>First Day of Next Month</button>
<br />
<button type='button' onclick='
user_addYmdQDate( "q_StartDate", 0, 0, -1 );
user_setFootnote_StartDate();
'>-1d</button>
<button type='button' onclick='
user_addYmdQDate( "q_StartDate", 0, 0, 1 );
user_setFootnote_StartDate();
'>+1d</button>
<button type='button' onclick='
user_addYmdQDate( "q_StartDate", 0, 0, 7 );
user_setFootnote_StartDate();
'>+7d</button>
<button type='button' onclick='
user_addYmdQDate( "q_StartDate", 0, 1, 0 );
user_setFootnote_StartDate();
'>+1m</button>
<button type='button' onclick='
user_addYmdQDate( "q_StartDate", 1, 0, 0 );
user_setFootnote_StartDate();
'>+1y</button>
<script>
qbpms.form.on( 'ready', user_setFootnote_StartDate ); // for Initial Value
qbpms.form.on( 'change', 'q_StartDate', user_setFootnote_StartDate );
function user_setFootnote_StartDate(){
let dateTarget = qbpms.form.get( "q_StartDate" );
if( dateTarget === null ){ return; }
let strInnerHtml = "";
if( dateTarget.getDay() === 0 ){ // Sunday - Saturday : 0 - 6
strInnerHtml = '<span style="background-color:#FFCCCC">Sun 日曜日</span>';
}else if( dateTarget.getDay() === 1 ){
strInnerHtml = '<span>Mon 月曜日</span>';
}else if( dateTarget.getDay() === 2 ){
strInnerHtml = '<span>Tue 火曜日</span>';
}else if( dateTarget.getDay() === 3 ){
strInnerHtml = '<span>Wed 水曜日</span>';
}else if( dateTarget.getDay() === 4 ){
strInnerHtml = '<span>Thu 木曜日</span>';
}else if( dateTarget.getDay() === 5 ){
strInnerHtml = '<span>Fri 金曜日</span>';
}else if( dateTarget.getDay() === 6 ){
strInnerHtml = '<span style="background-color:#BDD7EE">Sat 土曜日</span>';
}
document.querySelector( '#user_footnote_StartDate' ).innerHTML = strInnerHtml;
}
function user_addYmdQDate( qfieldName, numY, numM, numD, numHour = 0, numMin = 0 ){
// Supports QDate and QDatetime (Undefined parts will be completed 2000-01-01T00:00:00.)
let dateTmp = qbpms.form.get( qfieldName );
if( dateTmp === null ){ return; }
// console.log( dateTmp.toISOString() );
dateTmp.setFullYear( dateTmp.getFullYear() + numY );
dateTmp.setMonth ( dateTmp.getMonth() + numM );
dateTmp.setDate ( dateTmp.getDate() + numD );
dateTmp.setHours ( dateTmp.getHours() + numHour );
dateTmp.setMinutes ( dateTmp.getMinutes() + numMin );
qbpms.form.set( qfieldName, dateTmp );
}
function user_rewriteQDate_First( qfieldName ){
let dateTmp = qbpms.form.get( qfieldName );
if( dateTmp === null ){ return; }
dateTmp.setDate(1);
qbpms.form.set( qfieldName, dateTmp );
}
function user_rewriteQDate_MonOfNext( qfieldName ){
let dateTmp = qbpms.form.get( qfieldName );
if( dateTmp === null ){ return; }
// getDay() -> Sunday - Saturday : 0 - 6
// plus(7-getDay()) -> Weekend Sunday
dateTmp.setDate( dateTmp.getDate() + ( 7 - dateTmp.getDay() ) + 1 );
// Week: Monday - Sunday (ISO 8601)
qbpms.form.set( qfieldName, dateTmp );
}
function user_rewriteQDate_FirstOfNext( qfieldName ){
let dateTmp = qbpms.form.get( qfieldName );
if( dateTmp === null ){ return; }
dateTmp.setDate(1);
dateTmp.setMonth( dateTmp.getMonth() + 1 );
qbpms.form.set( qfieldName, dateTmp );
}
</script>
<span id="user_footnote_EndDate"></span>
<br />
<button type='button' onclick='user_copyPasteQDate( "q_StartDate", "q_EndDate" )'>copy from q_StartDate</button>
<br />
<button type='button' onclick='
user_addYmdQDate( "q_EndDate", 1, 0, -1 );
user_setFootnote_EndDate();
'>+1y-1d</button>
<button type='button' onclick='
user_addYmdQDate( "q_EndDate", 0, 1, -1 );
user_setFootnote_EndDate();
'>+1m-1d</button>
<br />
<button type='button' onclick='
user_addYmdQDate( "q_EndDate", 0, -1, 0 );
user_setFootnote_EndDate();
'>-1m</button>
<button type='button' onclick='
user_addYmdQDate( "q_EndDate", 0, 0, -1 );
user_setFootnote_EndDate();
'>-1d</button>
<button type='button' onclick='
user_addYmdQDate( "q_EndDate", 0, 0, 1 );
user_setFootnote_EndDate();
'>+1d</button>
<button type='button' onclick='
user_addYmdQDate( "q_EndDate", 0, 1, 0 );
user_setFootnote_EndDate();
'>+1m</button>
<button type='button' onclick='
user_addYmdQDate( "q_EndDate", 1, 0, 0 );
user_setFootnote_EndDate();
'>+1y</button>
<script>
qbpms.form.on( 'ready', user_setFootnote_EndDate ); // for Initial Value
qbpms.form.on( 'change', 'q_EndDate', user_setFootnote_EndDate );
function user_setFootnote_EndDate(){
let dateStart = qbpms.form.get( "q_StartDate" );
if( dateStart === null ){ return; }
let dateEnd = qbpms.form.get( "q_EndDate" );
if( dateEnd === null ){ return; }
let numDateDifMsec = dateEnd.getTime() - dateStart.getTime();
let numDateDifDays = numDateDifMsec /1000 /60 / 60 /24;
let strInnerHtml = "";
if( numDateDifDays < 0 ){
strInnerHtml =
'<span style="background-color:#ff0000"> Start-End Range: ' +
( numDateDifDays + 1 ) + ' days ( < 0 )' +
'</span>';
}else{
strInnerHtml = ' Start-End Range: ' + ( numDateDifDays + 1 ) + ' days';
}
document.querySelector( '#user_footnote_EndDate' ).innerHTML = strInnerHtml;
}
function user_copyPasteQDate( qfieldNameSource, qfieldNameDestination ){
// Also supports QDatetime (Undefined parts completed 2000-01-01T00:00:00.)
if( qbpms.form.get( qfieldNameSource ) === null ){ return; }
const dateSource = qbpms.form.get( qfieldNameSource );
qbpms.form.set( qfieldNameDestination, dateSource );
}
</script>
<span id="user_footnote_NotificationDatetime"></span>
<br />
<button type='button' onclick='
user_copyPasteQDate( "q_StartDate", "q_NotificationDatetime" );
user_setFootnote_NotificationDatetime();
'>copy from q_StartDate</button>
<br />
<button type='button' onclick='
user_setHmQDatetime( "q_NotificationDatetime", 9, 0 );
user_setFootnote_NotificationDatetime();
'>09:00</button>
<button type='button' onclick='
user_setHmQDatetime( "q_NotificationDatetime", 12, 0 );
user_setFootnote_NotificationDatetime();
'>12:00</button>
<button type='button' onclick='
user_setHmQDatetime( "q_NotificationDatetime", 17, 0 );
user_setFootnote_NotificationDatetime();
'>17:00</button>
<br />
<button type='button' onclick='user_addYmdQDate( "q_NotificationDatetime", 0, -1, 0 ); user_setFootnote_NotificationDatetime();'>-1m</button>
<button type='button' onclick='user_addYmdQDate( "q_NotificationDatetime", 0, 0, -1 ); user_setFootnote_NotificationDatetime();'>-1d</button>
<button type='button' onclick='user_addYmdQDate( "q_NotificationDatetime", 0, 0, 0, -1 ); user_setFootnote_NotificationDatetime();'>-1h</button>
<button type='button' onclick='user_addYmdQDate( "q_NotificationDatetime", 0, 0, 0, 1 ); user_setFootnote_NotificationDatetime();'>+1h</button>
<button type='button' onclick='user_addYmdQDate( "q_NotificationDatetime", 0, 0, 1 ); user_setFootnote_NotificationDatetime();'>+1d</button>
<script>
qbpms.form.on( 'ready', user_setFootnote_NotificationDatetime ); // for Initial Value
qbpms.form.on( 'change', 'q_NotificationDatetime', user_setFootnote_NotificationDatetime );
function user_setFootnote_NotificationDatetime(){
let dateStart = qbpms.form.get( "q_StartDate" );
if( dateStart === null ){ return; }
let dateNoDa = qbpms.form.get( "q_NotificationDatetime" );
if( dateNoDa === null ){ return; }
let numDateDifMsec = dateNoDa.getTime() - dateStart.getTime();
let numDateDifDays = numDateDifMsec /1000 /60 / 60 /24;
let strInnerHtml = "";
if( numDateDifDays > 0 ){
strInnerHtml =
'<span style="background-color:#ff0000"> Start-Notification: ' +
Math.floor( numDateDifDays ) + ' days passed ( > 0 )' +
'</span>';
}else{
strInnerHtml = ' Start-Notification: ' + Math.floor( numDateDifDays ) + ' days';
}
document.querySelector( '#user_footnote_NotificationDatetime' ).innerHTML = strInnerHtml;
}
function user_setHmQDatetime( qfieldName, numHour = 0, numMin = 0 ){
let dateTmp = qbpms.form.get( qfieldName );
if( dateTmp === null ){ return; }
// console.log( dateTmp.toISOString() );
dateTmp.setHours ( numHour );
dateTmp.setMinutes ( numMin );
qbpms.form.set( qfieldName, dateTmp );
}
</script>
Copy to Clipboard

Input / Output
- ← STRING (STRING_TEXTFIELD):
q_SalesTSV
- → clipboard
Code Example for DESCRIPTION of Workflow-App
<button type='button' onclick='
user_copyQStringToClipboard( "q_SalesTSV", "#user_clicklog_copy" )
'>copy q_SalesTSV to Clipboard</button>
<span id="user_clicklog_copy"></span><br>
<script>
function user_copyQStringToClipboard( qfieldName, spanId ){
let strValue = qbpms.form.get( qfieldName );
let strInnerHtml = "";
if( strValue === "" ){ // ReadWrite
strInnerHtml = '<span style="color:#ff0000"> empty, clipboard not updated </span>';
}else if( strValue === null ){ // Read-only
strInnerHtml = '<span style="color:#ff8000"> empty, clipboard not updated </span>';
}else{
navigator.clipboard.writeText( strValue );
// https://developer.mozilla.org/docs/Web/API/Clipboard_API
let dateNow = new Date();
strInnerHtml = dateNow.toLocaleTimeString() + " copied (" + strValue.length + ")";
}
document.querySelector( spanId ).innerHTML = strInnerHtml;
}
</script>
Tab codes in Read-only String will not be copied.
Validate with Regular Expressions

Input / Output
- ← STRING (STRING_TEXTFIELD):
q_Email
- →
span#user_footnote_Email
Code Example for DESCRIPTION of Workflow-App
<span id="user_footnote_Email"></span><br>
<script>
qbpms.form.on( 'change', 'q_Email', user_setFootnote_Email );
function user_setFootnote_Email(){
let regEmail = /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
// https://html.spec.whatwg.org/multipage/input.html#valid-e-mail-address
let strInnerHtml = "";
let strTarget = qbpms.form.get( "q_Email" );
if( strTarget === "" ){ // ReadWrite QString
strInnerHtml = '<span style="color:#ffa500"> empty </span>';
}else if( strTarget === null ){ // Read-only QString ( no case )
strInnerHtml = '<span style="color:#ffa500"> empty / 空です </span>';
}else{
if( regEmail.test( strTarget ) ){
strInnerHtml = '<span style="color:#0000ff"> ✓ valid / 有効です </span>';
}else{
strInnerHtml = '<span style="color:#ff0000"> ✗ invalid / 問題があります </span>';
}
}
document.querySelector( '#user_footnote_Email' ).innerHTML = strInnerHtml;
}
</script>
Generate QR Code

Input / Output
- ← STRING (STRING_TEXTAREA)
q_Url
- →
span#user_output_encode
Code Example for DESCRIPTION of Workflow-App
<button type='button' onclick='
user_encodeQStringToQrcode( "q_Url", "#user_output_encode" )
'>encode q_Url to QR-code image</button><br>
<span id="user_output_encode"></span>
<script>
function user_encodeQStringToQrcode( qfieldName, spanId ){
let strValue = qbpms.form.get( qfieldName );
console.log( "Encode QString: " + strValue );
let strInnerHtml = "";
if( strValue === "" ){ // ReadWrite
strInnerHtml = '<span style="color:#ff0000"> empty, QR-Code image not created </span>';
document.querySelector( spanId ).innerHTML = strInnerHtml;
return;
}
if( strValue === null ){ // Read-only
strInnerHtml = '<span style="color:#ff8000"> empty, QR-Code image not created </span>';
document.querySelector( spanId ).innerHTML = strInnerHtml;
return;
}
// js-dynamic-loading
user_getScript( 'https://cdn.jsdelivr.net/npm/qrcode@1.4.4/build/qrcode.min.js',
'sha256-DhdpoP64xch/Frz8CiBQE12en55NX+RhlPGRg6KWm5s=',
function () {
// https://www.jsdelivr.com/package/npm/qrcode?version=1.4.4
// https://cdn.jsdelivr.net/npm/qrcode@1.4.4/license
// https://cdn.jsdelivr.net/npm/qrcode@1.4.4/README.md
const opts = {
errorCorrectionLevel: 'H', // L, M, Q, H
type: 'image/png', width: 200, quality: 0.3, margin: 1,
color: { dark:"#000", light:"#FFF0" } // alpha channel (opacity) = 0
};
QRCode.toDataURL( strValue, opts, function (err, url) {
if (err) throw err;
strInnerHtml = '';
strInnerHtml += '<a href="' + url + '" download="QR.png">';
strInnerHtml += '<img src="' + url + '" alt="QR Code" />';
strInnerHtml += '<br>';
strInnerHtml += 'Click to Download';
strInnerHtml += '</a>';
document.querySelector( spanId ).innerHTML = strInnerHtml;
});
}); // endof js-dynamic-loading
}
function user_getScript(scriptUrl, integrity, callback) { // for dynamic-loading
const script = document.createElement('script');
script.src = scriptUrl;
// Subresource Integrity (SRI) is a security feature that enables browsers to verify that
// resources they fetch (for example, from a CDN) are delivered without unexpected manipulation.
// https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity
script.integrity = integrity;
script.crossOrigin = "anonymous"; // note: property name is "crossOrigin", not "crossorigin"
script.onload = callback;
document.body.appendChild(script);
}
</script>
Preview HTML Code in New Window

Input / Output
- ← STRING (STRING_TEXTAREA):
q_Str_EmailHtmlReport
- → window:
_Blank
Code Example for DESCRIPTION of Workflow-App
<button type='button' onclick='
user_previewHtmlInNewWindow( "q_Str_EmailHtmlReport" )
'>Open Window to Preview HTML</button>
<script>
function user_previewHtmlInNewWindow( qfieldName ) {
let strHtml = qbpms.form.get( qfieldName );
if( strHtml === null ){ return; } // when ReadOnly
if( strHtml === "" ){
throw new Error('No String in this Textarea');
}
let blob = new Blob([ strHtml ], { type: 'text/html' });
let strUrl = URL.createObjectURL(blob);
window.open( strUrl, "_Blank" );
}
</script>
Export Text as UTF8B-File


Input / Output
- ← STRING (STRING_TEXTAREA):
q_MultilineString
- →
download.txt
(download)
Code Example for DESCRIPTION of Workflow-App
<button type='button' onclick='user_exportAsFile_MultilineString()'>Save as Text File</button>
<script>
function user_exportAsFile_MultilineString(){
let strTmp = qbpms.form.get("q_Multiline_String");
if( strTmp === null ){ return; } // when ReadOnly
if( strTmp === "" ) { return; } // when ReadWrite
let bom = new Uint8Array([0xEF, 0xBB, 0xBF]);
let blob = new Blob([bom, strTmp], {type: 'text/plain'});
let url = (window.URL || window.webkitURL).createObjectURL(blob);
let link = document.createElement('a');
link.download = 'download.txt';
link.href = url;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
</script>
Check Form History via REST API

Input / Output
- ← STRING (STRING_TEXTFIELD):
q_ContentsOfDuties
- ← Questetra Workflow API, ProcessInstance:
get /API/OR/ProcessInstance/list
- →
span#user_Result_ContentsOfDuties
- ←
button.user_Appender
- → STRING (STRING_TEXTFIELD):
q_ContentsOfDuties
- ←
Code Example for DESCRIPTION of Workflow-App
<button type="button" onclick="user_getHistory_ContentsOfDuties()" class="user_Search">Sample Search →</button>
<span id="user_footnote_ContentsOfDuties"></span>
<script>
document.addEventListener("click", function (e) {
console.log( " DocumentElement clicked: " + e.target.textContent );
if (e.target.classList.contains("user_Appender")) {
qbpms.form.set( "q_ContentsOfDuties", e.target.value ); ///EDIT///
}
});
function user_getHistory_ContentsOfDuties() {
let numTARGETID = 8; ///EDIT/// ID of q_ContentsOfDuties
let numLIMITSIZE = 10; ///EDIT///
let numWORKFLOWAPPID = 1883; ///EDIT///
let strSearchWord = qbpms.form.get( "q_ContentsOfDuties" ); ///EDIT///
let elResultSpan = document.querySelector("#user_footnote_ContentsOfDuties"); ///EDIT///
let objCriteria = {}; // JSON criteria: (BPMS v13.3~)
// https://questetra.zendesk.com/hc/ja/articles/4415341995289
// https://questetra.zendesk.com/hc/en-us/articles/4415341995289
objCriteria.processModelInfoId = numWORKFLOWAPPID;
objCriteria.processInstanceState = [];
objCriteria.processInstanceState[0] = "ENDED"; // only completed instances
if ( strSearchWord !== "" ) {
objCriteria.data = [];
objCriteria.data[0] = {};
objCriteria.data[0].type = "string";
objCriteria.data[0].number = numTARGETID;
objCriteria.data[0].method = "contains";
objCriteria.data[0].value = strSearchWord;
}
objCriteria.fields = [];
objCriteria.fields[0] = {};
objCriteria.fields[0].type = "string";
objCriteria.fields[0].number = numTARGETID;
let xhr = new XMLHttpRequest();
xhr.open( "GET", "/API/OR/ProcessInstance/list?limit=" + numLIMITSIZE +
"&criteria=" + encodeURIComponent(JSON.stringify(objCriteria)) );
xhr.responseType = 'json';
xhr.send();
xhr.onload = function() {
if (xhr.status != 200) {
console.error(`Error ${xhr.status}: ${xhr.statusText}`); // e.g. "404: Not Found"
} else { // show the result
let objResponse = xhr.response;
console.log( " Decoration XHR: Found PastData " + objResponse.processInstances.length +
"/" + objResponse.count );
elResultSpan.innerHTML = ""; // clear
for (let i = 0; i < objResponse.processInstances.length; i++ ) {
let elNewAnchor = document.createElement("a");
elNewAnchor.href = "/OR/ProcessInstance/view?processInstanceId=" +
objResponse.processInstances[i].processInstanceId;
elNewAnchor.target = "_blank";
elNewAnchor.innerText = "p" + objResponse.processInstances[i].processInstanceId;
let elNewButton = document.createElement("button");
elNewButton.type = "button";
elNewButton.classList.add("user_Appender");
if ( objResponse.processInstances[i].data[numTARGETID].value === null ){
elNewButton.value = "";
elNewButton.innerHTML = "?";
}else{
elNewButton.value = objResponse.processInstances[i].data[numTARGETID].value;
elNewButton.innerText = objResponse.processInstances[i].data[numTARGETID].value;
}
// "<a>p123</a>=<button>消耗品費</button>"
elResultSpan.append( " ", elNewAnchor,"=" );
elResultSpan.append( elNewButton );
}
}
console.log( " Decoration XHR: finished " );
};
xhr.onerror = function() {
alert("Request failed");
};
}
</script>
<style type="text/css">
button.user_Appender {
border : 0;
line-height : 1.2;
padding : 3px 8px;
font-size : 0.9rem;
text-align : center;
color : #fff;
text-shadow : 1px 1px 1px #000;
border-radius : 10px;
background-color: #050505;
}
button.user_Appender:hover {
background-color: #00bb00;
}
button.user_Appender:active {
box-shadow : inset -2px -2px 3px rgba(255, 255, 255, .6),
inset 2px 2px 3px rgba(0, 0, 0, .6);
}
button.user_Search {
display : inline-block;
border-radius : 5%;
font-size : 1rem;
text-align : center;
cursor : pointer;
padding : 5px 20px;
background : #009900;
color : #ffffff;
line-height : 1em;
transition : .3s;
box-shadow : 2px 2px 3px #666666;
border : 2px solid #009900;
}
button.user_Search:hover {
box-shadow : none;
color : #009900;
background : #ffffff;
}
</style>
Switch between Edit and Read-Only

Input / Output
- ← SELECT (SELECT_SINGLE)
q_Answer
- → STRING (STRING_TEXTFIELD)
q_FreeText
Code Example for DESCRIPTION of Workflow-App
<script>
qbpms.form.on( 'ready', user_syncWithSelected ); // for Initial Value
qbpms.form.on( 'change', 'q_Answer', user_syncWithSelected );
function user_syncWithSelected(){
let qfieldNameSelect = "q_Answer"; //// EDIT
let qfieldNameString = "q_FreeText"; //// EDIT
/// Format as of v14.1 (Risk of change in the future)
let selector = "div[data-var-name='" + qfieldNameString + "']";
let elContainer = document.querySelector( selector );
let elTarget = elContainer.querySelector( "input" );
let arrOptions = qbpms.form.get( qfieldNameSelect );
console.log( "Num of Selected: " + arrOptions.length );
if( arrOptions.length ){
if( arrOptions[0].value === 'other' ){
// https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/readonly
// https://developer.mozilla.org/ja/docs/Web/HTML/Attributes/readonly
elTarget.readOnly = false;
elTarget.style.backgroundColor = "#ffffff";
return;
}
}
elTarget.readOnly = true;
elTarget.style.backgroundColor = "#f2f2f2";
}
</script>