diff --git a/src/zabapgit_css_common.w3mi.data.css b/src/zabapgit_css_common.w3mi.data.css
index 45d35dc88..4ab352f62 100644
--- a/src/zabapgit_css_common.w3mi.data.css
+++ b/src/zabapgit_css_common.w3mi.data.css
@@ -47,7 +47,9 @@ input:focus, textarea:focus {
.crossout { text-decoration: line-through !important; }
.right { text-align:right; }
.paddings { padding: 0.5em 0.5em; }
-.pad-sides { padding: 0 0.3em; }
+.pad-sides { padding-left: 0.3em; padding-right: 0.3em; }
+.margin-v5 { margin-top: 0.5em; margin-bottom: 0.5em; }
+.indent5em { padding-left: 0.5em; }
.pad4px { padding: 4px; }
.w100 { width: 100%; }
.w40 { width: 40%; }
@@ -360,6 +362,9 @@ table.repo_tab {
}
/* STAGE */
+div.stage-container { width: 850px; }
+input.stage-filter { width: 18em; }
+
.stage_tab {
border: 1px solid #DDD;
background: #fff;
@@ -388,6 +393,8 @@ table.repo_tab {
color: #444 !important;
font-weight: bold;
}
+
+.stage_tab td.cmd { cursor: pointer; }
.stage_tab td.cmd a { padding: 0px 4px; }
.stage_tab th.cmd a { padding: 0px 4px; }
.stage_tab td.method { color: #ccc; }
diff --git a/src/zabapgit_html_chunks.prog.abap b/src/zabapgit_html_chunks.prog.abap
index 2cb693cfe..de94e6cca 100644
--- a/src/zabapgit_html_chunks.prog.abap
+++ b/src/zabapgit_html_chunks.prog.abap
@@ -209,7 +209,9 @@ CLASS lcl_gui_chunk_lib IMPLEMENTATION.
METHOD render_js_error_banner.
CREATE OBJECT ro_html.
ro_html->add( '
' ).
- ro_html->add( |{ lcl_html=>icon( 'alert/red' ) } JS init error, please log an issue| ).
+ ro_html->add( |{ lcl_html=>icon( 'alert/red' ) }| &&
+ ' If this does not disappear soon,' &&
+ ' then there is a JS init error, please log an issue' ).
ro_html->add( '
' ).
ENDMETHOD. "render_js_error_stub
diff --git a/src/zabapgit_js_common.w3mi.data.js b/src/zabapgit_js_common.w3mi.data.js
index 45496a49a..f37db91d9 100644
--- a/src/zabapgit_js_common.w3mi.data.js
+++ b/src/zabapgit_js_common.w3mi.data.js
@@ -87,6 +87,37 @@ function confirmInitialized() {
debugOutput("js: OK"); // Final final confirmation :)
}
+/**********************************************************
+ * Performance utils (for debugging)
+ **********************************************************/
+
+var gPerf = [];
+
+function perfOut(prefix) {
+ var totals = {};
+ for (var i = gPerf.length - 1; i >= 0; i--) {
+ if (!totals[gPerf[i].name]) totals[gPerf[i].name] = {count: 0, time: 0};
+ totals[gPerf[i].name].time += gPerf[i].time;
+ totals[gPerf[i].name].count += 1;
+ }
+
+ var keys = Object.keys(totals);
+ for (var i = keys.length - 1; i >= 0; i--) {
+ console.log(prefix
+ + " " + keys[i] + ": "
+ + totals[keys[i]].time.toFixed(3) + "ms"
+ + " (" + totals[keys[i]].count.toFixed() +")");
+ }
+}
+
+function perfLog(name, startTime) {
+ gPerf.push({name: name, time: window.performance.now() - startTime});
+}
+
+function perfClear() {
+ gPerf = [];
+}
+
/**********************************************************
* STAGE PAGE Logic
**********************************************************/
@@ -113,12 +144,19 @@ function StageHelper(params) {
// Constants
this.HIGHLIGHT_STYLE = "highlight";
this.STATUS = {
- "add": "A",
- "remove": "R",
- "ignore": "I",
- "reset": "?"
+ add: "A",
+ remove: "R",
+ ignore: "I",
+ reset: "?",
+ isValid: function (status) { return "ARI?".indexOf(status) == -1; }
};
+ this.TEMPLATES = {
+ cmdReset: "reset",
+ cmdLocal: "add",
+ cmdRemote: "ignoreremove"
+ };
+
this.setHooks();
}
@@ -154,34 +192,32 @@ StageHelper.prototype.onPageUnload = function() {
// Re-store table state on entering the page
StageHelper.prototype.onPageLoad = function() {
-
var data = window.sessionStorage && JSON.parse(window.sessionStorage.getItem(this.pageSeed));
- if (data) {
- this.iterateStageTab(function (row) {
- var status = data[row.cells[this.col.name].innerText];
- this.updateRow(row, status || this.STATUS.reset);
- });
- debugOutput("StageHelper.onPageLoad from Storage");
-
- } else { // Render initial commands
-
- this.iterateStageTab(function (row) {
- this.updateRow(row, this.STATUS.reset);
- });
- debugOutput("StageHelper.onPageLoad initial state");
- }
+ this.iterateStageTab(true, function (row) {
+ var status = data && data[row.cells[this.col.name].innerText];
+ this.updateRow(row, status || this.STATUS.reset);
+ });
this.updateMenu();
+ debugOutput("StageHelper.onPageLoad: " + ((data) ? "from Storage" : "initial state"));
}
// Table event handler, change status
StageHelper.prototype.onTableClick = function (event) {
var target = event.target || event.srcElement;
- if (!target || target.tagName != "A") return;
+ if (!target) return;
- var td = target.parentNode;
- if (!td || ["TD","TH"].indexOf(td.tagName) == -1 || td.className != "cmd") return;
+ if (target.tagName === "A") {
+ var td = target.parentNode;
+ } else if (target.tagName === "TD") {
+ var td = target;
+ if (td.children.length === 1 && td.children[0].tagName === "A") {
+ target = td.children[0];
+ } else return;
+ } else return;
+
+ if (["TD","TH"].indexOf(td.tagName) == -1 || td.className != "cmd") return;
var status = this.STATUS[target.innerText]; // Convert anchor text to status
var targetRow = td.parentNode;
@@ -189,9 +225,9 @@ StageHelper.prototype.onTableClick = function (event) {
if (td.tagName === "TD") {
this.updateRow(targetRow, status);
} else { // TH
- this.iterateStageTab(function (row) {
- if (row.style.display !== "none" // Not filtered out
- && row.className === targetRow.className // Same context as header
+ this.iterateStageTab(true, function (row) {
+ if (row.style.display !== "none" // Not filtered out
+ && row.className === targetRow.className // Same context as header
) {
this.updateRow(row, status);
}
@@ -208,7 +244,7 @@ StageHelper.prototype.onSearch = function (e) {
|| e.type === "keypress" && e.which === 13 ) {
this.lastSearchValue = e.target.value;
- this.iterateStageTab(this.applyFilterToRow, e.target.value);
+ this.iterateStageTab(true, this.applyFilterToRow, e.target.value);
}
}
@@ -219,14 +255,10 @@ StageHelper.prototype.applyFilterToRow = function (row, filter) {
if (filter) {
newTxt = origTxt.replace(filter, ""+filter+"");
- if (newTxt !== origTxt) { // fits filter
- row.style.display = "table-row";
- } else {
- row.style.display = "none";
- }
+ row.style.display = (newTxt !== origTxt) ? "" : "none";
} else { // No filter -> just reset the value
newTxt = origTxt;
- row.style.display = "table-row";
+ row.style.display = ""; // default, visible
}
if (td.firstChild.tagName === "A") {
@@ -240,7 +272,7 @@ StageHelper.prototype.applyFilterToRow = function (row, filter) {
StageHelper.prototype.getStatusImpact = function (status) {
if (typeof status !== "string"
|| status.length !== 1
- || "ARI?".indexOf(status) == -1) {
+ || this.STATUS.isValid(status) ) {
alert("Unknown status");
} else {
return (status !== this.STATUS.reset) ? 1 : 0;
@@ -252,37 +284,41 @@ StageHelper.prototype.updateRow = function (row, newStatus) {
var oldStatus = row.cells[this.col.status].innerText;
if (oldStatus !== newStatus) {
- row.cells[this.col.status].innerText = newStatus;
- if (newStatus === this.STATUS.reset) {
- row.cells[this.col.status].classList.remove(this.HIGHLIGHT_STYLE);
- } else {
- row.cells[this.col.status].classList.add(this.HIGHLIGHT_STYLE);
- }
+ this.updateRowStatus(row, newStatus);
this.updateRowCommand(row, newStatus);
- } else if (!row.cells[this.col.cmd].innerText) {
+ } else if (!row.cells[this.col.cmd].children.length) {
this.updateRowCommand(row, newStatus); // For initial run
}
this.choiseCount += this.getStatusImpact(newStatus) - this.getStatusImpact(oldStatus);
}
-// Update command cell (render set of commands)
-StageHelper.prototype.updateRowCommand = function (row, status) {
- var CMD_RESET = "reset";
- var CMD_LOCAL = "add";
- var CMD_REMOTE = "ignoreremove";
-
+// Update Status cell (render set of commands)
+StageHelper.prototype.updateRowStatus = function (row, status) {
+ row.cells[this.col.status].innerText = status;
if (status === this.STATUS.reset) {
- row.cells[this.col.cmd].innerHTML = (row.className == "local") ? CMD_LOCAL : CMD_REMOTE;
+ row.cells[this.col.status].classList.remove(this.HIGHLIGHT_STYLE);
} else {
- row.cells[this.col.cmd].innerHTML = CMD_RESET;
+ row.cells[this.col.status].classList.add(this.HIGHLIGHT_STYLE);
+ }
+}
+
+// Update Command cell (render set of commands)
+StageHelper.prototype.updateRowCommand = function (row, status) {
+ var cell = row.cells[this.col.cmd];
+ if (status === this.STATUS.reset) {
+ cell.innerHTML = (row.className == "local")
+ ? this.TEMPLATES.cmdLocal
+ : this.TEMPLATES.cmdRemote;
+ } else {
+ cell.innerHTML = this.TEMPLATES.cmdReset;
}
}
// Update menu items visibility
StageHelper.prototype.updateMenu = function () {
- this.dom.commitBtn.style.display = (this.choiseCount > 0) ? "inline" : "none";
- this.dom.commitAllBtn.style.display = (this.choiseCount > 0) ? "none" : "inline";
+ this.dom.commitBtn.style.display = (this.choiseCount > 0) ? "" : "none";
+ this.dom.commitAllBtn.style.display = (this.choiseCount > 0) ? "none" : "";
this.dom.fileCounter.innerHTML = this.choiseCount.toString();
}
@@ -294,19 +330,36 @@ StageHelper.prototype.submit = function () {
// Extract data from the table
StageHelper.prototype.collectData = function () {
var data = {};
- this.iterateStageTab(function (row) {
+ this.iterateStageTab(false, function (row) {
data[row.cells[this.col.name].innerText] = row.cells[this.col.status].innerText;
});
return data;
}
-// table iteration helper
-StageHelper.prototype.iterateStageTab = function (cb /*, ...*/) {
- for (var b = 0, bN = this.dom.stageTab.tBodies.length; b < bN; b++) {
- var tbody = this.dom.stageTab.tBodies[b];
+// Table iteration helper
+StageHelper.prototype.iterateStageTab = function (changeMode, cb /*, ...*/) {
+
+ var restArgs = Array.prototype.slice.call(arguments, 2);
+ var table = this.dom.stageTab;
+
+ if (changeMode) {
+ var scrollOffset = window.pageYOffset;
+ this.dom.stageTab.style.display = "none";
+ // var stageTabParent = this.dom.stageTab.parentNode;
+ // table = stageTabParent.removeChild(this.dom.stageTab);
+ }
+
+ for (var b = 0, bN = table.tBodies.length; b < bN; b++) {
+ var tbody = table.tBodies[b];
for (var r = 0, rN = tbody.rows.length; r < rN; r++) {
- args = [tbody.rows[r]].concat(Array.prototype.slice.call(arguments, 1));
+ args = [tbody.rows[r]].concat(restArgs);
cb.apply(this, args); // callback
}
}
+
+ if (changeMode) {
+ this.dom.stageTab.style.display = "";
+ // stageTabParent.appendChild(table);
+ window.scrollTo(0, scrollOffset);
+ }
}
diff --git a/src/zabapgit_page_stage.prog.abap b/src/zabapgit_page_stage.prog.abap
index 4151272a7..32098bace 100644
--- a/src/zabapgit_page_stage.prog.abap
+++ b/src/zabapgit_page_stage.prog.abap
@@ -31,22 +31,26 @@ CLASS lcl_gui_page_stage DEFINITION FINAL INHERITING FROM lcl_gui_page.
METHODS:
render_list
RETURNING VALUE(ro_html) TYPE REF TO lcl_html,
+
render_file
IMPORTING iv_context TYPE string
is_file TYPE ty_file
is_item TYPE ty_item OPTIONAL
RETURNING VALUE(ro_html) TYPE REF TO lcl_html,
- render_menu
+
+ render_actions
RETURNING VALUE(ro_html) TYPE REF TO lcl_html,
+
read_last_changed_by
IMPORTING is_file TYPE ty_file
- RETURNING VALUE(rv_user) TYPE xubname.
+ RETURNING VALUE(rv_user) TYPE xubname,
- METHODS process_stage_list
- IMPORTING it_postdata TYPE cnht_post_data_tab
- RAISING lcx_exception.
- METHODS build_menu
- RETURNING VALUE(ro_menu) TYPE REF TO lcl_html_toolbar.
+ process_stage_list
+ IMPORTING it_postdata TYPE cnht_post_data_tab
+ RAISING lcx_exception,
+
+ build_menu
+ RETURNING VALUE(ro_menu) TYPE REF TO lcl_html_toolbar.
ENDCLASS.
@@ -162,19 +166,21 @@ CLASS lcl_gui_page_stage IMPLEMENTATION.
CREATE OBJECT ro_html.
- ro_html->add( '' ).
+ ro_html->add( '' ).
" Local changes
LOOP AT ms_files-local ASSIGNING .
AT FIRST.
- ro_html->add('').
- ro_html->add('Type | ').
- ro_html->add('Files to add (click to see diff) | ' ).
- ro_html->add('Changed by | ').
- ro_html->add(' | ' ). " Status
- ro_html->add('↓add/reset↓ | ' ).
- ro_html->add('
').
- ro_html->add('').
+ ro_html->add( '' ).
+ ro_html->add( 'Type | ' ).
+ ro_html->add( 'Files to add (click to see diff) | ' ).
+ ro_html->add( 'Changed by | ' ).
+ ro_html->add( ' | ' ). " Status
+ ro_html->add( '' ).
+ ro_html->add( 'add↓ reset↓' ).
+ ro_html->add( ' | ' ).
+ ro_html->add( '
' ).
+ ro_html->add( '' ).
ENDAT.
ro_html->add( render_file(
@@ -194,8 +200,9 @@ CLASS lcl_gui_page_stage IMPLEMENTATION.
ro_html->add( ' | ' ). " Type
ro_html->add( 'Files to remove or non-code | ' ).
ro_html->add( ' | ' ). " Status
- ro_html->add( '' &&
- '↓ignoreremovereset↓ | ' ).
+ ro_html->add( '' ).
+ ro_html->add( 'ignore↓ remove↓ reset↓' ).
+ ro_html->add( ' | ' ).
ro_html->add( '' ).
ro_html->add( '' ).
ENDAT.
@@ -252,38 +259,52 @@ CLASS lcl_gui_page_stage IMPLEMENTATION.
ro_html->add( '' ).
ro_html->add( lcl_gui_chunk_lib=>render_repo_top( mo_repo ) ).
ro_html->add( lcl_gui_chunk_lib=>render_js_error_banner( ) ).
- ro_html->add( render_menu( ) ).
+
+ ro_html->add( '
' ).
+ ro_html->add( render_actions( ) ).
ro_html->add( render_list( ) ).
ro_html->add( '
' ).
+ ro_html->add( '
' ).
+
ENDMETHOD. "render_content
- METHOD render_menu.
+ METHOD render_actions.
- DATA lv_local_count TYPE i.
+ DATA: lv_local_count TYPE i,
+ lv_add_all_txt TYPE string.
CREATE OBJECT ro_html.
lv_local_count = lines( ms_files-local ).
+ IF lv_local_count > 0.
+ lv_add_all_txt = |Add all and commit ({ lv_local_count })|.
+ " Otherwise empty, but the element (id) is preserved for JS
+ ENDIF.
- ro_html->add( '' ).
+ ro_html->add( '
' ).
+
+ ENDMETHOD. "render_actions
METHOD scripts.