Created
February 21, 2011 17:31
-
-
Save gifnksm/837380 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name Titech Portal Auto Login | |
// @namespace http://www.titech.ac.jp/ | |
// @id [email protected] | |
// @description 東工大ポータルに自動的にログインします | |
// @author NAKASHIMA, Makoto <[email protected]> | |
// @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