Skip to content

Instantly share code, notes, and snippets.

@siberex
Last active May 28, 2020 15:13
Show Gist options
  • Save siberex/9089425 to your computer and use it in GitHub Desktop.
Save siberex/9089425 to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset=utf-8>
<title>Javascript Base64 encoding/decoding</title>
</head>
<body>
<script id="myBase64">
var myBase64encode = function(input) {
var result = '', binData, i;
var base64Alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='.split(''); // Base is 65 in fact :-)
if (typeof input === 'string') for (i = 0, input = input.split(''); i < input.length; i++) input[i] = input[i].charCodeAt(0);
// Extreme optimization. Something like black magic.
// Risk of breaking the brain :-)
for (i = 0; i < input.length; i += 3) {
// Warning, bitwise operations! :-)
// Grabbing three bytes (octets in binary):
binData = (input[i] & 0xFF) << 16 | // FF.00.00
(input[i + 1] & 0xFF) << 8 | // 00.FF.00
(input[i + 2] & 0xFF); // 00.00.FF
// And converting them to four base64 "sixtets" (letters):
result += base64Alphabet[(binData & 0xFC0000) >>> 18] + //11111100.00000000.00000000 = 0xFC0000 = 16515072
base64Alphabet[(binData & 0x03F000) >>> 12] + //00000011.11110000.00000000 = 0x03F000 = 258048
base64Alphabet[( i + 3 >= input.length && (input.length << 1) % 3 === 2 ? 64 :
(binData & 0x000FC0) >>> 6 )] + //00000000.00001111.11000000 = 0x000FC0 = 4032
base64Alphabet[( i + 3 >= input.length && (input.length << 1) % 3 ? 64 :
binData & 0x00003F )]; //00000000.00000000.00111111 = 0x00003F = 63
// If we haven't last byte, or two (for complete three octets),
// we place '=' [61] letter (or two) at the end.
}
return result;
} // base64
var sniperBase64decode = function(s) {
var e={},i,k,v=[],r='',w=String.fromCharCode;
var n=[[65,91],[97,123],[48,58],[43,44],[47,48]];
for(z in n){for(i=n[z][0];i<n[z][1];i++){v.push(w(i));}}
for(i=0;i<64;i++){e[v[i]]=i;}
for(i=0;i<s.length;i+=72){
var b=0,c,x,l=0,o=s.substring(i,i+72);
for(x=0;x<o.length;x++){
c=e[o.charAt(x)];b=(b<<6)+c;l+=6;
while(l>=8){r+=w((b>>>(l-=8))%256);}
}
}
return r;
}
var brocBase64decode = function(s) {
var e={},i,b=0,c,x,l=0,a,r='',w=String.fromCharCode,L=s.length;
var A="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
for(i=0;i<64;i++){e[A.charAt(i)]=i;}
for(x=0;x<L;x++){
c=e[s.charAt(x)];b=(b<<6)+c;l+=6;
while(l>=8){((a=(b>>>(l-=8))&0xff)||(x<(L-2)))&&(r+=w(a));}
}
return r;
};
</script>
<script id="B64">
/*
Copyright Vassilis Petroulias [DRDigit]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
var B64 = {
alphabet: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=',
lookup: null,
ie: /MSIE /.test(navigator.userAgent),
ieo: /MSIE [67]/.test(navigator.userAgent),
encode: function (s) {
var buffer = B64.toUtf8(s),
position = -1,
len = buffer.length,
nan0, nan1, nan2, enc = [, , , ];
if (B64.ie) {
var result = [];
while (++position < len) {
nan0 = buffer[position];
nan1 = buffer[++position];
enc[0] = nan0 >> 2;
enc[1] = ((nan0 & 3) << 4) | (nan1 >> 4);
if (isNaN(nan1))
enc[2] = enc[3] = 64;
else {
nan2 = buffer[++position];
enc[2] = ((nan1 & 15) << 2) | (nan2 >> 6);
enc[3] = (isNaN(nan2)) ? 64 : nan2 & 63;
}
result.push(B64.alphabet.charAt(enc[0]), B64.alphabet.charAt(enc[1]), B64.alphabet.charAt(enc[2]), B64.alphabet.charAt(enc[3]));
}
return result.join('');
} else {
var result = '';
while (++position < len) {
nan0 = buffer[position];
nan1 = buffer[++position];
enc[0] = nan0 >> 2;
enc[1] = ((nan0 & 3) << 4) | (nan1 >> 4);
if (isNaN(nan1))
enc[2] = enc[3] = 64;
else {
nan2 = buffer[++position];
enc[2] = ((nan1 & 15) << 2) | (nan2 >> 6);
enc[3] = (isNaN(nan2)) ? 64 : nan2 & 63;
}
result += B64.alphabet[enc[0]] + B64.alphabet[enc[1]] + B64.alphabet[enc[2]] + B64.alphabet[enc[3]];
}
return result;
}
},
decode: function (s) {
if (s.length % 4)
throw new Error("InvalidCharacterError: 'B64.decode' failed: The string to be decoded is not correctly encoded.");
var buffer = B64.fromUtf8(s),
position = 0,
len = buffer.length;
if (B64.ieo) {
var result = [];
while (position < len) {
if (buffer[position] < 128)
result.push(String.fromCharCode(buffer[position++]));
else if (buffer[position] > 191 && buffer[position] < 224)
result.push(String.fromCharCode(((buffer[position++] & 31) << 6) | (buffer[position++] & 63)));
else
result.push(String.fromCharCode(((buffer[position++] & 15) << 12) | ((buffer[position++] & 63) << 6) | (buffer[position++] & 63)));
}
return result.join('');
} else {
var result = '';
while (position < len) {
if (buffer[position] < 128)
result += String.fromCharCode(buffer[position++]);
else if (buffer[position] > 191 && buffer[position] < 224)
result += String.fromCharCode(((buffer[position++] & 31) << 6) | (buffer[position++] & 63));
else
result += String.fromCharCode(((buffer[position++] & 15) << 12) | ((buffer[position++] & 63) << 6) | (buffer[position++] & 63));
}
return result;
}
},
toUtf8: function (s) {
var position = -1,
len = s.length,
chr, buffer = [];
if (/^[\x00-\x7f]*$/.test(s)) while (++position < len)
buffer.push(s.charCodeAt(position));
else while (++position < len) {
chr = s.charCodeAt(position);
if (chr < 128)
buffer.push(chr);
else if (chr < 2048)
buffer.push((chr >> 6) | 192, (chr & 63) | 128);
else
buffer.push((chr >> 12) | 224, ((chr >> 6) & 63) | 128, (chr & 63) | 128);
}
return buffer;
},
fromUtf8: function (s) {
var position = -1,
len, buffer = [],
enc = [, , , ];
if (!B64.lookup) {
len = B64.alphabet.length;
B64.lookup = {};
while (++position < len)
B64.lookup[B64.alphabet.charAt(position)] = position;
position = -1;
}
len = s.length;
while (++position < len) {
enc[0] = B64.lookup[s.charAt(position)];
enc[1] = B64.lookup[s.charAt(++position)];
buffer.push((enc[0] << 2) | (enc[1] >> 4));
enc[2] = B64.lookup[s.charAt(++position)];
if (enc[2] == 64)
break;
buffer.push(((enc[1] & 15) << 4) | (enc[2] >> 2));
enc[3] = B64.lookup[s.charAt(++position)];
if (enc[3] == 64)
break;
buffer.push(((enc[2] & 3) << 6) | enc[3]);
}
return buffer;
}
};
</script>
<script id=Base64>
/*
Copyright (c) 2008 Fred Palmer fred.palmer_at_gmail.com
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
*/
function StringBuffer()
{
this.buffer = [];
}
StringBuffer.prototype.append = function append(string)
{
this.buffer.push(string);
return this;
};
StringBuffer.prototype.toString = function toString()
{
return this.buffer.join("");
};
var Base64 =
{
codex : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
encode : function (input)
{
var output = new StringBuffer();
var enumerator = new Utf8EncodeEnumerator(input);
while (enumerator.moveNext())
{
var chr1 = enumerator.current;
enumerator.moveNext();
var chr2 = enumerator.current;
enumerator.moveNext();
var chr3 = enumerator.current;
var enc1 = chr1 >> 2;
var enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
var enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
var enc4 = chr3 & 63;
if (isNaN(chr2))
{
enc3 = enc4 = 64;
}
else if (isNaN(chr3))
{
enc4 = 64;
}
output.append(this.codex.charAt(enc1) + this.codex.charAt(enc2) + this.codex.charAt(enc3) + this.codex.charAt(enc4));
}
return output.toString();
},
decode : function (input)
{
var output = new StringBuffer();
var enumerator = new Base64DecodeEnumerator(input);
while (enumerator.moveNext())
{
var charCode = enumerator.current;
if (charCode < 128)
output.append(String.fromCharCode(charCode));
else if ((charCode > 191) && (charCode < 224))
{
enumerator.moveNext();
var charCode2 = enumerator.current;
output.append(String.fromCharCode(((charCode & 31) << 6) | (charCode2 & 63)));
}
else
{
enumerator.moveNext();
var charCode2 = enumerator.current;
enumerator.moveNext();
var charCode3 = enumerator.current;
output.append(String.fromCharCode(((charCode & 15) << 12) | ((charCode2 & 63) << 6) | (charCode3 & 63)));
}
}
return output.toString();
}
}
function Utf8EncodeEnumerator(input)
{
this._input = input;
this._index = -1;
this._buffer = [];
}
Utf8EncodeEnumerator.prototype =
{
current: Number.NaN,
moveNext: function()
{
if (this._buffer.length > 0)
{
this.current = this._buffer.shift();
return true;
}
else if (this._index >= (this._input.length - 1))
{
this.current = Number.NaN;
return false;
}
else
{
var charCode = this._input.charCodeAt(++this._index);
// "\r\n" -> "\n"
//
if ((charCode == 13) && (this._input.charCodeAt(this._index + 1) == 10))
{
charCode = 10;
this._index += 2;
}
if (charCode < 128)
{
this.current = charCode;
}
else if ((charCode > 127) && (charCode < 2048))
{
this.current = (charCode >> 6) | 192;
this._buffer.push((charCode & 63) | 128);
}
else
{
this.current = (charCode >> 12) | 224;
this._buffer.push(((charCode >> 6) & 63) | 128);
this._buffer.push((charCode & 63) | 128);
}
return true;
}
}
}
function Base64DecodeEnumerator(input)
{
this._input = input;
this._index = -1;
this._buffer = [];
}
Base64DecodeEnumerator.prototype =
{
current: 64,
moveNext: function()
{
if (this._buffer.length > 0)
{
this.current = this._buffer.shift();
return true;
}
else if (this._index >= (this._input.length - 1))
{
this.current = 64;
return false;
}
else
{
var enc1 = Base64.codex.indexOf(this._input.charAt(++this._index));
var enc2 = Base64.codex.indexOf(this._input.charAt(++this._index));
var enc3 = Base64.codex.indexOf(this._input.charAt(++this._index));
var enc4 = Base64.codex.indexOf(this._input.charAt(++this._index));
var chr1 = (enc1 << 2) | (enc2 >> 4);
var chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
var chr3 = ((enc3 & 3) << 6) | enc4;
this.current = chr1;
if (enc3 != 64)
this._buffer.push(chr2);
if (enc4 != 64)
this._buffer.push(chr3);
return true;
}
}
};
</script>
<script id=JSLitmus>
// JSLitmus.js
//
// Copyright (c) 2010, Robert Kieffer, http://broofa.com
// Available under MIT license (http://en.wikipedia.org/wiki/MIT_License)
(function() {
// Private methods and state
// Get platform info but don't go crazy trying to recognize everything
// that's out there. This is just for the major platforms and OSes.
var platform = 'unknown platform', ua = navigator.userAgent;
// Detect OS
var oses = ['Windows','iPhone OS','(Intel |PPC )?Mac OS X','Linux'].join('|');
var pOS = new RegExp('((' + oses + ') [^ \);]*)').test(ua) ? RegExp.$1 : null;
if (!pOS) pOS = new RegExp('((' + oses + ')[^ \);]*)').test(ua) ? RegExp.$1 : null;
// Detect browser
var pName = /(Chrome|MSIE|Safari|Opera|Firefox)/.test(ua) ? RegExp.$1 : null;
// Detect version
var vre = new RegExp('(Version|' + pName + ')[ \/]([^ ;]*)');
var pVersion = (pName && vre.test(ua)) ? RegExp.$2 : null;
var platform = (pOS && pName && pVersion) ? pName + ' ' + pVersion + ' on ' + pOS : 'unknown platform';
/**
* A smattering of methods that are needed to implement the JSLitmus testbed.
*/
var jsl = {
/**
* Enhanced version of escape()
*/
escape: function(s) {
s = s.replace(/,/g, '\\,');
s = escape(s);
s = s.replace(/\+/g, '%2b');
s = s.replace(/ /g, '+');
return s;
},
/**
* Get an element by ID.
*/
$: function(id) {
return document.getElementById(id);
},
/**
* Null function
*/
F: function() {},
/**
* Set the status shown in the UI
*/
status: function(msg) {
var el = jsl.$('jsl_status');
if (el) el.innerHTML = msg || '';
},
/**
* Convert a number to an abbreviated string like, "15K" or "10M"
*/
toLabel: function(n) {
if (n == Infinity) {
return 'Infinity';
} else if (n > 1e9) {
n = Math.round(n/1e8);
return n/10 + 'B';
} else if (n > 1e6) {
n = Math.round(n/1e5);
return n/10 + 'M';
} else if (n > 1e3) {
n = Math.round(n/1e2);
return n/10 + 'K';
}
return n;
},
/**
* Copy properties from src to dst
*/
extend: function(dst, src) {
for (var k in src) dst[k] = src[k]; return dst;
},
/**
* Like Array.join(), but for the key-value pairs in an object
*/
join: function(o, delimit1, delimit2) {
if (o.join) return o.join(delimit1); // If it's an array
var pairs = [];
for (var k in o) pairs.push(k + delimit1 + o[k]);
return pairs.join(delimit2);
},
/**
* Array#indexOf isn't supported in IE, so we use this as a cross-browser solution
*/
indexOf: function(arr, o) {
if (arr.indexOf) return arr.indexOf(o);
for (var i = 0; i < this.length; i++) if (arr[i] === o) return i;
return -1;
}
};
/**
* Test manages a single test (created with
* JSLitmus.test())
*
* @private
*/
var Test = function (name, f) {
if (!f) throw new Error('Undefined test function');
if (!/function[^\(]*\(([^,\)]*)/.test(f.toString())) {
throw new Error('"' + name + '" test: Test is not a valid Function object');
}
this.loopArg = RegExp.$1;
this.name = name;
this.f = f;
};
jsl.extend(Test, /** @lends Test */ {
/** Calibration tests for establishing iteration loop overhead */
CALIBRATIONS: [
new Test('calibrating loop', function(count) {while (count--);}),
new Test('calibrating function', jsl.F)
],
/**
* Run calibration tests. Returns true if calibrations are not yet
* complete (in which case calling code should run the tests yet again).
* onCalibrated - Callback to invoke when calibrations have finished
*/
calibrate: function(onCalibrated) {
for (var i = 0; i < Test.CALIBRATIONS.length; i++) {
var cal = Test.CALIBRATIONS[i];
if (cal.running) return true;
if (!cal.count) {
cal.isCalibration = true;
cal.onStop = onCalibrated;
//cal.MIN_TIME = .1; // Do calibrations quickly
cal.run(2e4);
return true;
}
}
return false;
}
});
jsl.extend(Test.prototype, {/** @lends Test.prototype */
/** Initial number of iterations */
INIT_COUNT: 10,
/** Max iterations allowed (i.e. used to detect bad looping functions) */
MAX_COUNT: 1e9,
/** Minimum time a test should take to get valid results (secs) */
MIN_TIME: .5,
/** Callback invoked when test state changes */
onChange: jsl.F,
/** Callback invoked when test is finished */
onStop: jsl.F,
/**
* Reset test state
*/
reset: function() {
delete this.count;
delete this.time;
delete this.running;
delete this.error;
},
/**
* Run the test (in a timeout). We use a timeout to make sure the browser
* has a chance to finish rendering any UI changes we've made, like
* updating the status message.
*/
run: function(count) {
count = count || this.INIT_COUNT;
jsl.status(this.name + ' x ' + count);
this.running = true;
var me = this;
setTimeout(function() {me._run(count);}, 200);
},
/**
* The nuts and bolts code that actually runs a test
*/
_run: function(count) {
var me = this;
// Make sure calibration tests have run
if (!me.isCalibration && Test.calibrate(function() {me.run(count);})) return;
this.error = null;
try {
var start, f = this.f, now, i = count;
// Start the timer
start = new Date();
// Now for the money shot. If this is a looping function ...
if (this.loopArg) {
// ... let it do the iteration itself
f(count);
} else {
// ... otherwise do the iteration for it
while (i--) f();
}
// Get time test took (in secs)
this.time = Math.max(1,new Date() - start)/1000;
// Store iteration count and per-operation time taken
this.count = count;
this.period = this.time/count;
// Do we need to do another run?
this.running = this.time <= this.MIN_TIME;
// ... if so, compute how many times we should iterate
if (this.running) {
// Bump the count to the nearest power of 2
var x = this.MIN_TIME/this.time;
var pow = Math.pow(2, Math.max(1, Math.ceil(Math.log(x)/Math.log(2))));
count *= pow;
if (count > this.MAX_COUNT) {
throw new Error('Max count exceeded. If this test uses a looping function, make sure the iteration loop is working properly.');
}
}
} catch (e) {
// Exceptions are caught and displayed in the test UI
this.reset();
this.error = e;
}
// Figure out what to do next
if (this.running) {
me.run(count);
} else {
jsl.status('');
me.onStop(me);
}
// Finish up
this.onChange(this);
},
/**
* Get the number of operations per second for this test.
*
* @param normalize if true, iteration loop overhead taken into account
*/
getHz: function(/**Boolean*/ normalize) {
var p = this.period;
// Adjust period based on the calibration test time
if (normalize && !this.isCalibration) {
var cal = Test.CALIBRATIONS[this.loopArg ? 0 : 1];
// If the period is within 20% of the calibration time, then zero the
// it out
p = p < cal.period*1.2 ? 0 : p - cal.period;
}
return Math.round(1/p);
},
/**
* Get a friendly string describing the test
*/
toString: function() {
return this.name + ' - ' + this.time/this.count + ' secs';
}
});
// CSS we need for the UI
var STYLESHEET = '<style> \
#jslitmus {font-family:sans-serif; font-size: 12px;} \
#jslitmus a {text-decoration: none;} \
#jslitmus a:hover {text-decoration: underline;} \
#jsl_status { \
margin-top: 10px; \
font-size: 10px; \
color: #888; \
} \
A IMG {border:none} \
#test_results { \
margin-top: 10px; \
font-size: 12px; \
font-family: sans-serif; \
border-collapse: collapse; \
border-spacing: 0px; \
} \
#test_results th, #test_results td { \
border: solid 1px #ccc; \
vertical-align: top; \
padding: 3px; \
} \
#test_results th { \
vertical-align: bottom; \
background-color: #ccc; \
padding: 1px; \
font-size: 10px; \
} \
#test_results #test_platform { \
color: #444; \
text-align:center; \
} \
#test_results .test_row { \
color: #006; \
cursor: pointer; \
} \
#test_results .test_nonlooping { \
border-left-style: dotted; \
border-left-width: 2px; \
} \
#test_results .test_looping { \
border-left-style: solid; \
border-left-width: 2px; \
} \
#test_results .test_name {white-space: nowrap;} \
#test_results .test_pending { \
} \
#test_results .test_running { \
font-style: italic; \
} \
#test_results .test_done {} \
#test_results .test_done { \
text-align: right; \
font-family: monospace; \
} \
#test_results .test_error {color: #600;} \
#test_results .test_error .error_head {font-weight:bold;} \
#test_results .test_error .error_body {font-size:85%;} \
#test_results .test_row:hover td { \
background-color: #ffc; \
text-decoration: underline; \
} \
#chart { \
margin: 10px 0px; \
width: 250px; \
} \
#chart img { \
border: solid 1px #ccc; \
margin-bottom: 5px; \
} \
#chart #tiny_url { \
height: 40px; \
width: 250px; \
} \
#jslitmus_credit { \
font-size: 10px; \
color: #888; \
margin-top: 8px; \
} \
</style>';
// HTML markup for the UI
var MARKUP = '<div id="jslitmus"> \
<button onclick="JSLitmus.runAll(event)">Run Tests</button> \
<button id="stop_button" disabled="disabled" onclick="JSLitmus.stop()">Stop Tests</button> \
<br \> \
<br \> \
<input type="checkbox" style="vertical-align: middle" id="test_normalize" checked="checked" onchange="JSLitmus.renderAll()""> Normalize results \
<table id="test_results"> \
<colgroup> \
<col /> \
<col width="100" /> \
</colgroup> \
<tr><th id="test_platform" colspan="2">' + platform + '</th></tr> \
<tr><th>Test</th><th>Ops/sec</th></tr> \
<tr id="test_row_template" class="test_row" style="display:none"> \
<td class="test_name"></td> \
<td class="test_result">Ready</td> \
</tr> \
</table> \
<div id="jsl_status"></div> \
<div id="chart" style="display:none"> \
<a id="chart_link" target="_blank"><img id="chart_image"></a> \
TinyURL (for chart): \
<iframe id="tiny_url" frameBorder="0" scrolling="no" src=""></iframe> \
</div> \
<a id="jslitmus_credit" title="JSLitmus home page" href="http://code.google.com/p/jslitmus" target="_blank">Powered by JSLitmus</a> \
</div>';
/**
* The public API for creating and running tests
*/
window.JSLitmus = {
/** The list of all tests that have been registered with JSLitmus.test */
_tests: [],
/** The queue of tests that need to be run */
_queue: [],
/**
* The parsed query parameters the current page URL. This is provided as a
* convenience for test functions - it's not used by JSLitmus proper
*/
params: {},
/**
* Initialize
*/
_init: function() {
// Parse query params into JSLitmus.params[] hash
var match = (location + '').match(/([^?#]*)(#.*)?$/);
if (match) {
var pairs = match[1].split('&');
for (var i = 0; i < pairs.length; i++) {
var pair = pairs[i].split('=');
if (pair.length > 1) {
var key = pair.shift();
var value = pair.length > 1 ? pair.join('=') : pair[0];
this.params[key] = value;
}
}
}
// Write out the stylesheet. We have to do this here because IE
// doesn't honor sheets written after the document has loaded.
document.write(STYLESHEET);
// Setup the rest of the UI once the document is loaded
if (window.addEventListener) {
window.addEventListener('load', this._setup, false);
} else if (document.addEventListener) {
document.addEventListener('load', this._setup, false);
} else if (window.attachEvent) {
window.attachEvent('onload', this._setup);
}
return this;
},
/**
* Set up the UI
*/
_setup: function() {
var el = jsl.$('jslitmus_container');
if (!el) document.body.appendChild(el = document.createElement('div'));
el.innerHTML = MARKUP;
// Render the UI for all our tests
for (var i=0; i < JSLitmus._tests.length; i++)
JSLitmus.renderTest(JSLitmus._tests[i]);
},
/**
* (Re)render all the test results
*/
renderAll: function() {
for (var i = 0; i < JSLitmus._tests.length; i++)
JSLitmus.renderTest(JSLitmus._tests[i]);
JSLitmus.renderChart();
},
/**
* (Re)render the chart graphics
*/
renderChart: function() {
var url = JSLitmus.chartUrl();
jsl.$('chart_link').href = url;
jsl.$('chart_image').src = url;
jsl.$('chart').style.display = '';
// Update the tiny URL
jsl.$('tiny_url').src = 'http://tinyurl.com/api-create.php?url='+escape(url);
},
/**
* (Re)render the results for a specific test
*/
renderTest: function(test) {
// Make a new row if needed
if (!test._row) {
var trow = jsl.$('test_row_template');
if (!trow) return;
test._row = trow.cloneNode(true);
test._row.style.display = '';
test._row.id = '';
test._row.onclick = function() {JSLitmus._queueTest(test);};
test._row.title = 'Run ' + test.name + ' test';
trow.parentNode.appendChild(test._row);
test._row.cells[0].innerHTML = test.name;
}
var cell = test._row.cells[1];
var cns = [test.loopArg ? 'test_looping' : 'test_nonlooping'];
if (test.error) {
cns.push('test_error');
cell.innerHTML =
'<div class="error_head">' + test.error + '</div>' +
'<ul class="error_body"><li>' +
jsl.join(test.error, ': ', '</li><li>') +
'</li></ul>';
} else {
if (test.running) {
cns.push('test_running');
cell.innerHTML = 'running';
} else if (jsl.indexOf(JSLitmus._queue, test) >= 0) {
cns.push('test_pending');
cell.innerHTML = 'pending';
} else if (test.count) {
cns.push('test_done');
var hz = test.getHz(jsl.$('test_normalize').checked);
cell.innerHTML = hz != Infinity ? hz : '&infin;';
cell.title = 'Looped ' + test.count + ' times in ' + test.time + ' seconds';
} else {
cell.innerHTML = 'ready';
}
}
cell.className = cns.join(' ');
},
/**
* Create a new test
*/
test: function(name, f) {
// Create the Test object
var test = new Test(name, f);
JSLitmus._tests.push(test);
// Re-render if the test state changes
test.onChange = JSLitmus.renderTest;
// Run the next test if this one finished
test.onStop = function(test) {
if (JSLitmus.onTestFinish) JSLitmus.onTestFinish(test);
JSLitmus.currentTest = null;
JSLitmus._nextTest();
};
// Render the new test
this.renderTest(test);
},
/**
* Add all tests to the run queue
*/
runAll: function(e) {
e = e || window.event;
var reverse = e && e.shiftKey, len = JSLitmus._tests.length;
for (var i = 0; i < len; i++) {
JSLitmus._queueTest(JSLitmus._tests[!reverse ? i : (len - i - 1)]);
}
},
/**
* Remove all tests from the run queue. The current test has to finish on
* it's own though
*/
stop: function() {
while (JSLitmus._queue.length) {
var test = JSLitmus._queue.shift();
JSLitmus.renderTest(test);
}
},
/**
* Run the next test in the run queue
*/
_nextTest: function() {
if (!JSLitmus.currentTest) {
var test = JSLitmus._queue.shift();
if (test) {
jsl.$('stop_button').disabled = false;
JSLitmus.currentTest = test;
test.run();
JSLitmus.renderTest(test);
if (JSLitmus.onTestStart) JSLitmus.onTestStart(test);
} else {
jsl.$('stop_button').disabled = true;
JSLitmus.renderChart();
}
}
},
/**
* Add a test to the run queue
*/
_queueTest: function(test) {
if (jsl.indexOf(JSLitmus._queue, test) >= 0) return;
JSLitmus._queue.push(test);
JSLitmus.renderTest(test);
JSLitmus._nextTest();
},
/**
* Generate a Google Chart URL that shows the data for all tests
*/
chartUrl: function() {
var n = JSLitmus._tests.length, markers = [], data = [];
var d, min = 0, max = -1e10;
var normalize = jsl.$('test_normalize').checked;
// Gather test data
for (var i=0; i < JSLitmus._tests.length; i++) {
var test = JSLitmus._tests[i];
if (test.count) {
var hz = test.getHz(normalize);
var v = hz != Infinity ? hz : 0;
data.push(v);
markers.push('t' + jsl.escape(test.name + '(' + jsl.toLabel(hz)+ ')') + ',000000,0,' +
markers.length + ',10');
max = Math.max(v, max);
}
}
if (markers.length <= 0) return null;
// Build chart title
var title = document.getElementsByTagName('title');
title = (title && title.length) ? title[0].innerHTML : null;
var chart_title = [];
if (title) chart_title.push(title);
chart_title.push('Ops/sec (' + platform + ')');
// Build labels
var labels = [jsl.toLabel(min), jsl.toLabel(max)];
var w = 250, bw = 15;
var bs = 5;
var h = markers.length*(bw + bs) + 30 + chart_title.length*20;
var params = {
chtt: escape(chart_title.join('|')),
chts: '000000,10',
cht: 'bhg', // chart type
chd: 't:' + data.join(','), // data set
chds: min + ',' + max, // max/min of data
chxt: 'x', // label axes
chxl: '0:|' + labels.join('|'), // labels
chsp: '0,1',
chm: markers.join('|'), // test names
chbh: [bw, 0, bs].join(','), // bar widths
// chf: 'bg,lg,0,eeeeee,0,eeeeee,.5,ffffff,1', // gradient
chs: w + 'x' + h
};
return 'http://chart.apis.google.com/chart?' + jsl.join(params, '=', '&');
}
};
JSLitmus._init();
})();
</script>
<script>
var ascii = 'JSLitmus has a "Normalize results" checkbox that is enabled by default. With this enabled, the iteration loop overhead is subtracted when tabulating the test results. Normalization may occasionally cause a test to display "oo" or "Infinity" operations per second. This occurs if a test has performance comparable to the empty tests used to calibrate the iteration loop overhead. This may also happen if your CPU was busy with another process when JSLitmus ran it\'s calibration tests. The tests take place immediately before the first of your tests is run.';
var utf8 = 'Детские деревни-SOS – уникальный проект, где сироты живут не в изоляции, а в настоящем доме, ходят в обычную школу и готовят завтрак по утрам. Выход в самостоятельную жизнь не становится для них шоком – а главное, здесь не бывает отказов. В комментариях к предыдущему посту мы увидели восхищённое восклицание: "как ваши мамы умеют оттаивать детей!" Поэтому в этом сообщении хотелось бы побольше рассказать о том, кто такие SOS-мамы и как ими становятся. Детские деревни-SOS – это не приёмные семьи, а мамы не являются приемными родителями. Юридически – это';
var encoded = '';
// tests for ascii input
JSLitmus.test('B64 encoding test (ascii input) ', function() { encoded = B64.encode(ascii); });
JSLitmus.test('Base64 encoding test (ascii input) ', function() { encoded = Base64.encode(ascii); });
JSLitmus.test('myBase64 encoding test (ascii input) ', function() { encoded = myBase64encode(ascii); });
JSLitmus.test('B64 decoding test (ascii input) ', function() { B64.decode(encoded); });
JSLitmus.test('Base64 decoding test (ascii input) ', function() { Base64.decode(encoded); });
JSLitmus.test('sniperBase64decode decoding test (ascii input) ', function() { sniperBase64decode(encoded); });
JSLitmus.test('brocBase64decode decoding test (ascii input) ', function() { brocBase64decode(encoded); });
// Too fast, too furious ;-)
// JSLitmus.test('window.btoa encoding test (ascii input) ', function() { encoded = window.btoa(ascii); });
// JSLitmus.test('window.atob decoding test (ascii input) ', function() { window.atob(encoded); });
// tests for utf8 input
JSLitmus.test('B64 encoding test (utf8 input) ', function() { encoded = B64.encode(utf8); });
JSLitmus.test('Base64 encoding test (utf8 input) ', function() { encoded = Base64.encode(utf8); });
JSLitmus.test('myBase64 encoding test (utf8 input) ', function() { encoded = myBase64encode(utf8); });
JSLitmus.test('B64 decoding test (utf8 input) ', function() { B64.decode(encoded); });
JSLitmus.test('Base64 decoding test (utf8 input) ', function() { Base64.decode(encoded); });
JSLitmus.test('sniperBase64decode decoding test (utf8 input) ', function() { sniperBase64decode(encoded); });
JSLitmus.test('brocBase64decode decoding test (utf8 input) ', function() { brocBase64decode(encoded); });
// UTF is NOT supported by internal btoa & atob functions!
// JSLitmus.test('window.btoa encoding test (utf8 input) ', function() { encoded = window.btoa(utf8); });
// JSLitmus.test('window.atob decoding test (utf8 input) ', function() { window.atob(encoded); });
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment