NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript==
// @name ArcSoft Project Management
// @version 5
// @author maxint <NOT_SPAM_lnychina@gmail.com>, xhhjin
// @namespace http://maxint.github.io
// @description An enhancement for Arcsoft project management system in http://doc-server
// @include http://doc-server/*
// @include https://doc-server/*
// @updateURL https://github.com/xhhjin/userjs/raw/master/docserver/doc-server-project-ms.user.js
// @downloadURL https://github.com/xhhjin/userjs/raw/master/docserver/doc-server-project-ms.user.js
// @grant none
// @Note
// v5
// - Fix bug of no response in Firefox.
// - Sort project Ids.
//
// v4
// - Fix bug of wrong id in Chrome.
//
// v3
// - Update id checkboxes w.r.t. keyboard input.
// - Sort project id rows w.r.t. checked statuses.
// - Add input project id automatically.
//
// v2
// - Fix bug of pause when multiple delivery packages.
// - Add checkboxes to project id table.
//
// v1
// - Add support for delivery multiple packages to multiple projects at one time.
//
// v0.0.9
// - Add https support.
// - Add "Release" column to "/index2014/Engineering/index.asp" page.
//
// v0.0.8
// - Add partial auto fill support for "Edit" window.
// - Add message alert when flushing project IDs.
// - Fix bug of "Invert Rows" caused by web page update.
//
// v0.0.7
// - Update @updateURL and @downloadURL.
// - Add support for "Edit" in "Add Release Page".
//
// v0.0.6
// - Fix bug of date format.
//
// v0.0.5
// - Login page: login when press <Enter> in password input.
//
// v0.0.4
// - Add management of "Related Projects".
//
// v0.0.3
// - Add "Invert Rows" button to "Release List" page.
//
// v0.0.2
// - Auto save data in "Add Rlease" form.
//
// v0.0.1
// - 首页 | “项目” 导航到 “项目跟踪”
// - “项目跟踪”页面添加 "Release"
// - "Add Release Package" 窗口:
// * 自动填写日期
// * 根据Release Package填写Version
// * 自动查询Related Project名称
//
// ==/UserScript==
// a function that loads jQuery and calls a callback function when jQuery has finished loading
; (function (callback, safe) {
if (typeof(jQuery) == "undefined") {
var script = document.createElement("script");
script.type = "text/javascript";
script.src = "https://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.11.1.min.js";
if (safe) {
var cb = document.createElement("script");
cb.type = "text/javascript";
cb.textContent = "jQuery.noConflict();(" + callback.toString() + ")(jQuery, window);";
script.addEventListener('load', function () {
document.head.appendChild(cb);
});
} else {
var dollar = undefined;
if (typeof($) != "undefined") dollar = $;
script.addEventListener('load', function () {
jQuery.noConflict();
$ = dollar;
callback(jQuery, window);
});
}
document.head.appendChild(script);
} else {
console.log('Using jquery ' + jQuery().jquery);
setTimeout(function () {
//Firefox supports
console.log('Runing custom script');
callback(jQuery, typeof(unsafeWindow) == "undefined" ? window : unsafeWindow);
}, 30);
}
})(function ($, window) {
// helper functions
// local storage
var IStorage = function (prefix) {
var pref = prefix + '.';
var addpref = function (key) { return pref + key; };
this.get = function (key, def) {
var val = window.localStorage.getItem(addpref(key));
return val || def || null;
};
this.set = function (key, val) {
window.localStorage.setItem(addpref(key), val);
};
this.flush = function () {
window.localStorage.clear();
};
this.getObject = function (key, def) {
var val = this.get(key);
return val ? JSON.parse(val) : (def || null);
};
this.setObject = function (key, val) {
this.set(key, JSON.stringify(val));
};
};
// assert function
function assert(condition, message) {
if (!condition) {
throw message || "Assertion failed";
}
}
// replace main menu [Project]
function redirectToProjectListPage(obj) {
obj.attr('href', '/projectManage/ProjectList.asp');
}
redirectToProjectListPage($('li#mainMenuItem_150000 a'));
// add "Release" column in second row of table
function addReleaseToTable(table) {
var trs = table.find('tbody tr');
trs.first().find('th:nth-child(2)').after('<th>Operations</th>');
trs.nextAll().each(function () {
var id = $(this).find('td:first a:first').text();
var rlsUrl = 'http://doc-server/projectManage/ProjectOther/ReleaseList.asp?proj_id=' + id
var link = '<a href="' + rlsUrl + '">Release</a>'
$(this).find('td:nth-child(2)').after('<td>' + link + '</td>');
});
};
// fill input values
var fillValues = function (dict, istore) {
for (var key in dict) {
var elem = dict[key];
if (!elem.val())
elem.val(istore.get(key, ''));
}
}
// save input values
var storeValues = function (dict, istore) {
for (var key in dict) {
istore.set(key, dict[key].val());
}
}
// operate w.r.t. sub path
var subpath = window.location.pathname;
if (subpath == '/login.asp') {
$('input#password').keyup(function (e) {
if (e.which == 13) {
$('input[value="Login"]').click();
}
});
} else if (subpath == '/index2014/index.asp') {
console.log('Index2014');
redirectToProjectListPage($('div#headerTopDiv div a:nth-child(2)'));
} else if (subpath == '/projectManage/ProjectList.asp') {
console.log('ProjectList');
addReleaseToTable($('table.ListTable'));
} else if (subpath == '/index2014/Engineering/index.asp') {
console.log('Engineering');
addReleaseToTable($('#workspace table:first'));
} else if (subpath == '/projectManage/ProjectOther/ReleaseList.asp') {
console.log('ReleaseList');
if (window.location.protocol == 'https:') {
console.log('Redirecting to http ...');
window.location.replace(window.location.href.replace('https://', 'http://'));
return;
}
// invert table row
$('<input type="button" value="Invert Rows"/>').insertBefore($("input[type='button']")).click(function () {
$('table.ListTable tbody').each(function (elem, index) {
var arr = $.makeArray($("tr", this).detach());
var th = arr.shift();
arr.reverse();
arr.unshift(th);
$(this).append(arr);
});
}).click();
} else if (subpath == '/projectManage/ProjectDelivery/delivery_codingreport_update.asp') {
console.log('delivery');
var id = $('#projectid').val();
var istore = new IStorage('project/' + id);
var dict = {
'svn_path': $('input#SourceCodePath'),
'last_tag': $('input#SOSLabel'),
'hzpm_notes': $('textarea#hzpm_notes'),
};
fillValues(dict, istore);
$(window).unload(function () {
console.log('window unload');
storeValues(dict, istore);
});
} else if (subpath == '/projectManage/ProjectOther/addRelease.asp') {
console.log('addRelease');
var istore = new IStorage('addRelease');
// related project IDs
var IDManager = function (db) {
var storeid = 'IDs';
var idb = db;
var cb = function () {};
this.data = {};
this.load = function () {
this.data = idb.getObject(storeid, {});
cb(this.data);
return this;
};
this.save = function () {
idb.setObject(storeid, this.data);
return this;
};
this.contains = function (id) {
return id in this.data;
};
this.add = function (id, name) {
assert(/^\d{4}$/.test(id));
if (!this.contains(id)) {
this.data[id] = {name: name};
cb(this.data);
}
return this;
};
this.set = function (id, name) {
if (this.contains(id)) {
this.data[id] = {name: name};
}
return this;
};
this.remove = function (id) {
if (this.contains(id)) {
delete this.data[id];
cb(this.data);
}
return this;
};
this.clear = function (id) {
if (this.data) {
this.data = {};
cb(this.data);
}
return this;
};
this.change = function (callback) {
var oldcb = cb;
cb = function (data) { oldcb(data); callback(data); }
return this;
};
this.check = function (id, callback) {
if (/^[0-9]{4}$/.test(id)) {
var url = '../ProjectDelivery/delivery_releated_project_list.asp';
$.get(url, {projid: id}, function (data, status) {
name = $(data).find('td:nth-child(2)').text();
if (status == 'success' && name != "" && callback) {
proj_status = $(data).find('td:nth-child(3)').text();
callback(id, name, proj_status);
} else {
callback(id);
}
});
} else {
callback();
}
return this;
};
};
var idmgr = new IDManager(istore);
if (window.location.search.indexOf('IdentityID=') == -1) {
// date
var d = new Date();
$('input#txtDate').val(d.getFullYear() + '/' + (d.getMonth()+1) + '/' + d.getDate());
// create packages textarea
var packagesTextArea = $('<textarea id="txtDeliveryPackages"></textarea>').css({
'width': '100%',
'height': '80px',
});
$('input#txtDeliveryPackage').hide().siblings('input').hide().last().after(packagesTextArea);
packagesTextArea.next().each(function () {
$(this).html('请输入递交包文件名,支持多个文件一起提交,每行一个文件名。');
});
// fill previous data
var dict = {
'version': $("input[name='txtVersion']"),
'releasepath': $('input#txtReleasePath'),
'package': $('input#txtDeliveryPackage'),
'packages': $('textarea#txtDeliveryPackages'),
'relatedProjectID': $("input[name='txtReleaseReleatedProject']"),
'note': $('textarea#txtNotes'),
};
fillValues(dict, istore);
// save result before close window
$(window).unload(function () {
console.log('window closing');
storeValues(dict, istore);
idmgr.save();
});
// submit
$('#btnSubmit').removeAttr('onclick').click(function () {
// check form
console.log('checking form ...');
if (!checkReleateProject())
return;
var pkgs = packagesTextArea.val().split('\n');
for (var i in pkgs) {
var pkg = pkgs[i];
$('input#txtReleasePath,input#txtDeliveryPackage').val(pkg);
if (!$('#form1').valid()) {
alert('提交包列表格式不对');
return;
}
}
// submit
$(this).attr('disabled', true);
for (var i in pkgs) {
var pkg = pkgs[i];
console.log('submitting ' + pkg + ' ...');
$('input#txtReleasePath,input#txtDeliveryPackage').val(pkg);
var qstr = $('#form1').formSerialize();
$.post('/projectManage/Ajax/AjaxSubmitProjectRelease.asp', qstr, function (data) {
if (data != "success")
alert(data);
});
}
alert("提交成功");
parent.document.location.reload();
});
}
// update version
$('#txtDeliveryPackage,#txtDeliveryPackages').change(function () {
var val = $(this).val();
var vers = /(\d+)\.(\d+)\.(\d+)\.(\d+)/g.exec(val);
if (vers == null)
vers = /(\d+)\.(\d+)\.(\d+)/g.exec(val);
if (vers) {
vstr = [vers[1], vers[2], vers[3], vers[4]].join('.')
$("input[name='txtVersion']").val(vstr);
}
}).css({
'width': '100%',
});
// release package
$('input#txtReleasePath').css({
'width': '100%',
});
// related project table
$(function () {
var table = $('<table>' +
'<thead><tr style="width:10px">' +
'<td><input id="selectAllIDs" type="checkbox"></td><td style="width:40px">ID</td><td>Name</td><td style="width:60px">Operation</td>' +
'</tr></thead>' +
'<tbody></tbody>' +
'<tfoot>' +
'<tr><td/>' +
'<td><input id="inputID" type="text" maxlength="4" size="4"/></td>' +
'<td id="inputIDtxt" class="projIdName">Clicking "Add" to add item</td>' +
'<td><input id="addID" type="button" value="Add" disabled="true"/></td></tr>' +
'<tr><td/><td/><td>Clear all saved data</td>' +
'<td><input id="flushIDs" type="button" value="Flush"/></td></tr>' +
'</tfoot>' +
'</table>').css({ width: '100%' });
$('input#btnCheckReleatedProject').after('<div></div>').next().after(table);
// update check states of check boxes
var updateCheckBoxes = function() {
var checked = table.find('tbody input:checked').length;
var total = table.find('tbody input:checkbox').length;
table.find('input#selectAllIDs').each(function () {
this.indeterminate = checked != total && checked != 0;
if (!this.indeterminate)
this.checked == checked == total;
});
};
// detete id input box
$("input[name='txtReleaseReleatedProject']").keyup(function () {
var val = this.value.trim();
var selected = val.split(',');
if (val == '' || selected.every(function(id) { return /^\d{4}$/.test(id); })) {
console.log('Selected: ' + selected);
$(':checkbox', table).attr('checked', false);
for (var i in selected) {
var id = selected[i];
if (!idmgr.contains(id)) {
idmgr.check(id, function (id, name) {
if (name) {
idmgr.add(id, name);
}
});
}
$('#proj_' + id + ' :checkbox', table).attr('checked', true);
}
// move checked IDs to the front
$('tbody input:checked', table).parent().parent().detach().prependTo($('tbody', table));
}
updateCheckBoxes();
});
idmgr.change(function (data) {
table.find('tbody').each(function () {
$(this).empty();
// insert items
var keys = Object.keys(data);
keys.sort();
for (var i in keys) {
var id = keys[i];
var val = data[id];
$('<tr id=proj_' + id + '>' +
'<td><input type="checkbox"></td>' +
'<td>' + id + '</td>' +
'<td class="projIdName">' + val['name'] + '</td>' +
'<td><input id="delID" type="button" value="Del"/></td></tr>').appendTo($(this));
}
$("input[name='txtReleaseReleatedProject']").keyup();
});
}).load();
table.find('tbody').delegate('input#delID', 'click', function () {
var id = $(this).parent().prev().prev().text();
console.log('Delete: ' + id);
$('#proj_' + id + ' :checkbox', table).attr('checked', false).click();
idmgr.remove(id);
});
table.delegate(':checkbox', 'click', function() {
var ids = [];
table.find('tbody input:checked').parent().next().each(function() {
ids.push($(this).text());
});
$("input[name='txtReleaseReleatedProject']").val(ids.join(','));
updateCheckBoxes();
}).delegate('td.projIdName', 'click', function () {
if ($(this).find('input').length) return;
var oldval = $(this).text();
$(this).addClass('cellEditing');
$(this).empty();
var input = $('<input type="text"/>').val(oldval);
input.appendTo($(this)).focus().keypress(function (e) {
if (e.which == 13) {
// enter
var id = $(this).parent().prev().text();
var newval = $(this).val();
$(this).parent().text(newval);
idmgr.set(id, newval);
}
}).blur(function () {
$(this).parent().text(oldval);
}).css({
width: '100%',
});
}).find('input#selectAllIDs').click(function () {
table.find(':checkbox').attr('checked', this.checked);
})
var addButton = $('input#addID', table)
table.find('tfoot').delegate('input#inputID', 'keyup', function (e) {
if (e.which == 13 && !addButton.attr('disabled')) {
addButton.click();
return;
}
idmgr.check(this.value, function (name) {
table.find('td#inputIDtxt').html(name || '<font color="#FF0000">[Invalid project id]</font>');
addButton.attr('disabled', name == undefined)
});
}).delegate('input#addID', 'click', function () {
var id = table.find('input#inputID').val();
var name = table.find('td#inputIDtxt').text();
console.log('Add: ' + id +', ' + name);
idmgr.add(id, name);
}).delegate('input#flushIDs', 'click', function () {
if (confirm('Delete all project IDs?')) {
idmgr.clear();
}
});
});
// notes
$('textarea#txtNotes').css({
width: '100%',
height: '80px',
});
}
}, true);