Skip to content

Instantly share code, notes, and snippets.

@gifnksm
Created February 21, 2011 17:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save gifnksm/837380 to your computer and use it in GitHub Desktop.
Save gifnksm/837380 to your computer and use it in GitHub Desktop.
// ==UserScript==
// @name Titech Portal Auto Login
// @namespace http://www.titech.ac.jp/
// @id titech-portal-auto-login@www.titech.ac.jp
// @description 東工大ポータルに自動的にログインします
// @author NAKASHIMA, Makoto <makoto.nksm@gmail.com>
// @include https://portal1.nap.gsic.titech.ac.jp/*
// @include https://portal.nap.gsic.titech.ac.jp/*
// @include http://portal.titech.ac.jp/*
// @jsversion 1.8
// @license MIT License
// @noframes
// ==/UserScript==
// {{{ Configure
const MATRIX_WIDTH = 10;
const MATRIX_HEIGHT = 7;
const MATRIX_ROW_LABELS = "1234567".split("");
const MATRIX_COL_LABELS = "ABCDEFGHIJ".split("");
const MATRIX_COORD_REG = /\[([A-J]),([1-7])\]/;
// }}}
// {{{ Detect current page type
const PageType = { START: 0, PASSWORD: 1, MATRIX: 2, UNKNOWN: 3 };
const CurrentPageType = (function() {
switch (location.pathname) {
case "/":
case "/portal.pl":
return PageType.START;
case "/GetAccess/Login":
if (location.search.indexOf("Template=userpass_key") !== -1)
return PageType.PASSWORD;
if (location.search.indexOf("Template=idg_key") !== -1)
return PageType.MATRIX;
// fall through
}
return PageType.UNKNOWN;
})();
// }}}
// {{{ IDs and ID convertes
const NAME_PREFIX = "GM_titech_portal_auto_login_";
const TABLE_ID = NAME_PREFIX + "table_id";
const CELL_ID_PREFIX = NAME_PREFIX + "cell";
function cellId2index(id) {
let [, row, col] = id.match(new RegExp(CELL_ID_PREFIX + "(\\d+)_(\\d+)"));
row = ~~row; col = ~~col;
return [row, col];
}
function index2cellId(row, col) {
return CELL_ID_PREFIX + row + "_" + col;
}
// }}}
// {{{ Utilities
String.prototype.repeat = function(n, sep) {
if (sep === undefined || sep === "")
return Array(n+1).join(this);
return Array(n).join(this + sep) + this;
};
function xml2dom(xml, xmlns, doc) {
if (xmlns === undefined)
xmlns = "http://www.w3.org/1999/xhtml";
if (doc === undefined)
doc = document;
let xmlString = '<root xmlns="' + xmlns + '">' +
xml.toXMLString() + "</root>";
let newDoc = new DOMParser().parseFromString(xmlString, "application/xml");
let imported = document.importNode(newDoc.documentElement, true);
let range = document.createRange();
range.selectNodeContents(imported);
let fragment = range.extractContents();
range.detach();
if (fragment.childNodes.length > 1)
return fragment;
return fragment.firstChild;
}
// }}}
// {{{ MatrixCode
const MatrixCode = {
isValidMatrix: function(mat) {
let rows = String(mat).split(/,/);
return rows.length === MATRIX_HEIGHT &&
rows.every(MatrixCode.isValidRow);
},
isValidRow: function(row) {
return row.length === MATRIX_WIDTH &&
Array.every(row, MatrixCode.isValidCell);
},
isValidCell: function(cell) /^[a-zA-Z_]$/.test(cell),
isValidIndex: function(row, col) 0 <= row && row < MATRIX_HEIGHT &&
0 <= col && col < MATRIX_WIDTH,
_assertMatrix: function(mat) {
if (!this.isValidMatrix(mat))
throw Error("Invalid Matrix Code [" + mat + "]");
},
_assertCell: function(cell) {
if (!this.isValidCell(cell))
throw Error("Invalid Cell Value [" + cell + "]");
},
_assertIndex: function(row, col) {
if (!this.isValidIndex(row, col))
throw Error("Invalid Index [" + row + "," + col + "]");
},
_value2array: function(value) value.split(",").map(
function(row) row.split("")),
_array2value: function(array) array.map(function(row) row.join("")).join(","),
__array: null,
get _array() {
if (this.__array === null)
this.__array = this._value2array(this.value);
return this.__array;
},
set _array(array) this.__array = array,
_key: "matrix_code",
_defaultValue: "_".repeat(MATRIX_WIDTH).repeat(MATRIX_HEIGHT, ","),
_value: null,
_restoreValue: function() {
this._value = GM_getValue(this._key, this._defaultValue);
},
_saveValue: function() {
GM_setValue(this._key, this._value);
},
get value() {
if (this._value === null)
this._restoreValue();
return this._value;
},
set value(value) {
this._assertMatrix(value);
this._value = value;
this._array = null;
this._saveValue();
return this._value;
},
getCell: function(row, col) {
this._assertIndex(row, col);
return this._array[row][col];
},
setCell: function(row, col, value) {
this._assertIndex(row, col);
this._assertCell(value);
this._array[row][col] = value.toUpperCase();
this._value = this._array2value(this._array);
this._saveValue();
return this._array[row][col];
}
};
// }}}
// {{{ MatrixTable
const MatrixTable = {
_parent: null,
get parent() this._parent,
set parent(value) {
this._parent = value;
if (this._parent !== null && this._table !== null)
this._parent.appendChild(this._table);
},
_table: null,
_createTable: function() {
let table = <table id={TABLE_ID} style="display: none;">
<tbody><tr><th/></tr></tbody>
</table>;
MATRIX_COL_LABELS.forEach(
function(l) table.tbody.tr.appendChild(<th>{l}</th>));
for (let row = 0; row < MATRIX_HEIGHT; row++) {
let tr = <tr class={row % 2 == 0 ? "even" : "odd"}/>;
tr.appendChild(<th>{MATRIX_ROW_LABELS[row]}</th>);
for (let col = 0; col < MATRIX_WIDTH; col++) {
let value = MatrixCode.getCell(row, col);
tr.appendChild(
<td><input value={value} id={index2cellId(row, col)}/></td>);
}
table.tbody.appendChild(tr);
}
this._initStyle();
this._table = xml2dom(table);
let self = this;
Array.forEach(
this._table.querySelectorAll('td input[id^="' + CELL_ID_PREFIX + '"]'),
function(input) input.addEventListener("keyup", self, false));
if (this.parent !== null)
this.parent.appendChild(this._table);
},
_initStyle: function() {
GM_addStyle(<><![CDATA[
#TABLE_ID { margin: 0.5em; }
#TABLE_ID input,
#TABLE_ID table { font-size: 100%; }
#TABLE_ID table td { padding: 0; }
#TABLE_ID input {
width: 1.5em; height: 1.5em; border: none;
text-align: center; font-family: inherit;
background-color: transparent;
}
#TABLE_ID input:focus { outline: 2px solid #3d80df; }
#TABLE_ID input.error { outline: 2px solid red!important; }
#TABLE_ID tr.even { background-color: #ffe8e8; }
]]></>.toString().replace(/#TABLE_ID/g, "#" + TABLE_ID));
},
handleEvent: function({type, target: input, keyCode}) {
if (type !== "keyup")
return;
let [row, col] = cellId2index(input.id);
if (input.value === "") {
input.classList.remove("error");
return;
}
try {
MatrixCode.setCell(row, col, input.value);
} catch (err) {
input.classList.add("error");
return;
}
input.classList.remove("error");
input.value = MatrixCode.getCell(row, col);
if (keyCode !== KEY_EVENT.DOM_VK_ENTER &&
keyCode !== KEY_EVENT.DOM_VK_RETURN &&
String.fromCharCode(keyCode).toUpperCase() != input.value)
return;
let nextCol = (col + 1) % MATRIX_WIDTH;
let nextRow = (nextCol === 0) ? (row + 1) % MATRIX_HEIGHT : row;
let nextInput = document.getElementById(index2cellId(nextRow, nextCol));
setTimeout(function() { nextInput.focus(); }, 10);
},
_visible: false,
show: function() {
this._visible = true;
if (this._table === null)
this._createTable();
this._table.style.display = '';
},
hide: function() {
this._visible = false;
if (this._table == null) return;
this._table.style.display = 'none';
},
toggle: function() this._visible ? this.hide() : this.show()
};
// }}}
// {{{ Initializers
function initStartPage() {
let form = xml2dom(
<form name="matrix" method="post" action="#">
<input type="submit" value="マトリクスコードの登録" name="OK"/>
<span>ログイン用マトリクスコードを登録します</span>
</form>);
MatrixTable.parent = form;
form.addEventListener("submit", function(e) {
e.preventDefault();
MatrixTable.toggle();
}, false);
document.getElementById("portal-form").appendChild(form);
}
function initPasswordPage() {
document.querySelector('form[name="login"]').removeAttribute("autocomplete");
}
function initMatrixPage() {
let inputCount = 0;
Array.forEach(
document.querySelectorAll('input[name^="message"]'),
function(input) {
if (!MATRIX_COORD_REG.test(input.parentNode.parentNode.innerHTML))
return;
let col = RegExp.$1.charCodeAt(0) - "A".charCodeAt(0);
let row = RegExp.$2 - 1;
let code = MatrixCode.getCell(row, col);
if (code == "_")
return;
input.value = code;
inputCount++;
});
if (document.documentElement.innerHTML.indexOf(
'マトリクス認証に失敗しました。') !== -1 || inputCount < 3)
return;
document.querySelector('form[name="login"]').submit();
}
// }}}
switch (CurrentPageType) {
case PageType.START: initStartPage(); break;
case PageType.PASSWORD: initPasswordPage(); break;
case PageType.MATRIX: initMatrixPage(); break;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment