L.ui.view.extend({ PubkeyListValue: L.cbi.AbstractValue.extend({ base64Table: { 'A': 0, 'B': 1, 'C': 2, 'D': 3, 'E': 4, 'F': 5, 'G': 6, 'H': 7, 'I': 8, 'J': 9, 'K': 10, 'L': 11, 'M': 12, 'N': 13, 'O': 14, 'P': 15, 'Q': 16, 'R': 17, 'S': 18, 'T': 19, 'U': 20, 'V': 21, 'W': 22, 'X': 23, 'Y': 24, 'Z': 25, 'a': 26, 'b': 27, 'c': 28, 'd': 29, 'e': 30, 'f': 31, 'g': 32, 'h': 33, 'i': 34, 'j': 35, 'k': 36, 'l': 37, 'm': 38, 'n': 39, 'o': 40, 'p': 41, 'q': 42, 'r': 43, 's': 44, 't': 45, 'u': 46, 'v': 47, 'w': 48, 'x': 49, 'y': 50, 'z': 51, '0': 52, '1': 53, '2': 54, '3': 55, '4': 56, '5': 57, '6': 58, '7': 59, '8': 60, '9': 61, '+': 62, '/': 63, '=': 64 }, base64Decode: function(s) { var i = 0; var d = ''; if (s.match(/[^A-Za-z0-9\+\/\=]/)) return undefined; while (i < s.length) { var e1 = this.base64Table[s.charAt(i++)]; var e2 = this.base64Table[s.charAt(i++)]; var e3 = this.base64Table[s.charAt(i++)]; var e4 = this.base64Table[s.charAt(i++)]; var c1 = ( e1 << 2) | (e2 >> 4); var c2 = ((e2 & 15) << 4) | (e3 >> 2); var c3 = ((e3 & 3) << 6) | e4; d += String.fromCharCode(c1); if (e3 < 64) d += String.fromCharCode(c2); if (e4 < 64) d += String.fromCharCode(c3); } return d; }, lengthDecode: function(s, off) { var l = (s.charCodeAt(off++) << 24) | (s.charCodeAt(off++) << 16) | (s.charCodeAt(off++) << 8) | s.charCodeAt(off++); if (l < 0 || (off + l) > s.length) return -1; return l; }, pubkeyDecode: function(s) { var parts = s.split(/\s+/); if (parts.length < 2) return undefined; var key = this.base64Decode(parts[1]); if (!key) return undefined; var off, len; off = 0; len = this.lengthDecode(key, off); if (len < 0) return undefined; var type = key.substr(off + 4, len); if (type != parts[0]) return undefined; off += 4 + len; var len1 = this.lengthDecode(key, off); if (len1 < 0) return undefined; off += 4 + len1; var len2 = this.lengthDecode(key, off); if (len2 < 0) return undefined; if (len1 & 1) len1--; if (len2 & 1) len2--; switch (type) { case 'ssh-rsa': return { type: 'RSA', bits: len2 * 8, comment: parts[2] }; case 'ssh-dss': return { type: 'DSA', bits: len1 * 8, comment: parts[2] }; default: return undefined; } }, _remove: function(ev) { var self = ev.data.self; self._keys.splice(ev.data.index, 1); self._render(ev.data.div); }, _add: function(ev) { var self = ev.data.self; var form = $('
') .append($('

') .text(L.tr('Paste the public key line into the field below and press "%s" to continue.').format(L.tr('Ok')))) .append($('

') .text(L.tr('Unrecognized public key! Please add only RSA or DSA keys.')) .addClass('alert alert-danger') .hide()) .append($('

') .append($('') .attr('type', 'text') .attr('placeholder', L.tr('Paste key here')) .addClass('form-control'))); L.ui.dialog(L.tr('Add new public key'), form, { style: 'confirm', confirm: function() { var val = form.find('input').val(); if (!val) { return; } var key = self.pubkeyDecode(val); if (!key) { form.find('input').val(''); form.find('.alert').show(); return; } self._keys.push(val); self._render(ev.data.div); L.ui.dialog(false); } }); }, _show: function(ev) { var self = ev.data.self; L.ui.dialog( L.tr('Public key'), $('

').text(self._keys[ev.data.index]),
				{ style: 'close' }
			);
		},

		_render: function(div)
		{
			div.empty();

			for (var i = 0; i < this._keys.length; i++)
			{
				var k = this.pubkeyDecode(this._keys[i] || '');

				if (!k)
					continue;

				$('
') .addClass('input-group') .append($('') .addClass('form-control') .attr('type', 'text') .prop('readonly', true) .click({ self: this, index: i }, this._show) .val('%dBit %s - %s'.format(k.bits, k.type, k.comment || '?'))) .append($('') .addClass('input-group-btn') .append($('