├── public ├── favicon.ico ├── stylesheets │ └── style.css └── js │ ├── siphash.js │ ├── back.js │ ├── sync-client.js │ ├── md5.js │ ├── murmurhash3.js │ ├── bit-sync.js │ └── socket.io-1.4.5.js ├── views ├── error.ejs └── index.ejs ├── routes ├── users.js └── index.js ├── package.json ├── README.md ├── test.js ├── bin └── www ├── app.js └── LICENSE /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebDeltaSync/WebR2sync_plus/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /views/error.ejs: -------------------------------------------------------------------------------- 1 |

<%= message %>

2 |

<%= error.status %>

3 |
<%= error.stack %>
4 | -------------------------------------------------------------------------------- /public/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 50px; 3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; 4 | } 5 | 6 | a { 7 | color: #00B7FF; 8 | } 9 | -------------------------------------------------------------------------------- /routes/users.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | 4 | /* GET users listing. */ 5 | router.get('/', function(req, res, next) { 6 | res.send('respond with a resource'); 7 | }); 8 | 9 | module.exports = router; 10 | -------------------------------------------------------------------------------- /routes/index.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | 4 | /* GET home page. */ 5 | router.get('/', function(req, res, next) { 6 | res.render('index', { title: 'WebR2sync+' }); 7 | }); 8 | 9 | module.exports = router; 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "websync2", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "node ./bin/www" 7 | }, 8 | "dependencies": { 9 | "ali-oss": "^4.7.3", 10 | "arraybuffer-to-buffer": "0.0.4", 11 | "body-parser": "~1.15.2", 12 | "co": "^4.6.0", 13 | "cookie-parser": "~1.4.3", 14 | "debug": "~2.2.0", 15 | "ejs": "~2.5.2", 16 | "express": "~4.14.0", 17 | "fs": "0.0.1-security", 18 | "hashmap": "^2.0.6", 19 | "md5": "^2.2.1", 20 | "morgan": "~1.7.0", 21 | "serve-favicon": "~2.3.0", 22 | "socket.io": "^1.7.1", 23 | "stream-to-buffer": "^0.1.0" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WebRsync+ 2 | 3 | *Notice: 4 | 5 | 1. As a demo, we replace ECS API with local File API so that you can run locally. 6 | 2. Use pure javascript version of server side so you don't need to compile c++ code (which is same as Linux Rsync). 7 | 8 | ## Get started 9 | > environment: node.js v6.9.0 10 | >> \>\> git clone this repository 11 | >> 12 | >> \>\> install npm 13 | >> 14 | >> \>\> cd to the path of this project 15 | >> 16 | >> \>\> npm install 17 | >> 18 | >> \>\> node bin/www 19 | >> 20 | >> \>\> Now ,visit localhost:7070 on your browser 21 | 22 | ## Document 23 | > Javascript Implementation of rsync algorithm: WebR2sync_plus/blob/master/public/js/bit-sync.js 24 | > 25 | > WebRsync Client: WebR2sync_plus/blob/master/public/js/sync-client.js 26 | > 27 | > WebRsync Server: WebR2sync_plus/blob/master/app.js 28 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by xiaohe on 2017/1/22. 3 | */ 4 | var MOD_ADLER = 65521; 5 | function rollingChecksum(adlerInfo, offset, end, data) 6 | { 7 | var temp = data[offset - 1]; //this is the first byte used in the previous iteration 8 | var a = ((adlerInfo.a - temp + data[end]) % MOD_ADLER + MOD_ADLER)%MOD_ADLER; 9 | var b = ((adlerInfo.b - ((end - offset + 1) * temp) + a - 1) % MOD_ADLER + MOD_ADLER)%MOD_ADLER; 10 | return {a: a&0xffffffff, b: b&0xffffffff, checksum: ((b << 16) | a)&0xffffffff }; 11 | } 12 | 13 | function adler32(offset, end, data) 14 | { 15 | 16 | var a = 1, b= 0; 17 | 18 | //adjust the end to make sure we don't exceed the extents of the data. 19 | if(end >= data.length) 20 | end = data.length - 1; 21 | 22 | for(i=offset; i <= end; i++) 23 | { 24 | a += data[i]; 25 | b += a; 26 | a %= MOD_ADLER; 27 | b %= MOD_ADLER; 28 | } 29 | 30 | return {a: a&0xffffffff, b: b&0xffffffff, checksum: ((b << 16) | a) & 0xffffffff }; 31 | 32 | } 33 | var data8view = new Uint8Array([1,2,3,4,5,6,7,8]); 34 | console.log(data8view); 35 | adler32info = adler32(0,4,data8view) 36 | console.log(adler32info); 37 | adler32info = rollingChecksum(adler32info,1,5,data8view); 38 | console.log(adler32info); 39 | adler32info1 = adler32(1,5,data8view) 40 | console.log(adler32info1); 41 | -------------------------------------------------------------------------------- /views/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%= title %> 5 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 30 |

<%= title %>

31 |

Welcome to <%= title %>

32 | 33 | 34 | 35 | 36 |

37 | 38 |

39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /bin/www: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var app = require('../app'); 8 | var debug = require('debug')('websync2:server'); 9 | var http = require('http'); 10 | 11 | /** 12 | * Get port from environment and store in Express. 13 | */ 14 | 15 | var port = normalizePort(process.env.PORT || '7070'); 16 | app.set('port', port); 17 | 18 | /** 19 | * Create HTTP server. 20 | */ 21 | 22 | var server = http.createServer(app); 23 | 24 | /** 25 | * Listen on provided port, on all network interfaces. 26 | */ 27 | 28 | server.listen(port); 29 | server.on('error', onError); 30 | server.on('listening', onListening); 31 | 32 | /** 33 | * Normalize a port into a number, string, or false. 34 | */ 35 | 36 | function normalizePort(val) { 37 | var port = parseInt(val, 10); 38 | 39 | if (isNaN(port)) { 40 | // named pipe 41 | return val; 42 | } 43 | 44 | if (port >= 0) { 45 | // port number 46 | return port; 47 | } 48 | 49 | return false; 50 | } 51 | 52 | /** 53 | * Event listener for HTTP server "error" event. 54 | */ 55 | 56 | function onError(error) { 57 | if (error.syscall !== 'listen') { 58 | throw error; 59 | } 60 | 61 | var bind = typeof port === 'string' 62 | ? 'Pipe ' + port 63 | : 'Port ' + port; 64 | 65 | // handle specific listen errors with friendly messages 66 | switch (error.code) { 67 | case 'EACCES': 68 | console.error(bind + ' requires elevated privileges'); 69 | process.exit(1); 70 | break; 71 | case 'EADDRINUSE': 72 | console.error(bind + ' is already in use'); 73 | process.exit(1); 74 | break; 75 | default: 76 | throw error; 77 | } 78 | } 79 | 80 | /** 81 | * Event listener for HTTP server "listening" event. 82 | */ 83 | 84 | function onListening() { 85 | var addr = server.address(); 86 | var bind = typeof addr === 'string' 87 | ? 'pipe ' + addr 88 | : 'port ' + addr.port; 89 | debug('Listening on ' + bind); 90 | } 91 | -------------------------------------------------------------------------------- /public/js/siphash.js: -------------------------------------------------------------------------------- 1 | var SipHash = new function() { 2 | "use strict"; 3 | function _add(a, b) { 4 | var rl = a.l + b.l, 5 | a2 = { h: a.h + b.h + (rl / 2 >>> 31) >>> 0, 6 | l: rl >>> 0 }; 7 | a.h = a2.h; a.l = a2.l; 8 | } 9 | 10 | function _xor(a, b) { 11 | a.h ^= b.h; a.h >>>= 0; 12 | a.l ^= b.l; a.l >>>= 0; 13 | } 14 | 15 | function _rotl(a, n) { 16 | var a2 = { 17 | h: a.h << n | a.l >>> (32 - n), 18 | l: a.l << n | a.h >>> (32 - n) 19 | }; 20 | a.h = a2.h; a.l = a2.l; 21 | } 22 | 23 | function _rotl32(a) { 24 | var al = a.l; 25 | a.l = a.h; a.h = al; 26 | } 27 | 28 | function _compress(v0, v1, v2, v3) { 29 | _add(v0, v1); 30 | _add(v2, v3); 31 | _rotl(v1, 13); 32 | _rotl(v3, 16); 33 | _xor(v1, v0); 34 | _xor(v3, v2); 35 | _rotl32(v0); 36 | _add(v2, v1); 37 | _add(v0, v3); 38 | _rotl(v1, 17); 39 | _rotl(v3, 21); 40 | _xor(v1, v2); 41 | _xor(v3, v0); 42 | _rotl32(v2); 43 | } 44 | 45 | function _get_int(a, offset) { 46 | return a[offset + 3] << 24 | 47 | a[offset + 2] << 16 | 48 | a[offset + 1] << 8 | 49 | a[offset]; 50 | } 51 | 52 | function hash(m) { 53 | var key = [ 0xdeadbeef, 0xcafebabe, 0x8badf00d, 0x1badb002 ] 54 | var k0 = { h: key[1] >>> 0, l: key[0] >>> 0 }, 55 | k1 = { h: key[3] >>> 0, l: key[2] >>> 0 }, 56 | v0 = { h: k0.h, l: k0.l }, v2 = k0, 57 | v1 = { h: k1.h, l: k1.l }, v3 = k1, 58 | mi, mp = 0, ml = m.length, ml7 = ml - 7, 59 | buf = new Uint8Array(new ArrayBuffer(8)); 60 | 61 | _xor(v0, { h: 0x736f6d65, l: 0x70736575 }); 62 | _xor(v1, { h: 0x646f7261, l: 0x6e646f83 }); 63 | _xor(v2, { h: 0x6c796765, l: 0x6e657261 }); 64 | _xor(v3, { h: 0x74656462, l: 0x79746573 }); 65 | while (mp < ml7) { 66 | mi = { h: _get_int(m, mp + 4), l: _get_int(m, mp) }; 67 | _xor(v3, mi); 68 | _compress(v0, v1, v2, v3); 69 | _compress(v0, v1, v2, v3); 70 | _xor(v0, mi); 71 | mp += 8; 72 | } 73 | buf[7] = ml; 74 | var ic = 0; 75 | while (mp < ml) { 76 | buf[ic++] = m[mp++]; 77 | } 78 | while (ic < 7) { 79 | buf[ic++] = 0; 80 | } 81 | mi = { h: buf[7] << 24 | buf[6] << 16 | buf[5] << 8 | buf[4], 82 | l: buf[3] << 24 | buf[2] << 16 | buf[1] << 8 | buf[0] }; 83 | _xor(v3, mi); 84 | _compress(v0, v1, v2, v3); 85 | _compress(v0, v1, v2, v3); 86 | _xor(v0, mi); 87 | _xor(v2, { h: 0, l: 0xee }); 88 | _compress(v0, v1, v2, v3); 89 | _compress(v0, v1, v2, v3); 90 | _compress(v0, v1, v2, v3); 91 | _compress(v0, v1, v2, v3); 92 | 93 | var hh = { h: v0.h , l: v0.l }; 94 | _xor(hh, v1); 95 | _xor(hh, v2); 96 | _xor(hh, v3); 97 | _xor(v1, { h: 0, l: 0xdd }); 98 | _compress(v0, v1, v2, v3); 99 | _compress(v0, v1, v2, v3); 100 | _compress(v0, v1, v2, v3); 101 | _compress(v0, v1, v2, v3); 102 | var hl = v0; 103 | _xor(hl, v1); 104 | _xor(hl, v2); 105 | _xor(hl, v3); 106 | 107 | return [hl.h, hl.l, hh.h, hh.l]; 108 | } 109 | 110 | function string16_to_key(a) { 111 | return [_get_int(a, 0), _get_int(a, 4), 112 | _get_int(a, 8), _get_int(a, 12)]; 113 | } 114 | function hash_uint(key, m) { 115 | var r = hash(key, m); 116 | return (r.h & 0x1fffff) * 0x100000000 + r.l; 117 | } 118 | 119 | this.lib = { 120 | string16_to_key: string16_to_key, 121 | hash: hash, 122 | hash_uint: hash_uint 123 | }; 124 | } 125 | 126 | if(((typeof require) != "undefined") && 127 | ((typeof module) != "undefined") && 128 | ((typeof module.exports) != "undefined")) 129 | module.exports = SipHash; -------------------------------------------------------------------------------- /public/js/back.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by xiaohe on 2016/12/12. 3 | */ 4 | /** 5 | * Create a patch document that contains all the information needed to bring the destination data into synchronization with the source data. 6 | * 7 | * The patch document looks like this: (little Endian) 8 | * 4 bytes - blockSize 9 | * 4 bytes - number of patches 10 | * 4 bytes - number of matched blocks 11 | * For each matched block: 12 | * 4 bytes - the index of the matched block 13 | * For each patch: 14 | * 4 bytes - last matching block index. NOTE: This is 1 based index! Zero indicates beginning of file, NOT the first block 15 | * 4 bytes - patch size 16 | * n bytes - new data 17 | */ 18 | function createPatchDocument(checksumDocument, data) 19 | { 20 | function appendBuffer( buffer1, buffer2 ) { 21 | var tmp = new Uint8Array( buffer1.byteLength + buffer2.byteLength ); 22 | tmp.set( new Uint8Array( buffer1 ), 0 ); 23 | tmp.set( new Uint8Array( buffer2 ), buffer1.byteLength ); 24 | return tmp.buffer; 25 | } 26 | 27 | /** 28 | * First, check to see if there's a match on the 16 bit hash 29 | * Then, look through all the entries in the hashtable row for an adler 32 match. 30 | * Finally, do a strong md5 comparison 31 | */ 32 | function checkMatch(adlerInfo, hashTable, block) 33 | { 34 | var hash = hash16(adlerInfo.checksum); 35 | if(!(hashTable[hash])) return false; 36 | var row = hashTable[hash]; 37 | var i=0; 38 | var matchedIndex=0; 39 | 40 | for(i=0; i data.byteLength) 90 | { 91 | chunkSize = data.byteLength - i; 92 | adlerInfo=null; //need to reset this because the rolling checksum doesn't work correctly on a final non-aligned block 93 | } 94 | else 95 | chunkSize = blockSize; 96 | 97 | if(adlerInfo) 98 | adlerInfo = rollingChecksum(adlerInfo, i, i + chunkSize - 1, dataUint8); 99 | else 100 | adlerInfo = adler32(i, i + chunkSize - 1, dataUint8); 101 | 102 | var matchedBlock = checkMatch(adlerInfo, hashTable, new Uint8Array(data,i,chunkSize)); 103 | if(matchedBlock) 104 | { 105 | //if we have a match, do the following: 106 | //1) add the matched block index to our tracking buffer 107 | //2) check to see if there's a current patch. If so, add it to the patch document. 108 | //3) jump forward blockSize bytes and continue 109 | matchedBlocksUint32[matchCount] = matchedBlock; 110 | matchCount++; 111 | //check to see if we need more memory for the matched blocks 112 | if(matchCount >= matchedBlocksUint32.length) 113 | { 114 | matchedBlocks = appendBuffer(matchedBlocks, new ArrayBuffer(1000)); 115 | matchedBlocksUint32 = new Uint32Array(matchedBlocks); 116 | } 117 | if(currentPatchSize > 0) 118 | { 119 | //create the patch and append it to the patches buffer 120 | patch = new ArrayBuffer(4 + 4); //4 for last match index, 4 for patch size 121 | var patchUint32 = new Uint32Array(patch,0,2); 122 | patchUint32[0] = lastMatchIndex; 123 | patchUint32[1] = currentPatchSize; 124 | patch = appendBuffer(patch,currentPatch.slice(0,currentPatchSize)); 125 | patches = appendBuffer(patches, patch); 126 | currentPatch = new ArrayBuffer(1000); 127 | currentPatchUint8 = new Uint8Array(currentPatch); 128 | currentPatchSize = 0; 129 | numPatches++; 130 | } 131 | lastMatchIndex = matchedBlock; 132 | i+=blockSize; 133 | if(i >= dataUint8.length -1 ) break; 134 | adlerInfo=null; 135 | continue; 136 | } 137 | else 138 | { 139 | //while we don't have a block match, append bytes to the current patch 140 | currentPatchUint8[currentPatchSize] = dataUint8[i]; 141 | currentPatchSize++; 142 | if(currentPatchSize >= currentPatch.byteLength) 143 | { 144 | //allocate another 1000 bytes 145 | currentPatch = appendBuffer(currentPatch, new ArrayBuffer(1000)); 146 | currentPatchUint8 = new Uint8Array(currentPatch); 147 | } 148 | } 149 | if((i) >= dataUint8.length -1) break; 150 | i++; 151 | } //end for each byte in the data 152 | if(currentPatchSize > 0) 153 | { 154 | //create the patch and append it to the patches buffer 155 | patch = new ArrayBuffer(4 + 4); //4 for last match index, 4 for patch size 156 | var patchUint32 = new Uint32Array(patch,0,2); 157 | patchUint32[0] = lastMatchIndex; 158 | patchUint32[1] = currentPatchSize; 159 | patch = appendBuffer(patch,currentPatch.slice(0,currentPatchSize)); 160 | patches = appendBuffer(patches, patch); 161 | numPatches++; 162 | } 163 | 164 | var patchDocumentView32 = new Uint32Array(patchDocument); 165 | patchDocumentView32[0] = blockSize; 166 | patchDocumentView32[1] = numPatches; 167 | patchDocumentView32[2] = matchCount; 168 | patchDocument = appendBuffer(patchDocument, matchedBlocks.slice(0,matchCount * 4)); 169 | patchDocument = appendBuffer(patchDocument, patches); 170 | 171 | var patchDocumentView32 = new Uint32Array(patchDocument,0,matchCount + 3); 172 | var patchDocumentView8 = new Uint8Array(patchDocument); 173 | 174 | return patchDocument; 175 | } -------------------------------------------------------------------------------- /public/js/sync-client.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by xiaohe on 2016/12/9. 3 | * client functions for web-sync 4 | */ 5 | 6 | // Check for the various File API support. 7 | if (window.File && window.FileReader && window.FileList && window.Blob) { 8 | // Great success! All the File APIs are supported. 9 | console.log("Great success! All the File APIs are supported.") 10 | } else { 11 | alert('The File APIs are not fully supported in this browser.'); 12 | } 13 | 14 | var lock = true; 15 | var current_file = null; 16 | var block_size = 64 *1024; 17 | var chunkSize = 1024 * 1024 * 1024; // bytes 18 | var timetamp; 19 | var checksum_timetamp; 20 | var test_start_time = (new Date()).getTime(); 21 | var traffic; 22 | 23 | var test_hash_average = 0; 24 | var test_hash_times = 0; 25 | 26 | var socket = io.connect('http://'+window.location.hostname+':8001'); 27 | socket.on('matchdoc',function(req){ 28 | traffic += req.matchdoc.byteLength; 29 | console.log("<= 0){ 85 | data_offset += block_size; 86 | } 87 | else{ 88 | if(patchsize < doc_offset+block_size + 4*2){ 89 | patchdoc = appendBlock(patchdoc,new ArrayBuffer(patchsize+block_size)); 90 | patchdoc32View = new Uint32Array(patchdoc); 91 | patchdoc8View = new Uint8Array(patchdoc); 92 | patchsize += patchsize + block_size; 93 | } 94 | //not match save into patch 95 | numPatch++; 96 | patchdoc32View[doc_offset/4] = block_index; 97 | doc_offset+=4; 98 | var current_blocksize = block_size; 99 | if(data.byteLength - data_offset < block_size){ 100 | current_blocksize = data.byteLength - data_offset; 101 | } 102 | patchdoc32View[doc_offset/4] = current_blocksize; 103 | doc_offset+=4; 104 | for(j = 0; j < current_blocksize;j++){ 105 | patchdoc8View[doc_offset] = data8View[data_offset+j]; 106 | doc_offset++; 107 | } 108 | data_offset += current_blocksize; 109 | //if doc_offset is not 4-times 110 | doc_offset = Math.ceil(doc_offset/4)*4; 111 | 112 | } 113 | block_index++; 114 | } 115 | patchdoc32View[1] = numPatch; 116 | var d = new Date(); 117 | var t = d.getTime() - checksum_timetamp.getTime(); 118 | console.info(current_file.name,'patchdoc time is',t,'ms'); 119 | console.log('patchdoc from',start,'to',stop,':',doc_offset,current_file.size); 120 | 121 | //emit the patchdoc 122 | traffic += doc_offset; 123 | socket.emit('patchdoc', {'filename':current_file.name,'patchdoc':patchdoc.slice(0,doc_offset),'numChunk':numChunk}); 124 | }) 125 | 126 | } 127 | 128 | /* 129 | * parse file 130 | */ 131 | function parseFile(file, callback) { 132 | var fileSize = file.size; 133 | 134 | var offset = 0; 135 | var self = this; // we need a reference to the current object 136 | var chunkReaderBlock = null; 137 | 138 | var readEventHandler = function(evt) { 139 | if (evt.target.error == null) { 140 | var start = offset 141 | offset += evt.target.result.byteLength; 142 | var stop = offset 143 | callback('data',evt.target.result,start,stop); // callback for handling read chunk 144 | 145 | } else { 146 | console.log("Read error: " + evt.target.error); 147 | return; 148 | } 149 | if (offset >= fileSize) { 150 | // console.log("Done reading file"); 151 | return; 152 | } 153 | chunkReaderBlock(offset, chunkSize, file); 154 | } 155 | 156 | chunkReaderBlock = function(_offset, length, _file) { 157 | var r = new FileReader(); 158 | var start = _offset; 159 | var stop = start + length; 160 | if(stop > _file.size) stop = _file.size; 161 | if (file.webkitSlice) { 162 | var blob = file.webkitSlice(start, stop); 163 | } else if (file.mozSlice) { 164 | var blob = file.mozSlice(start, stop ); 165 | }else if(file.slice) { 166 | blob = file.slice(start, stop); 167 | } 168 | r.onloadend = readEventHandler; 169 | r.readAsArrayBuffer(blob); 170 | } 171 | 172 | // now let's start the read with the first block 173 | chunkReaderBlock(offset, chunkSize, file); 174 | } 175 | /* 176 | * load blocks from file 177 | * @param: block_size : bytes 178 | */ 179 | function load_blocks() { 180 | // block_size = blockSize; 181 | if(lock) { 182 | lock = false; 183 | traffic = 0; 184 | $("#result").text("同步开始"); 185 | timetamp = new Date(); 186 | } 187 | 188 | else{ 189 | console.log('wait for lock of parsing file'); 190 | return; 191 | } 192 | 193 | var files = document.getElementById('files').files; 194 | if (!files.length) { 195 | alert('Please select a file!'); 196 | return; 197 | } 198 | current_file = files[0]; 199 | 200 | var all_numBlocks = Math.ceil(current_file.size / block_size); 201 | var i=0; 202 | var all_docLength = 203 | 4 + //4 bytes for block size 204 | 4 + //4 bytes for the number of blocks 205 | 4 + // 4 bytes for bytelength 206 | ( all_numBlocks * //the number of blocks times 207 | ( 4 + //the 4 bytes for the adler32 plus 208 | 16) //the 16 bytes for the md5 209 | ) 210 | var all_checksumdoc = new ArrayBuffer(all_docLength); 211 | var all_docview = new Uint32Array(all_checksumdoc); 212 | all_docview[0] = block_size; 213 | all_docview[1] = all_numBlocks; 214 | all_bytelength = 0; 215 | var doc_offset = 3; 216 | parseFile(current_file,function(type,data,start,stop){ 217 | var checksumstart = new Date().getTime(); 218 | checksumdoc = BSync.createChecksumDocument(block_size,data); 219 | var docView = new Uint32Array(checksumdoc); 220 | numBlocks = docView[1]; 221 | all_bytelength += docView[2]; 222 | for(i = 0; i < numBlocks*(1+4);i++){ 223 | all_docview[doc_offset + i] = docView[i+3]; 224 | } 225 | doc_offset += numBlocks*(1+4); 226 | 227 | console.log('checksum from',start,'to',stop,':',doc_offset,"/",all_docLength/4); 228 | 229 | if(doc_offset == all_docLength/4){ 230 | all_docview[2] = all_bytelength; 231 | console.log('all checksum length is ',all_docLength); 232 | console.log('>>emit checksum doc',all_docLength); 233 | traffic += all_checksumdoc.byteLength; 234 | d = new Date(); 235 | t = d.getTime() - checksumstart; 236 | console.log('checksum time is',t,'ms'); 237 | checksum_timetamp = d; 238 | socket.emit('checksumdoc', {'filename':current_file.name,'checksumdoc':all_checksumdoc}); 239 | // lock = true; 240 | 241 | test_hash_average += t; 242 | test_hash_times ++; 243 | console.log('average time is',test_hash_average/test_hash_times,'ms'); 244 | 245 | } 246 | }) 247 | } 248 | var test_handler = null; 249 | var sync_handler = null; 250 | var test_time = 0; 251 | function test(){ 252 | d = new Date(); 253 | test_time = d.getTime() - test_start_time; 254 | console.info(test_time); 255 | } 256 | function start_test(){ 257 | 258 | test_start_time = (new Date()).getTime(); 259 | if(!test_handler){ 260 | console.log('start_test'); 261 | test_handler = setInterval(function(){ test(); }, 100); 262 | sync_handler = setTimeout(load_blocks,200); 263 | }else{ 264 | clearInterval(test_handler); 265 | test_handler = null; 266 | sync_handler = null; 267 | console.log('stop_test'); 268 | } 269 | } 270 | 271 | function stop_test(){ 272 | if(test_handler){ 273 | clearInterval(test_handler); 274 | test_handler = null; 275 | sync_handler = null; 276 | console.log('stop_test'); 277 | } 278 | } 279 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var path = require('path'); 3 | var favicon = require('serve-favicon'); 4 | var logger = require('morgan'); 5 | var cookieParser = require('cookie-parser'); 6 | var bodyParser = require('body-parser'); 7 | var fs = require("fs"); 8 | var OSS = require('ali-oss'); 9 | var co = require('co'); 10 | var client = new OSS({ 11 | region: 'oss-cn-shanghai', 12 | accessKeyId: 'LTAIiTUIWMffMbLD', 13 | accessKeySecret: 'Ykcf2pSp3uTP9IKCqqRHzJXMgDwQzC', 14 | bucket:'xiaohe-websync', 15 | internal:true, 16 | endpoint:'oss-cn-shanghai-internal.aliyuncs.com', 17 | }); 18 | 19 | var index = require('./routes/index'); 20 | var users = require('./routes/users'); 21 | var BSync = require('./public/js/bit-sync'); 22 | var arrayBufferToBuffer = require('arraybuffer-to-buffer'); 23 | 24 | var stream = require('stream'); 25 | var streamToBuffer = require('stream-to-buffer') 26 | 27 | 28 | var basePath = __dirname + "/upload/"; 29 | 30 | var app = express(); 31 | 32 | var server = require('http').Server(app); 33 | var io = require('socket.io')(server); 34 | 35 | var direct_stream = false; 36 | 37 | server.listen(8001); 38 | 39 | /* match_cache = [filename1:(1234 bytes,matchdoc], 40 | } 41 | */ 42 | var match_cache = {}; 43 | var patch_cache = {}; 44 | var patch_num_cache = {}; 45 | 46 | var origin_file_cache = {}; 47 | 48 | // view engine setup 49 | app.set('views', path.join(__dirname, 'views')); 50 | app.set('view engine', 'ejs'); 51 | 52 | // uncomment after placing your favicon in /public 53 | //app.use(favicon(path.join(__dirname, 'public', 'favicon.ico'))); 54 | app.use(logger('dev')); 55 | app.use(bodyParser.json()); 56 | app.use(bodyParser.urlencoded({ extended: false })); 57 | app.use(cookieParser()); 58 | app.use(express.static(path.join(__dirname, 'public'))); 59 | 60 | app.use('/', index); 61 | app.use('/users', users); 62 | 63 | // catch 404 and forward to error handler 64 | app.use(function(req, res, next) { 65 | var err = new Error('Not Found'); 66 | err.status = 404; 67 | next(err); 68 | }); 69 | 70 | // error handler 71 | app.use(function(err, req, res, next) { 72 | // set locals, only providing error in development 73 | res.locals.message = err.message; 74 | res.locals.error = req.app.get('env') === 'development' ? err : {}; 75 | 76 | // render the error page 77 | res.status(err.status || 500); 78 | res.render('error'); 79 | }); 80 | 81 | io.on('connection', function(socket) 82 | { 83 | console.log('new connect!'); 84 | socket.on('checksumdoc', function(req) 85 | { 86 | 87 | console.log('got checksumdoc of filename:',req.filename); 88 | filePath = basePath + req.filename; 89 | 90 | checksumdocView = new Uint8Array(req.checksumdoc); 91 | checksumdocBuffer = checksumdocView.buffer; 92 | 93 | if(direct_stream){ 94 | co(function * () { 95 | client.useBucket('xiaohe-websync'); 96 | var result = yield client.getStream(req.filename); 97 | streamToBuffer(result.stream, function (err, buffer) { 98 | origin_file_cache[req.filename] = buffer 99 | matchret = BSync.createMatchDocument(checksumdocBuffer,buffer); 100 | matchdoc = matchret[0]; 101 | filebytelength = matchret[1]; 102 | match_cache[req.filename] = [filebytelength,matchdoc]; 103 | socket.emit('matchdoc',{filename:req.filename,matchdoc:matchdoc}); 104 | }) 105 | }).catch(function (err) { 106 | console.log(err); 107 | origin_file_cache[req.filename] = new ArrayBuffer(0); 108 | matchret = BSync.createMatchDocument(checksumdocBuffer,new ArrayBuffer(0)); 109 | matchdoc = matchret[0]; 110 | filebytelength = matchret[1]; 111 | match_cache[req.filename] = [filebytelength,matchdoc]; 112 | socket.emit('matchdoc',{filename:req.filename,matchdoc:matchdoc}); 113 | }); 114 | }else{ 115 | fs.stat(filePath, function (err,stat) { 116 | 117 | if (err == null) { 118 | getFileData(filePath,function(data){ 119 | origin_file_cache[req.filename] = data 120 | matchret = BSync.createMatchDocument(checksumdocBuffer,data); 121 | matchdoc = matchret[0]; 122 | filebytelength = matchret[1]; 123 | match_cache[req.filename] = [filebytelength,matchdoc]; 124 | socket.emit('matchdoc',{filename:req.filename,matchdoc:matchdoc}); 125 | }) 126 | } else { 127 | origin_file_cache[req.filename] = new ArrayBuffer(0); 128 | matchret = BSync.createMatchDocument(checksumdocBuffer,new ArrayBuffer(0)); 129 | matchdoc = matchret[0]; 130 | filebytelength = matchret[1]; 131 | match_cache[req.filename] = [filebytelength,matchdoc]; 132 | socket.emit('matchdoc',{filename:req.filename,matchdoc:matchdoc}); 133 | } 134 | }); 135 | } 136 | }); 137 | 138 | socket.on('patchdoc',function(req){ 139 | console.log('patchdoc of',req.filename); 140 | 141 | //todo combine patch doc and matched blocks 142 | var matchdoc = match_cache[req.filename][1]; 143 | var filebytelength = match_cache[req.filename][0]; 144 | var matchtable = BSync.parseMatchDocument(matchdoc); 145 | filePath = basePath + req.filename; 146 | var newFilebuffer = new ArrayBuffer(filebytelength); 147 | var file8View = new Uint8Array(newFilebuffer); 148 | if(!patch_cache[req.filename]) { 149 | patch_cache[req.filename] = {}; 150 | patch_num_cache[req.filename] = 0; 151 | } 152 | 153 | parsePatchDoc(req.filename,req.patchdoc,function(blocksize){ 154 | if(patch_num_cache[req.filename] == req.numChunk){ 155 | data = origin_file_cache[req.filename]; 156 | var data8View = new Uint8Array(data); 157 | numBlocks = Math.ceil(filebytelength/blocksize); 158 | dataoffset = 0; 159 | for(i = 1; i <= numBlocks;i++){ 160 | //i is blockindex 161 | if(matchtable[i] >= 0){ 162 | matchoffset = matchtable[i]; 163 | // for(j = 0;j < blocksize && (j+matchoffset) < data.length;j++){ 164 | // file8View[dataoffset++] = data8View[matchoffset+j]; 165 | // } 166 | var b_size = Math.min(blocksize,data.length - matchoffset); 167 | file8View.set(data8View.slice(matchoffset,matchoffset+b_size),dataoffset); 168 | dataoffset += b_size; 169 | }else{ 170 | blockcontent = patch_cache[req.filename][i]; 171 | block8View = new Uint8Array(blockcontent); 172 | // for(j = 0; j < blockcontent.byteLength;j++){ 173 | // file8View[dataoffset++] = block8View[j]; 174 | // } 175 | file8View.set(block8View,dataoffset); 176 | dataoffset += block8View.length; 177 | 178 | } 179 | } 180 | if(direct_stream){ 181 | co(function * () { 182 | client.useBucket('xiaohe-websync'); 183 | var bufferStream = new stream.PassThrough(); 184 | bufferStream.end(arrayBufferToBuffer(newFilebuffer)); 185 | var result = yield client.putStream(req.filename,bufferStream); 186 | if(result['res']['status'] == 200){ 187 | console.log('file write over~'); 188 | BlockSyncStatus = 'success'; 189 | socket.emit('SyncOver', BlockSyncStatus); 190 | //reset cache 191 | delete match_cache[req.filename] 192 | delete patch_num_cache[req.filename] 193 | delete patch_cache[req.filename] 194 | } 195 | }).catch(function (err) { 196 | console.log(err); 197 | }); 198 | }else{ 199 | fs.writeFile(filePath,arrayBufferToBuffer(newFilebuffer),function(err){ 200 | if (err){ 201 | throw 'error writing file: ' + err; 202 | } 203 | else{ 204 | console.log('file write over~'); 205 | BlockSyncStatus = 'success'; 206 | socket.emit('SyncOver', BlockSyncStatus); 207 | 208 | console.log('冲突:',BSync.hash16_coll); 209 | } 210 | //reset cache 211 | delete match_cache[req.filename] 212 | delete patch_num_cache[req.filename] 213 | delete patch_cache[req.filename] 214 | }) 215 | } 216 | } 217 | }); 218 | }) 219 | }); 220 | function parsePatchDoc(filename,patchdoc,callback){ 221 | var patchdoc8View = new Uint8Array(patchdoc); 222 | var patchdoc32View = new Uint32Array(patchdoc8View.buffer); 223 | var patch_offset = 2; 224 | var blocksize = patchdoc32View[0]; 225 | var numPatch = patchdoc32View[1]; 226 | // var patchtable = {}; 227 | for(i = 0; i < numPatch;i++){ 228 | var blockindex = patchdoc32View[patch_offset]; 229 | patch_offset++; 230 | var currentblocksize = patchdoc32View[patch_offset]; 231 | patch_offset++; 232 | var filecontent = new ArrayBuffer(currentblocksize); 233 | var file8View = new Uint8Array(filecontent); 234 | for(j = 0;j < currentblocksize;j++){ 235 | file8View[j] = patchdoc8View[patch_offset*4+j]; 236 | } 237 | patch_offset += Math.ceil(j/4); 238 | patch_cache[filename][blockindex] = filecontent; 239 | } 240 | patch_num_cache[filename]++; 241 | callback(blocksize); 242 | } 243 | 244 | // 在服务器端读取文件 245 | function getFileData(file, callback) 246 | { 247 | fs.readFile(file, function(err, data) { 248 | if (err) { 249 | // console.error("Error getting file data: " + err); 250 | } 251 | callback(data); 252 | }); 253 | } 254 | 255 | 256 | module.exports = app; 257 | -------------------------------------------------------------------------------- /public/js/md5.js: -------------------------------------------------------------------------------- 1 | /* 2 | * JavaScript MD5 3 | * https://github.com/blueimp/JavaScript-MD5 4 | * 5 | * Copyright 2011, Sebastian Tschan 6 | * https://blueimp.net 7 | * 8 | * Licensed under the MIT license: 9 | * http://www.opensource.org/licenses/MIT 10 | * 11 | * Based on 12 | * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message 13 | * Digest Algorithm, as defined in RFC 1321. 14 | * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009 15 | * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet 16 | * Distributed under the BSD License 17 | * See http://pajhome.org.uk/crypt/md5 for more info. 18 | */ 19 | 20 | /* global define */ 21 | 22 | ;(function ($) { 23 | 'use strict' 24 | 25 | /* 26 | * Add integers, wrapping at 2^32. This uses 16-bit operations internally 27 | * to work around bugs in some JS interpreters. 28 | */ 29 | function safeAdd (x, y) { 30 | var lsw = (x & 0xFFFF) + (y & 0xFFFF) 31 | var msw = (x >> 16) + (y >> 16) + (lsw >> 16) 32 | return (msw << 16) | (lsw & 0xFFFF) 33 | } 34 | 35 | /* 36 | * Bitwise rotate a 32-bit number to the left. 37 | */ 38 | function bitRotateLeft (num, cnt) { 39 | return (num << cnt) | (num >>> (32 - cnt)) 40 | } 41 | 42 | /* 43 | * These functions implement the four basic operations the algorithm uses. 44 | */ 45 | function md5cmn (q, a, b, x, s, t) { 46 | return safeAdd(bitRotateLeft(safeAdd(safeAdd(a, q), safeAdd(x, t)), s), b) 47 | } 48 | function md5ff (a, b, c, d, x, s, t) { 49 | return md5cmn((b & c) | ((~b) & d), a, b, x, s, t) 50 | } 51 | function md5gg (a, b, c, d, x, s, t) { 52 | return md5cmn((b & d) | (c & (~d)), a, b, x, s, t) 53 | } 54 | function md5hh (a, b, c, d, x, s, t) { 55 | return md5cmn(b ^ c ^ d, a, b, x, s, t) 56 | } 57 | function md5ii (a, b, c, d, x, s, t) { 58 | return md5cmn(c ^ (b | (~d)), a, b, x, s, t) 59 | } 60 | 61 | /* 62 | * Calculate the MD5 of an array of little-endian words, and a bit length. 63 | */ 64 | function binlMD5 (x, len) { 65 | /* append padding */ 66 | x[len >> 5] |= 0x80 << (len % 32) 67 | x[(((len + 64) >>> 9) << 4) + 14] = len 68 | 69 | var i 70 | var olda 71 | var oldb 72 | var oldc 73 | var oldd 74 | var a = 1732584193 75 | var b = -271733879 76 | var c = -1732584194 77 | var d = 271733878 78 | 79 | for (i = 0; i < x.length; i += 16) { 80 | olda = a 81 | oldb = b 82 | oldc = c 83 | oldd = d 84 | 85 | a = md5ff(a, b, c, d, x[i], 7, -680876936) 86 | d = md5ff(d, a, b, c, x[i + 1], 12, -389564586) 87 | c = md5ff(c, d, a, b, x[i + 2], 17, 606105819) 88 | b = md5ff(b, c, d, a, x[i + 3], 22, -1044525330) 89 | a = md5ff(a, b, c, d, x[i + 4], 7, -176418897) 90 | d = md5ff(d, a, b, c, x[i + 5], 12, 1200080426) 91 | c = md5ff(c, d, a, b, x[i + 6], 17, -1473231341) 92 | b = md5ff(b, c, d, a, x[i + 7], 22, -45705983) 93 | a = md5ff(a, b, c, d, x[i + 8], 7, 1770035416) 94 | d = md5ff(d, a, b, c, x[i + 9], 12, -1958414417) 95 | c = md5ff(c, d, a, b, x[i + 10], 17, -42063) 96 | b = md5ff(b, c, d, a, x[i + 11], 22, -1990404162) 97 | a = md5ff(a, b, c, d, x[i + 12], 7, 1804603682) 98 | d = md5ff(d, a, b, c, x[i + 13], 12, -40341101) 99 | c = md5ff(c, d, a, b, x[i + 14], 17, -1502002290) 100 | b = md5ff(b, c, d, a, x[i + 15], 22, 1236535329) 101 | 102 | a = md5gg(a, b, c, d, x[i + 1], 5, -165796510) 103 | d = md5gg(d, a, b, c, x[i + 6], 9, -1069501632) 104 | c = md5gg(c, d, a, b, x[i + 11], 14, 643717713) 105 | b = md5gg(b, c, d, a, x[i], 20, -373897302) 106 | a = md5gg(a, b, c, d, x[i + 5], 5, -701558691) 107 | d = md5gg(d, a, b, c, x[i + 10], 9, 38016083) 108 | c = md5gg(c, d, a, b, x[i + 15], 14, -660478335) 109 | b = md5gg(b, c, d, a, x[i + 4], 20, -405537848) 110 | a = md5gg(a, b, c, d, x[i + 9], 5, 568446438) 111 | d = md5gg(d, a, b, c, x[i + 14], 9, -1019803690) 112 | c = md5gg(c, d, a, b, x[i + 3], 14, -187363961) 113 | b = md5gg(b, c, d, a, x[i + 8], 20, 1163531501) 114 | a = md5gg(a, b, c, d, x[i + 13], 5, -1444681467) 115 | d = md5gg(d, a, b, c, x[i + 2], 9, -51403784) 116 | c = md5gg(c, d, a, b, x[i + 7], 14, 1735328473) 117 | b = md5gg(b, c, d, a, x[i + 12], 20, -1926607734) 118 | 119 | a = md5hh(a, b, c, d, x[i + 5], 4, -378558) 120 | d = md5hh(d, a, b, c, x[i + 8], 11, -2022574463) 121 | c = md5hh(c, d, a, b, x[i + 11], 16, 1839030562) 122 | b = md5hh(b, c, d, a, x[i + 14], 23, -35309556) 123 | a = md5hh(a, b, c, d, x[i + 1], 4, -1530992060) 124 | d = md5hh(d, a, b, c, x[i + 4], 11, 1272893353) 125 | c = md5hh(c, d, a, b, x[i + 7], 16, -155497632) 126 | b = md5hh(b, c, d, a, x[i + 10], 23, -1094730640) 127 | a = md5hh(a, b, c, d, x[i + 13], 4, 681279174) 128 | d = md5hh(d, a, b, c, x[i], 11, -358537222) 129 | c = md5hh(c, d, a, b, x[i + 3], 16, -722521979) 130 | b = md5hh(b, c, d, a, x[i + 6], 23, 76029189) 131 | a = md5hh(a, b, c, d, x[i + 9], 4, -640364487) 132 | d = md5hh(d, a, b, c, x[i + 12], 11, -421815835) 133 | c = md5hh(c, d, a, b, x[i + 15], 16, 530742520) 134 | b = md5hh(b, c, d, a, x[i + 2], 23, -995338651) 135 | 136 | a = md5ii(a, b, c, d, x[i], 6, -198630844) 137 | d = md5ii(d, a, b, c, x[i + 7], 10, 1126891415) 138 | c = md5ii(c, d, a, b, x[i + 14], 15, -1416354905) 139 | b = md5ii(b, c, d, a, x[i + 5], 21, -57434055) 140 | a = md5ii(a, b, c, d, x[i + 12], 6, 1700485571) 141 | d = md5ii(d, a, b, c, x[i + 3], 10, -1894986606) 142 | c = md5ii(c, d, a, b, x[i + 10], 15, -1051523) 143 | b = md5ii(b, c, d, a, x[i + 1], 21, -2054922799) 144 | a = md5ii(a, b, c, d, x[i + 8], 6, 1873313359) 145 | d = md5ii(d, a, b, c, x[i + 15], 10, -30611744) 146 | c = md5ii(c, d, a, b, x[i + 6], 15, -1560198380) 147 | b = md5ii(b, c, d, a, x[i + 13], 21, 1309151649) 148 | a = md5ii(a, b, c, d, x[i + 4], 6, -145523070) 149 | d = md5ii(d, a, b, c, x[i + 11], 10, -1120210379) 150 | c = md5ii(c, d, a, b, x[i + 2], 15, 718787259) 151 | b = md5ii(b, c, d, a, x[i + 9], 21, -343485551) 152 | 153 | a = safeAdd(a, olda) 154 | b = safeAdd(b, oldb) 155 | c = safeAdd(c, oldc) 156 | d = safeAdd(d, oldd) 157 | } 158 | return [a, b, c, d] 159 | } 160 | 161 | /* 162 | * Convert an array of little-endian words to a string 163 | */ 164 | function binl2rstr (input) { 165 | var i 166 | var output = '' 167 | var length32 = input.length * 32 168 | for (i = 0; i < length32; i += 8) { 169 | output += String.fromCharCode((input[i >> 5] >>> (i % 32)) & 0xFF) 170 | } 171 | return output 172 | } 173 | 174 | /* 175 | * Convert a raw string to an array of little-endian words 176 | * Characters >255 have their high-byte silently ignored. 177 | */ 178 | function rstr2binl (input) { 179 | var i 180 | var output = [] 181 | output[(input.length >> 2) - 1] = undefined 182 | for (i = 0; i < output.length; i += 1) { 183 | output[i] = 0 184 | } 185 | var length8 = input.length * 8 186 | for (i = 0; i < length8; i += 8) { 187 | output[i >> 5] |= (input.charCodeAt(i / 8) & 0xFF) << (i % 32) 188 | } 189 | return output 190 | } 191 | 192 | /* 193 | * Calculate the MD5 of a raw string 194 | */ 195 | function rstrMD5 (s) { 196 | return binl2rstr(binlMD5(rstr2binl(s), s.length * 8)) 197 | } 198 | 199 | /* 200 | * Calculate the HMAC-MD5, of a key and some data (raw strings) 201 | */ 202 | function rstrHMACMD5 (key, data) { 203 | var i 204 | var bkey = rstr2binl(key) 205 | var ipad = [] 206 | var opad = [] 207 | var hash 208 | ipad[15] = opad[15] = undefined 209 | if (bkey.length > 16) { 210 | bkey = binlMD5(bkey, key.length * 8) 211 | } 212 | for (i = 0; i < 16; i += 1) { 213 | ipad[i] = bkey[i] ^ 0x36363636 214 | opad[i] = bkey[i] ^ 0x5C5C5C5C 215 | } 216 | hash = binlMD5(ipad.concat(rstr2binl(data)), 512 + data.length * 8) 217 | return binl2rstr(binlMD5(opad.concat(hash), 512 + 128)) 218 | } 219 | 220 | /* 221 | * Convert a raw string to a hex string 222 | */ 223 | function rstr2hex (input) { 224 | var hexTab = '0123456789abcdef' 225 | var output = '' 226 | var x 227 | var i 228 | for (i = 0; i < input.length; i += 1) { 229 | x = input.charCodeAt(i) 230 | output += hexTab.charAt((x >>> 4) & 0x0F) + 231 | hexTab.charAt(x & 0x0F) 232 | } 233 | return output 234 | } 235 | 236 | /* 237 | * Encode a string as utf-8 238 | */ 239 | function str2rstrUTF8 (input) { 240 | return unescape(encodeURIComponent(input)) 241 | } 242 | 243 | /* 244 | * Take string arguments and return either raw or hex encoded strings 245 | */ 246 | function rawMD5 (s) { 247 | return rstrMD5(str2rstrUTF8(s)) 248 | } 249 | function hexMD5 (s) { 250 | return rstr2hex(rawMD5(s)) 251 | } 252 | function rawHMACMD5 (k, d) { 253 | return rstrHMACMD5(str2rstrUTF8(k), str2rstrUTF8(d)) 254 | } 255 | function hexHMACMD5 (k, d) { 256 | return rstr2hex(rawHMACMD5(k, d)) 257 | } 258 | 259 | function md5 (string, key, raw) { 260 | if (!key) { 261 | if (!raw) { 262 | return hexMD5(string) 263 | } 264 | return rawMD5(string) 265 | } 266 | if (!raw) { 267 | return hexHMACMD5(key, string) 268 | } 269 | return rawHMACMD5(key, string) 270 | } 271 | 272 | if (typeof define === 'function' && define.amd) { 273 | define(function () { 274 | return md5 275 | }) 276 | } else if (typeof module === 'object' && module.exports) { 277 | module.exports = md5 278 | } else { 279 | $.md5 = md5 280 | } 281 | }(this)) 282 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /public/js/murmurhash3.js: -------------------------------------------------------------------------------- 1 | // +----------------------------------------------------------------------+ 2 | // | murmurHash3.js v2.1.2 (http://github.com/karanlyons/murmurHash.js) | 3 | // | A javascript implementation of MurmurHash3's x86 hashing algorithms. | 4 | // |----------------------------------------------------------------------| 5 | // | Copyright (c) 2012 Karan Lyons | 6 | // | Freely distributable under the MIT license. | 7 | // +----------------------------------------------------------------------+ 8 | 9 | 10 | var MMHASH3 = new function() { 11 | 12 | // Create a local object that'll be exported or referenced globally. 13 | var library = { 14 | 'version': '2.1.2', 15 | 'x86': {}, 16 | 'x64': {} 17 | }; 18 | 19 | 20 | // PRIVATE FUNCTIONS 21 | // ----------------- 22 | 23 | function _x86Multiply(m, n) { 24 | // 25 | // Given two 32bit ints, returns the two multiplied together as a 26 | // 32bit int. 27 | // 28 | 29 | return ((m & 0xffff) * n) + ((((m >>> 16) * n) & 0xffff) << 16); 30 | } 31 | 32 | 33 | function _x86Rotl(m, n) { 34 | // 35 | // Given a 32bit int and an int representing a number of bit positions, 36 | // returns the 32bit int rotated left by that number of positions. 37 | // 38 | 39 | return (m << n) | (m >>> (32 - n)); 40 | } 41 | 42 | 43 | function _x86Fmix(h) { 44 | // 45 | // Given a block, returns murmurHash3's final x86 mix of that block. 46 | // 47 | 48 | h ^= h >>> 16; 49 | h = _x86Multiply(h, 0x85ebca6b); 50 | h ^= h >>> 13; 51 | h = _x86Multiply(h, 0xc2b2ae35); 52 | h ^= h >>> 16; 53 | 54 | return h; 55 | } 56 | 57 | 58 | function _x64Add(m, n) { 59 | // 60 | // Given two 64bit ints (as an array of two 32bit ints) returns the two 61 | // added together as a 64bit int (as an array of two 32bit ints). 62 | // 63 | 64 | m = [m[0] >>> 16, m[0] & 0xffff, m[1] >>> 16, m[1] & 0xffff]; 65 | n = [n[0] >>> 16, n[0] & 0xffff, n[1] >>> 16, n[1] & 0xffff]; 66 | var o = [0, 0, 0, 0]; 67 | 68 | o[3] += m[3] + n[3]; 69 | o[2] += o[3] >>> 16; 70 | o[3] &= 0xffff; 71 | 72 | o[2] += m[2] + n[2]; 73 | o[1] += o[2] >>> 16; 74 | o[2] &= 0xffff; 75 | 76 | o[1] += m[1] + n[1]; 77 | o[0] += o[1] >>> 16; 78 | o[1] &= 0xffff; 79 | 80 | o[0] += m[0] + n[0]; 81 | o[0] &= 0xffff; 82 | 83 | return [(o[0] << 16) | o[1], (o[2] << 16) | o[3]]; 84 | } 85 | 86 | 87 | function _x64Multiply(m, n) { 88 | // 89 | // Given two 64bit ints (as an array of two 32bit ints) returns the two 90 | // multiplied together as a 64bit int (as an array of two 32bit ints). 91 | // 92 | 93 | m = [m[0] >>> 16, m[0] & 0xffff, m[1] >>> 16, m[1] & 0xffff]; 94 | n = [n[0] >>> 16, n[0] & 0xffff, n[1] >>> 16, n[1] & 0xffff]; 95 | var o = [0, 0, 0, 0]; 96 | 97 | o[3] += m[3] * n[3]; 98 | o[2] += o[3] >>> 16; 99 | o[3] &= 0xffff; 100 | 101 | o[2] += m[2] * n[3]; 102 | o[1] += o[2] >>> 16; 103 | o[2] &= 0xffff; 104 | 105 | o[2] += m[3] * n[2]; 106 | o[1] += o[2] >>> 16; 107 | o[2] &= 0xffff; 108 | 109 | o[1] += m[1] * n[3]; 110 | o[0] += o[1] >>> 16; 111 | o[1] &= 0xffff; 112 | 113 | o[1] += m[2] * n[2]; 114 | o[0] += o[1] >>> 16; 115 | o[1] &= 0xffff; 116 | 117 | o[1] += m[3] * n[1]; 118 | o[0] += o[1] >>> 16; 119 | o[1] &= 0xffff; 120 | 121 | o[0] += (m[0] * n[3]) + (m[1] * n[2]) + (m[2] * n[1]) + (m[3] * n[0]); 122 | o[0] &= 0xffff; 123 | 124 | return [(o[0] << 16) | o[1], (o[2] << 16) | o[3]]; 125 | } 126 | 127 | 128 | function _x64Rotl(m, n) { 129 | // 130 | // Given a 64bit int (as an array of two 32bit ints) and an int 131 | // representing a number of bit positions, returns the 64bit int (as an 132 | // array of two 32bit ints) rotated left by that number of positions. 133 | // 134 | 135 | n %= 64; 136 | 137 | if (n === 32) { 138 | return [m[1], m[0]]; 139 | } 140 | 141 | else if (n < 32) { 142 | return [(m[0] << n) | (m[1] >>> (32 - n)), (m[1] << n) | (m[0] >>> (32 - n))]; 143 | } 144 | 145 | else { 146 | n -= 32; 147 | return [(m[1] << n) | (m[0] >>> (32 - n)), (m[0] << n) | (m[1] >>> (32 - n))]; 148 | } 149 | } 150 | 151 | 152 | function _x64LeftShift(m, n) { 153 | // 154 | // Given a 64bit int (as an array of two 32bit ints) and an int 155 | // representing a number of bit positions, returns the 64bit int (as an 156 | // array of two 32bit ints) shifted left by that number of positions. 157 | // 158 | 159 | n %= 64; 160 | 161 | if (n === 0) { 162 | return m; 163 | } 164 | 165 | else if (n < 32) { 166 | return [(m[0] << n) | (m[1] >>> (32 - n)), m[1] << n]; 167 | } 168 | 169 | else { 170 | return [m[1] << (n - 32), 0]; 171 | } 172 | } 173 | 174 | 175 | function _x64Xor(m, n) { 176 | // 177 | // Given two 64bit ints (as an array of two 32bit ints) returns the two 178 | // xored together as a 64bit int (as an array of two 32bit ints). 179 | // 180 | 181 | return [m[0] ^ n[0], m[1] ^ n[1]]; 182 | } 183 | 184 | 185 | function _x64Fmix(h) { 186 | // 187 | // Given a block, returns murmurHash3's final x64 mix of that block. 188 | // (`[0, h[0] >>> 1]` is a 33 bit unsigned right shift. This is the 189 | // only place where we need to right shift 64bit ints.) 190 | // 191 | 192 | h = _x64Xor(h, [0, h[0] >>> 1]); 193 | h = _x64Multiply(h, [0xff51afd7, 0xed558ccd]); 194 | h = _x64Xor(h, [0, h[0] >>> 1]); 195 | h = _x64Multiply(h, [0xc4ceb9fe, 0x1a85ec53]); 196 | h = _x64Xor(h, [0, h[0] >>> 1]); 197 | 198 | return h; 199 | } 200 | 201 | 202 | // PUBLIC FUNCTIONS 203 | // ---------------- 204 | 205 | library.x86.hash32 = function (key, seed) { 206 | // 207 | // Given a string and an optional seed as an int, returns a 32 bit hash 208 | // using the x86 flavor of MurmurHash3, as an unsigned int. 209 | // 210 | 211 | key = key || ''; 212 | seed = seed || 0; 213 | 214 | var remainder = key.length % 4; 215 | var bytes = key.length - remainder; 216 | 217 | var h1 = seed; 218 | 219 | var k1 = 0; 220 | 221 | var c1 = 0xcc9e2d51; 222 | var c2 = 0x1b873593; 223 | 224 | for (var i = 0; i < bytes; i = i + 4) { 225 | k1 = ((key.charCodeAt(i) & 0xff)) | ((key.charCodeAt(i + 1) & 0xff) << 8) | ((key.charCodeAt(i + 2) & 0xff) << 16) | ((key.charCodeAt(i + 3) & 0xff) << 24); 226 | 227 | k1 = _x86Multiply(k1, c1); 228 | k1 = _x86Rotl(k1, 15); 229 | k1 = _x86Multiply(k1, c2); 230 | 231 | h1 ^= k1; 232 | h1 = _x86Rotl(h1, 13); 233 | h1 = _x86Multiply(h1, 5) + 0xe6546b64; 234 | } 235 | 236 | k1 = 0; 237 | 238 | switch (remainder) { 239 | case 3: 240 | k1 ^= (key.charCodeAt(i + 2) & 0xff) << 16; 241 | 242 | case 2: 243 | k1 ^= (key.charCodeAt(i + 1) & 0xff) << 8; 244 | 245 | case 1: 246 | k1 ^= (key.charCodeAt(i) & 0xff); 247 | k1 = _x86Multiply(k1, c1); 248 | k1 = _x86Rotl(k1, 15); 249 | k1 = _x86Multiply(k1, c2); 250 | h1 ^= k1; 251 | } 252 | 253 | h1 ^= key.length; 254 | h1 = _x86Fmix(h1); 255 | 256 | return h1 >>> 0; 257 | }; 258 | 259 | 260 | library.x86.hash128 = function (key, seed) { 261 | // 262 | // Given a string and an optional seed as an int, returns a 128 bit 263 | // hash using the x86 flavor of MurmurHash3, as an unsigned hex. 264 | // 265 | 266 | key = key || ''; 267 | seed = seed || 0; 268 | 269 | var remainder = key.length % 16; 270 | var bytes = key.length - remainder; 271 | 272 | var h1 = seed; 273 | var h2 = seed; 274 | var h3 = seed; 275 | var h4 = seed; 276 | 277 | var k1 = 0; 278 | var k2 = 0; 279 | var k3 = 0; 280 | var k4 = 0; 281 | 282 | var c1 = 0x239b961b; 283 | var c2 = 0xab0e9789; 284 | var c3 = 0x38b34ae5; 285 | var c4 = 0xa1e38b93; 286 | 287 | for (var i = 0; i < bytes; i = i + 16) { 288 | k1 = ((key.charCodeAt(i) & 0xff)) | ((key.charCodeAt(i + 1) & 0xff) << 8) | ((key.charCodeAt(i + 2) & 0xff) << 16) | ((key.charCodeAt(i + 3) & 0xff) << 24); 289 | k2 = ((key.charCodeAt(i + 4) & 0xff)) | ((key.charCodeAt(i + 5) & 0xff) << 8) | ((key.charCodeAt(i + 6) & 0xff) << 16) | ((key.charCodeAt(i + 7) & 0xff) << 24); 290 | k3 = ((key.charCodeAt(i + 8) & 0xff)) | ((key.charCodeAt(i + 9) & 0xff) << 8) | ((key.charCodeAt(i + 10) & 0xff) << 16) | ((key.charCodeAt(i + 11) & 0xff) << 24); 291 | k4 = ((key.charCodeAt(i + 12) & 0xff)) | ((key.charCodeAt(i + 13) & 0xff) << 8) | ((key.charCodeAt(i + 14) & 0xff) << 16) | ((key.charCodeAt(i + 15) & 0xff) << 24); 292 | 293 | k1 = _x86Multiply(k1, c1); 294 | k1 = _x86Rotl(k1, 15); 295 | k1 = _x86Multiply(k1, c2); 296 | h1 ^= k1; 297 | 298 | h1 = _x86Rotl(h1, 19); 299 | h1 += h2; 300 | h1 = _x86Multiply(h1, 5) + 0x561ccd1b; 301 | 302 | k2 = _x86Multiply(k2, c2); 303 | k2 = _x86Rotl(k2, 16); 304 | k2 = _x86Multiply(k2, c3); 305 | h2 ^= k2; 306 | 307 | h2 = _x86Rotl(h2, 17); 308 | h2 += h3; 309 | h2 = _x86Multiply(h2, 5) + 0x0bcaa747; 310 | 311 | k3 = _x86Multiply(k3, c3); 312 | k3 = _x86Rotl(k3, 17); 313 | k3 = _x86Multiply(k3, c4); 314 | h3 ^= k3; 315 | 316 | h3 = _x86Rotl(h3, 15); 317 | h3 += h4; 318 | h3 = _x86Multiply(h3, 5) + 0x96cd1c35; 319 | 320 | k4 = _x86Multiply(k4, c4); 321 | k4 = _x86Rotl(k4, 18); 322 | k4 = _x86Multiply(k4, c1); 323 | h4 ^= k4; 324 | 325 | h4 = _x86Rotl(h4, 13); 326 | h4 += h1; 327 | h4 = _x86Multiply(h4, 5) + 0x32ac3b17; 328 | } 329 | 330 | k1 = 0; 331 | k2 = 0; 332 | k3 = 0; 333 | k4 = 0; 334 | 335 | switch (remainder) { 336 | case 15: 337 | k4 ^= key.charCodeAt(i + 14) << 16; 338 | 339 | case 14: 340 | k4 ^= key.charCodeAt(i + 13) << 8; 341 | 342 | case 13: 343 | k4 ^= key.charCodeAt(i + 12); 344 | k4 = _x86Multiply(k4, c4); 345 | k4 = _x86Rotl(k4, 18); 346 | k4 = _x86Multiply(k4, c1); 347 | h4 ^= k4; 348 | 349 | case 12: 350 | k3 ^= key.charCodeAt(i + 11) << 24; 351 | 352 | case 11: 353 | k3 ^= key.charCodeAt(i + 10) << 16; 354 | 355 | case 10: 356 | k3 ^= key.charCodeAt(i + 9) << 8; 357 | 358 | case 9: 359 | k3 ^= key.charCodeAt(i + 8); 360 | k3 = _x86Multiply(k3, c3); 361 | k3 = _x86Rotl(k3, 17); 362 | k3 = _x86Multiply(k3, c4); 363 | h3 ^= k3; 364 | 365 | case 8: 366 | k2 ^= key.charCodeAt(i + 7) << 24; 367 | 368 | case 7: 369 | k2 ^= key.charCodeAt(i + 6) << 16; 370 | 371 | case 6: 372 | k2 ^= key.charCodeAt(i + 5) << 8; 373 | 374 | case 5: 375 | k2 ^= key.charCodeAt(i + 4); 376 | k2 = _x86Multiply(k2, c2); 377 | k2 = _x86Rotl(k2, 16); 378 | k2 = _x86Multiply(k2, c3); 379 | h2 ^= k2; 380 | 381 | case 4: 382 | k1 ^= key.charCodeAt(i + 3) << 24; 383 | 384 | case 3: 385 | k1 ^= key.charCodeAt(i + 2) << 16; 386 | 387 | case 2: 388 | k1 ^= key.charCodeAt(i + 1) << 8; 389 | 390 | case 1: 391 | k1 ^= key.charCodeAt(i); 392 | k1 = _x86Multiply(k1, c1); 393 | k1 = _x86Rotl(k1, 15); 394 | k1 = _x86Multiply(k1, c2); 395 | h1 ^= k1; 396 | } 397 | 398 | h1 ^= key.length; 399 | h2 ^= key.length; 400 | h3 ^= key.length; 401 | h4 ^= key.length; 402 | 403 | h1 += h2; 404 | h1 += h3; 405 | h1 += h4; 406 | h2 += h1; 407 | h3 += h1; 408 | h4 += h1; 409 | 410 | h1 = _x86Fmix(h1); 411 | h2 = _x86Fmix(h2); 412 | h3 = _x86Fmix(h3); 413 | h4 = _x86Fmix(h4); 414 | 415 | h1 += h2; 416 | h1 += h3; 417 | h1 += h4; 418 | h2 += h1; 419 | h3 += h1; 420 | h4 += h1; 421 | 422 | return ("00000000" + (h1 >>> 0).toString(16)).slice(-8) + ("00000000" + (h2 >>> 0).toString(16)).slice(-8) + ("00000000" + (h3 >>> 0).toString(16)).slice(-8) + ("00000000" + (h4 >>> 0).toString(16)).slice(-8); 423 | }; 424 | library.x86.hash128_arraybuffer = function (message, seed, offset,chunksize) { 425 | if(offset == null){ 426 | offset = 0; 427 | chunksize = message.length; 428 | } 429 | key = message; 430 | seed = seed || 0; 431 | 432 | var remainder = chunksize % 16; 433 | var bytes = chunksize - remainder; 434 | 435 | var h1 = seed; 436 | var h2 = seed; 437 | var h3 = seed; 438 | var h4 = seed; 439 | 440 | var k1 = 0; 441 | var k2 = 0; 442 | var k3 = 0; 443 | var k4 = 0; 444 | 445 | var c1 = 0x239b961b; 446 | var c2 = 0xab0e9789; 447 | var c3 = 0x38b34ae5; 448 | var c4 = 0xa1e38b93; 449 | 450 | for (var i = offset; i < offset+bytes; i = i + 16) { 451 | k1 = ((key[i] & 0xff)) | ((key[i + 1] & 0xff) << 8) | ((key[i + 2] & 0xff) << 16) | ((key[i + 3] & 0xff) << 24); 452 | k2 = ((key[i + 4] & 0xff)) | ((key[i + 5] & 0xff) << 8) | ((key[i + 6] & 0xff) << 16) | ((key[i + 7] & 0xff) << 24); 453 | k3 = ((key[i + 8] & 0xff)) | ((key[i + 9] & 0xff) << 8) | ((key[i + 10] & 0xff) << 16) | ((key[i + 11] & 0xff) << 24); 454 | k4 = ((key[i + 12] & 0xff)) | ((key[i + 13] & 0xff) << 8) | ((key[i + 14] & 0xff) << 16) | ((key[i + 15] & 0xff) << 24); 455 | 456 | k1 = _x86Multiply(k1, c1); 457 | k1 = _x86Rotl(k1, 15); 458 | k1 = _x86Multiply(k1, c2); 459 | h1 ^= k1; 460 | 461 | h1 = _x86Rotl(h1, 19); 462 | h1 += h2; 463 | h1 = _x86Multiply(h1, 5) + 0x561ccd1b; 464 | 465 | k2 = _x86Multiply(k2, c2); 466 | k2 = _x86Rotl(k2, 16); 467 | k2 = _x86Multiply(k2, c3); 468 | h2 ^= k2; 469 | 470 | h2 = _x86Rotl(h2, 17); 471 | h2 += h3; 472 | h2 = _x86Multiply(h2, 5) + 0x0bcaa747; 473 | 474 | k3 = _x86Multiply(k3, c3); 475 | k3 = _x86Rotl(k3, 17); 476 | k3 = _x86Multiply(k3, c4); 477 | h3 ^= k3; 478 | 479 | h3 = _x86Rotl(h3, 15); 480 | h3 += h4; 481 | h3 = _x86Multiply(h3, 5) + 0x96cd1c35; 482 | 483 | k4 = _x86Multiply(k4, c4); 484 | k4 = _x86Rotl(k4, 18); 485 | k4 = _x86Multiply(k4, c1); 486 | h4 ^= k4; 487 | 488 | h4 = _x86Rotl(h4, 13); 489 | h4 += h1; 490 | h4 = _x86Multiply(h4, 5) + 0x32ac3b17; 491 | } 492 | 493 | k1 = 0; 494 | k2 = 0; 495 | k3 = 0; 496 | k4 = 0; 497 | 498 | switch (remainder) { 499 | case 15: 500 | k4 ^= key[i + 14] << 16; 501 | 502 | case 14: 503 | k4 ^= key[i + 13] << 8; 504 | 505 | case 13: 506 | k4 ^= key[i + 12]; 507 | k4 = _x86Multiply(k4, c4); 508 | k4 = _x86Rotl(k4, 18); 509 | k4 = _x86Multiply(k4, c1); 510 | h4 ^= k4; 511 | 512 | case 12: 513 | k3 ^= key[i + 11] << 24; 514 | 515 | case 11: 516 | k3 ^= key[i + 10] << 16; 517 | 518 | case 10: 519 | k3 ^= key[i + 9] << 8; 520 | 521 | case 9: 522 | k3 ^= key[i + 8]; 523 | k3 = _x86Multiply(k3, c3); 524 | k3 = _x86Rotl(k3, 17); 525 | k3 = _x86Multiply(k3, c4); 526 | h3 ^= k3; 527 | 528 | case 8: 529 | k2 ^= key[i + 7] << 24; 530 | 531 | case 7: 532 | k2 ^= key[i + 6] << 16; 533 | 534 | case 6: 535 | k2 ^= key[i + 5] << 8; 536 | 537 | case 5: 538 | k2 ^= key[i + 4]; 539 | k2 = _x86Multiply(k2, c2); 540 | k2 = _x86Rotl(k2, 16); 541 | k2 = _x86Multiply(k2, c3); 542 | h2 ^= k2; 543 | 544 | case 4: 545 | k1 ^= key[i + 3] << 24; 546 | 547 | case 3: 548 | k1 ^= key[i + 2] << 16; 549 | 550 | case 2: 551 | k1 ^= key[i + 1] << 8; 552 | 553 | case 1: 554 | k1 ^= key[i]; 555 | k1 = _x86Multiply(k1, c1); 556 | k1 = _x86Rotl(k1, 15); 557 | k1 = _x86Multiply(k1, c2); 558 | h1 ^= k1; 559 | } 560 | 561 | // h1 ^= key.length; 562 | // h2 ^= key.length; 563 | // h3 ^= key.length; 564 | // h4 ^= key.length; 565 | h1 ^= chunksize; 566 | h2 ^= chunksize; 567 | h3 ^= chunksize; 568 | h4 ^= chunksize; 569 | 570 | h1 += h2; 571 | h1 += h3; 572 | h1 += h4; 573 | h2 += h1; 574 | h3 += h1; 575 | h4 += h1; 576 | 577 | h1 = _x86Fmix(h1); 578 | h2 = _x86Fmix(h2); 579 | h3 = _x86Fmix(h3); 580 | h4 = _x86Fmix(h4); 581 | 582 | h1 += h2; 583 | h1 += h3; 584 | h1 += h4; 585 | h2 += h1; 586 | h3 += h1; 587 | h4 += h1; 588 | // console.log('md5',offset,chunksize,new Uint32Array([h1,h2,h3,h4])); 589 | return new Uint32Array([h1,h2,h3,h4]); 590 | // return ("00000000" + (h1 >>> 0).toString(16)).slice(-8) + ("00000000" + (h2 >>> 0).toString(16)).slice(-8) + ("00000000" + (h3 >>> 0).toString(16)).slice(-8) + ("00000000" + (h4 >>> 0).toString(16)).slice(-8); 591 | }; 592 | library.x64.hash128_arraybuffer = function(message,seed){ 593 | //message is arraybuffer 594 | key = new Uint8Array(message); 595 | seed = seed || 0; 596 | 597 | var remainder = key.length % 16; 598 | var bytes = key.length - remainder; 599 | 600 | // var h1 = [0, seed]; 601 | // var h2 = [0, seed]; 602 | 603 | // var k1 = [0, 0]; 604 | // var k2 = [0, 0]; 605 | // var k1,k2; 606 | 607 | // var c1 = [0x87c37b91, 0x114253d5]; 608 | // var c2 = [0x4cf5ad43, 0x2745937f]; 609 | 610 | for (var i = 0; i < bytes; i = i + 16) { 611 | k1 = [((key[i + 4]& 0xff)) | ((key[i + 5] & 0xff) << 8) | ((key[i + 6] & 0xff) << 16) | ((key[i + 7] & 0xff) << 24), ((key[i] & 0xff)) | ((key[i + 1]& 0xff) << 8) | ((key[i + 2]& 0xff) << 16) | ((key[i + 3] & 0xff) << 24)]; 612 | k2 = [((key[i + 12] & 0xff)) | ((key[i + 13] & 0xff) << 8) | ((key[i + 14] & 0xff) << 16) | ((key[i + 15] & 0xff) << 24), ((key[i + 8] & 0xff)) | ((key[i + 9] & 0xff) << 8) | ((key[i + 10] & 0xff) << 16) | ((key[i + 11] & 0xff) << 24)]; 613 | 614 | k1 = _x64Multiply(k1, [0x87c37b91, 0x114253d5]); 615 | k1 = _x64Rotl(k1, 31); 616 | k1 = _x64Multiply(k1, [0x4cf5ad43, 0x2745937f]); 617 | // h1 = _x64Xor(h1, k1); 618 | 619 | // h1 = _x64Rotl(h1, 27); 620 | // h1 = _x64Add(h1, h2); 621 | // h1 = _x64Add(_x64Multiply(h1, [0, 5]), [0, 0x52dce729]); 622 | h1 = [0, seed]; 623 | h2 = [0, seed]; 624 | 625 | k2 = _x64Multiply(k2, [0x4cf5ad43, 0x2745937f]); 626 | k2 = _x64Rotl(k2, 33); 627 | k2 = _x64Multiply(k2, [0x87c37b91, 0x114253d5]); 628 | // h2 = _x64Xor(h2, k2); 629 | // 630 | // h2 = _x64Rotl(h2, 31); 631 | // h2 = _x64Add(h2, h1); 632 | // h2 = _x64Add(_x64Multiply(h2, [0, 5]), [0, 0x38495ab5]); 633 | } 634 | 635 | k1 = [0, 0]; 636 | k2 = [0, 0]; 637 | 638 | switch (remainder) { 639 | case 15: 640 | k2 = _x64Xor(k2, _x64LeftShift([0, key[i + 14]], 48)); 641 | 642 | case 14: 643 | k2 = _x64Xor(k2, _x64LeftShift([0, key[i + 13]], 40)); 644 | 645 | case 13: 646 | k2 = _x64Xor(k2, _x64LeftShift([0, key[i + 12]], 32)); 647 | 648 | case 12: 649 | k2 = _x64Xor(k2, _x64LeftShift([0, key[i + 11]], 24)); 650 | 651 | case 11: 652 | k2 = _x64Xor(k2, _x64LeftShift([0, key[i + 10]], 16)); 653 | 654 | case 10: 655 | k2 = _x64Xor(k2, _x64LeftShift([0, key[i + 9]], 8)); 656 | 657 | case 9: 658 | k2 = _x64Xor(k2, [0, key[i + 8]]); 659 | k2 = _x64Multiply(k2, [0x4cf5ad43, 0x2745937f]); 660 | k2 = _x64Rotl(k2, 33); 661 | k2 = _x64Multiply(k2, [0x87c37b91, 0x114253d5]); 662 | h2 = _x64Xor(h2, k2); 663 | 664 | case 8: 665 | k1 = _x64Xor(k1, _x64LeftShift([0, key[i + 7]], 56)); 666 | 667 | case 7: 668 | k1 = _x64Xor(k1, _x64LeftShift([0, key[i + 6]], 48)); 669 | 670 | case 6: 671 | k1 = _x64Xor(k1, _x64LeftShift([0, key[i + 5]], 40)); 672 | 673 | case 5: 674 | k1 = _x64Xor(k1, _x64LeftShift([0, key[i + 4]], 32)); 675 | 676 | case 4: 677 | k1 = _x64Xor(k1, _x64LeftShift([0, key[i + 3]], 24)); 678 | 679 | case 3: 680 | k1 = _x64Xor(k1, _x64LeftShift([0, key[i + 2]], 16)); 681 | 682 | case 2: 683 | k1 = _x64Xor(k1, _x64LeftShift([0, key[i + 1]], 8)); 684 | 685 | case 1: 686 | k1 = _x64Xor(k1, [0, key[i]]); 687 | k1 = _x64Multiply(k1, [0x87c37b91, 0x114253d5]); 688 | k1 = _x64Rotl(k1, 31); 689 | k1 = _x64Multiply(k1, [0x4cf5ad43, 0x2745937f]); 690 | h1 = _x64Xor(h1, k1); 691 | } 692 | 693 | h1 = _x64Xor(h1, [0, key.length]); 694 | h2 = _x64Xor(h2, [0, key.length]); 695 | 696 | h1 = _x64Add(h1, h2); 697 | h2 = _x64Add(h2, h1); 698 | 699 | h1 = _x64Fmix(h1); 700 | h2 = _x64Fmix(h2); 701 | 702 | h1 = _x64Add(h1, h2); 703 | h2 = _x64Add(h2, h1); 704 | 705 | return ("00000000" + (h1[0] >>> 0).toString(16)).slice(-8) + ("00000000" + (h1[1] >>> 0).toString(16)).slice(-8) + ("00000000" + (h2[0] >>> 0).toString(16)).slice(-8) + ("00000000" + (h2[1] >>> 0).toString(16)).slice(-8); 706 | } 707 | library.x64.hash128 = function (key, seed) { 708 | // 709 | // Given a string and an optional seed as an int, returns a 128 bit 710 | // hash using the x64 flavor of MurmurHash3, as an unsigned hex. 711 | // 712 | 713 | key = key || ''; 714 | seed = seed || 0; 715 | 716 | var remainder = key.length % 16; 717 | var bytes = key.length - remainder; 718 | 719 | var h1 = [0, seed]; 720 | var h2 = [0, seed]; 721 | 722 | var k1 = [0, 0]; 723 | var k2 = [0, 0]; 724 | 725 | var c1 = [0x87c37b91, 0x114253d5]; 726 | var c2 = [0x4cf5ad43, 0x2745937f]; 727 | 728 | for (var i = 0; i < bytes; i = i + 16) { 729 | k1 = [((key.charCodeAt(i + 4) & 0xff)) | ((key.charCodeAt(i + 5) & 0xff) << 8) | ((key.charCodeAt(i + 6) & 0xff) << 16) | ((key.charCodeAt(i + 7) & 0xff) << 24), ((key.charCodeAt(i) & 0xff)) | ((key.charCodeAt(i + 1) & 0xff) << 8) | ((key.charCodeAt(i + 2) & 0xff) << 16) | ((key.charCodeAt(i + 3) & 0xff) << 24)]; 730 | k2 = [((key.charCodeAt(i + 12) & 0xff)) | ((key.charCodeAt(i + 13) & 0xff) << 8) | ((key.charCodeAt(i + 14) & 0xff) << 16) | ((key.charCodeAt(i + 15) & 0xff) << 24), ((key.charCodeAt(i + 8) & 0xff)) | ((key.charCodeAt(i + 9) & 0xff) << 8) | ((key.charCodeAt(i + 10) & 0xff) << 16) | ((key.charCodeAt(i + 11) & 0xff) << 24)]; 731 | 732 | k1 = _x64Multiply(k1, c1); 733 | k1 = _x64Rotl(k1, 31); 734 | k1 = _x64Multiply(k1, c2); 735 | h1 = _x64Xor(h1, k1); 736 | 737 | h1 = _x64Rotl(h1, 27); 738 | h1 = _x64Add(h1, h2); 739 | h1 = _x64Add(_x64Multiply(h1, [0, 5]), [0, 0x52dce729]); 740 | 741 | k2 = _x64Multiply(k2, c2); 742 | k2 = _x64Rotl(k2, 33); 743 | k2 = _x64Multiply(k2, c1); 744 | h2 = _x64Xor(h2, k2); 745 | 746 | h2 = _x64Rotl(h2, 31); 747 | h2 = _x64Add(h2, h1); 748 | h2 = _x64Add(_x64Multiply(h2, [0, 5]), [0, 0x38495ab5]); 749 | } 750 | 751 | k1 = [0, 0]; 752 | k2 = [0, 0]; 753 | 754 | switch (remainder) { 755 | case 15: 756 | k2 = _x64Xor(k2, _x64LeftShift([0, key.charCodeAt(i + 14)], 48)); 757 | 758 | case 14: 759 | k2 = _x64Xor(k2, _x64LeftShift([0, key.charCodeAt(i + 13)], 40)); 760 | 761 | case 13: 762 | k2 = _x64Xor(k2, _x64LeftShift([0, key.charCodeAt(i + 12)], 32)); 763 | 764 | case 12: 765 | k2 = _x64Xor(k2, _x64LeftShift([0, key.charCodeAt(i + 11)], 24)); 766 | 767 | case 11: 768 | k2 = _x64Xor(k2, _x64LeftShift([0, key.charCodeAt(i + 10)], 16)); 769 | 770 | case 10: 771 | k2 = _x64Xor(k2, _x64LeftShift([0, key.charCodeAt(i + 9)], 8)); 772 | 773 | case 9: 774 | k2 = _x64Xor(k2, [0, key.charCodeAt(i + 8)]); 775 | k2 = _x64Multiply(k2, c2); 776 | k2 = _x64Rotl(k2, 33); 777 | k2 = _x64Multiply(k2, c1); 778 | h2 = _x64Xor(h2, k2); 779 | 780 | case 8: 781 | k1 = _x64Xor(k1, _x64LeftShift([0, key.charCodeAt(i + 7)], 56)); 782 | 783 | case 7: 784 | k1 = _x64Xor(k1, _x64LeftShift([0, key.charCodeAt(i + 6)], 48)); 785 | 786 | case 6: 787 | k1 = _x64Xor(k1, _x64LeftShift([0, key.charCodeAt(i + 5)], 40)); 788 | 789 | case 5: 790 | k1 = _x64Xor(k1, _x64LeftShift([0, key.charCodeAt(i + 4)], 32)); 791 | 792 | case 4: 793 | k1 = _x64Xor(k1, _x64LeftShift([0, key.charCodeAt(i + 3)], 24)); 794 | 795 | case 3: 796 | k1 = _x64Xor(k1, _x64LeftShift([0, key.charCodeAt(i + 2)], 16)); 797 | 798 | case 2: 799 | k1 = _x64Xor(k1, _x64LeftShift([0, key.charCodeAt(i + 1)], 8)); 800 | 801 | case 1: 802 | k1 = _x64Xor(k1, [0, key.charCodeAt(i)]); 803 | k1 = _x64Multiply(k1, c1); 804 | k1 = _x64Rotl(k1, 31); 805 | k1 = _x64Multiply(k1, c2); 806 | h1 = _x64Xor(h1, k1); 807 | } 808 | 809 | h1 = _x64Xor(h1, [0, key.length]); 810 | h2 = _x64Xor(h2, [0, key.length]); 811 | 812 | h1 = _x64Add(h1, h2); 813 | h2 = _x64Add(h2, h1); 814 | 815 | h1 = _x64Fmix(h1); 816 | h2 = _x64Fmix(h2); 817 | 818 | h1 = _x64Add(h1, h2); 819 | h2 = _x64Add(h2, h1); 820 | 821 | return ("00000000" + (h1[0] >>> 0).toString(16)).slice(-8) + ("00000000" + (h1[1] >>> 0).toString(16)).slice(-8) + ("00000000" + (h2[0] >>> 0).toString(16)).slice(-8) + ("00000000" + (h2[1] >>> 0).toString(16)).slice(-8); 822 | }; 823 | this.lib = library; 824 | } 825 | 826 | if(((typeof require) != "undefined") && 827 | ((typeof module) != "undefined") && 828 | ((typeof module.exports) != "undefined")) 829 | module.exports = MMHASH3; 830 | -------------------------------------------------------------------------------- /public/js/bit-sync.js: -------------------------------------------------------------------------------- 1 | /** 2 | * bit-sync.js 3 | * 4 | * For more information see the readme. 5 | * 6 | * Source is located at https://github.com/claytongulick/bit-sync 7 | * 8 | * Licensed under the MIT License 9 | * 10 | * Copyright Clayton C. Gulick 11 | * 12 | * Permission is hereby granted, free of charge, to any person obtaining a copy 13 | * of this software and associated documentation files (the "Software"), to deal 14 | * in the Software without restriction, including without limitation the rights 15 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 16 | * copies of the Software, and to permit persons to whom the Software is 17 | * furnished to do so, subject to the following conditions: 18 | * 19 | * The above copyright notice and this permission notice shall be included in 20 | * all copies or substantial portions of the Software. 21 | * 22 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 27 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 28 | * THE SOFTWARE. 29 | */ 30 | 31 | var MMHASH3; 32 | if(!MMHASH3){ 33 | MMHASH3 = require('./murmurhash3'); 34 | SipHash = require('./siphash'); 35 | var performance = Date; 36 | } 37 | var MOD_ADLER = 65521; 38 | 39 | var BSync = new function() 40 | { 41 | 42 | /******* Privates *********/ 43 | //todo 44 | var md5_0 = function (message,seed,offset,chunksize) { 45 | message = message.slice(offset,offset+chunksize) 46 | // Convert to byte array 47 | if (message.constructor == String) message = UTF8.stringToBytes(message); 48 | /* else, assume byte array already */ 49 | var bytesToWords = function (bytes) { 50 | for (var words = [], i = 0, b = 0; i < bytes.length; i++, b += 8) 51 | words[b >>> 5] |= (bytes[i] & 0xFF) << (24 - b % 32); 52 | return words; 53 | } 54 | 55 | var m = bytesToWords(message), 56 | l = message.length * 8, 57 | a = 1732584193, 58 | b = -271733879, 59 | c = -1732584194, 60 | d = 271733878; 61 | 62 | // Swap endian 63 | for (var i = 0; i < m.length; i++) { 64 | m[i] = ((m[i] << 8) | (m[i] >>> 24)) & 0x00FF00FF | 65 | ((m[i] << 24) | (m[i] >>> 8)) & 0xFF00FF00; 66 | } 67 | 68 | // Padding 69 | m[l >>> 5] |= 0x80 << (l % 32); 70 | m[(((l + 64) >>> 9) << 4) + 14] = l; 71 | 72 | // Method shortcuts 73 | // Auxiliary functions 74 | var FF = function (a, b, c, d, x, s, t) { 75 | var n = a + (b & c | ~b & d) + (x >>> 0) + t; 76 | return ((n << s) | (n >>> (32 - s))) + b; 77 | }; 78 | var GG = function (a, b, c, d, x, s, t) { 79 | var n = a + (b & d | c & ~d) + (x >>> 0) + t; 80 | return ((n << s) | (n >>> (32 - s))) + b; 81 | }; 82 | var HH = function (a, b, c, d, x, s, t) { 83 | var n = a + (b ^ c ^ d) + (x >>> 0) + t; 84 | return ((n << s) | (n >>> (32 - s))) + b; 85 | }; 86 | var II = function (a, b, c, d, x, s, t) { 87 | var n = a + (c ^ (b | ~d)) + (x >>> 0) + t; 88 | return ((n << s) | (n >>> (32 - s))) + b; 89 | }; 90 | 91 | for (var i = 0; i < m.length; i += 16) { 92 | 93 | var aa = a, 94 | bb = b, 95 | cc = c, 96 | dd = d; 97 | 98 | a = FF(a, b, c, d, m[i+ 0], 7, -680876936); 99 | d = FF(d, a, b, c, m[i+ 1], 12, -389564586); 100 | c = FF(c, d, a, b, m[i+ 2], 17, 606105819); 101 | b = FF(b, c, d, a, m[i+ 3], 22, -1044525330); 102 | a = FF(a, b, c, d, m[i+ 4], 7, -176418897); 103 | d = FF(d, a, b, c, m[i+ 5], 12, 1200080426); 104 | c = FF(c, d, a, b, m[i+ 6], 17, -1473231341); 105 | b = FF(b, c, d, a, m[i+ 7], 22, -45705983); 106 | a = FF(a, b, c, d, m[i+ 8], 7, 1770035416); 107 | d = FF(d, a, b, c, m[i+ 9], 12, -1958414417); 108 | c = FF(c, d, a, b, m[i+10], 17, -42063); 109 | b = FF(b, c, d, a, m[i+11], 22, -1990404162); 110 | a = FF(a, b, c, d, m[i+12], 7, 1804603682); 111 | d = FF(d, a, b, c, m[i+13], 12, -40341101); 112 | c = FF(c, d, a, b, m[i+14], 17, -1502002290); 113 | b = FF(b, c, d, a, m[i+15], 22, 1236535329); 114 | 115 | a = GG(a, b, c, d, m[i+ 1], 5, -165796510); 116 | d = GG(d, a, b, c, m[i+ 6], 9, -1069501632); 117 | c = GG(c, d, a, b, m[i+11], 14, 643717713); 118 | b = GG(b, c, d, a, m[i+ 0], 20, -373897302); 119 | a = GG(a, b, c, d, m[i+ 5], 5, -701558691); 120 | d = GG(d, a, b, c, m[i+10], 9, 38016083); 121 | c = GG(c, d, a, b, m[i+15], 14, -660478335); 122 | b = GG(b, c, d, a, m[i+ 4], 20, -405537848); 123 | a = GG(a, b, c, d, m[i+ 9], 5, 568446438); 124 | d = GG(d, a, b, c, m[i+14], 9, -1019803690); 125 | c = GG(c, d, a, b, m[i+ 3], 14, -187363961); 126 | b = GG(b, c, d, a, m[i+ 8], 20, 1163531501); 127 | a = GG(a, b, c, d, m[i+13], 5, -1444681467); 128 | d = GG(d, a, b, c, m[i+ 2], 9, -51403784); 129 | c = GG(c, d, a, b, m[i+ 7], 14, 1735328473); 130 | b = GG(b, c, d, a, m[i+12], 20, -1926607734); 131 | 132 | a = HH(a, b, c, d, m[i+ 5], 4, -378558); 133 | d = HH(d, a, b, c, m[i+ 8], 11, -2022574463); 134 | c = HH(c, d, a, b, m[i+11], 16, 1839030562); 135 | b = HH(b, c, d, a, m[i+14], 23, -35309556); 136 | a = HH(a, b, c, d, m[i+ 1], 4, -1530992060); 137 | d = HH(d, a, b, c, m[i+ 4], 11, 1272893353); 138 | c = HH(c, d, a, b, m[i+ 7], 16, -155497632); 139 | b = HH(b, c, d, a, m[i+10], 23, -1094730640); 140 | a = HH(a, b, c, d, m[i+13], 4, 681279174); 141 | d = HH(d, a, b, c, m[i+ 0], 11, -358537222); 142 | c = HH(c, d, a, b, m[i+ 3], 16, -722521979); 143 | b = HH(b, c, d, a, m[i+ 6], 23, 76029189); 144 | a = HH(a, b, c, d, m[i+ 9], 4, -640364487); 145 | d = HH(d, a, b, c, m[i+12], 11, -421815835); 146 | c = HH(c, d, a, b, m[i+15], 16, 530742520); 147 | b = HH(b, c, d, a, m[i+ 2], 23, -995338651); 148 | 149 | a = II(a, b, c, d, m[i+ 0], 6, -198630844); 150 | d = II(d, a, b, c, m[i+ 7], 10, 1126891415); 151 | c = II(c, d, a, b, m[i+14], 15, -1416354905); 152 | b = II(b, c, d, a, m[i+ 5], 21, -57434055); 153 | a = II(a, b, c, d, m[i+12], 6, 1700485571); 154 | d = II(d, a, b, c, m[i+ 3], 10, -1894986606); 155 | c = II(c, d, a, b, m[i+10], 15, -1051523); 156 | b = II(b, c, d, a, m[i+ 1], 21, -2054922799); 157 | a = II(a, b, c, d, m[i+ 8], 6, 1873313359); 158 | d = II(d, a, b, c, m[i+15], 10, -30611744); 159 | c = II(c, d, a, b, m[i+ 6], 15, -1560198380); 160 | b = II(b, c, d, a, m[i+13], 21, 1309151649); 161 | a = II(a, b, c, d, m[i+ 4], 6, -145523070); 162 | d = II(d, a, b, c, m[i+11], 10, -1120210379); 163 | c = II(c, d, a, b, m[i+ 2], 15, 718787259); 164 | b = II(b, c, d, a, m[i+ 9], 21, -343485551); 165 | 166 | a = (a + aa) >>> 0; 167 | b = (b + bb) >>> 0; 168 | c = (c + cc) >>> 0; 169 | d = (d + dd) >>> 0; 170 | 171 | } 172 | var rotl = function (n, b) { 173 | return (n << b) | (n >>> (32 - b)); 174 | } 175 | var endian = function (n) { 176 | 177 | // If number given, swap endian 178 | if (n.constructor == Number) { 179 | return rotl(n, 8) & 0x00FF00FF | 180 | rotl(n, 24) & 0xFF00FF00; 181 | } 182 | 183 | // Else, assume array and swap all items 184 | for (var i = 0; i < n.length; i++) 185 | n[i] = endian(n[i]); 186 | return n; 187 | 188 | } 189 | return new Uint32Array([a, b, c, d]); 190 | 191 | }; 192 | 193 | var md5_1 = MMHASH3.lib.x86.hash128_arraybuffer; 194 | 195 | var md5 = function (message,seed,offset,chunksize) { 196 | var m = message.slice(offset,offset+chunksize); 197 | return SipHash.lib.hash(m); 198 | } 199 | 200 | 201 | 202 | /** 203 | * Create a fast 16 bit hash of a 32bit number. Just using a simple mod 2^16 for this for now. 204 | * TO: Evaluate the distribution of adler32 to see if simple modulus is appropriate as a hashing function, or wheter 2^16 should be replaced with a prime 205 | */ 206 | function hash16(i) 207 | { 208 | // return num % 65536; 209 | var p = 1867; 210 | return ((i>>16)& 0xffff ^ ((i&0xffff) * p)) & 0xffff; 211 | } 212 | 213 | /** 214 | * Create a 32 bit checksum for the block, based on the adler-32 checksum, with M as 2^16 215 | * Used to feed the rollingChecksum function, so returns the broken out pieces that are required for fast calc (since there's no reason to do pointless 216 | * bit manipulation, we just cache the parts, like {a: ..., b: ..., checksum: ... }. 217 | * 218 | * Offset is the start, and end is the last byte for the block to be calculated. end - offset should equal the blockSize - 1 219 | * 220 | * Data should be a Uint8Array 221 | * 222 | * TDO: according to wikipedia, the zlib compression library has a much more efficient implementation of adler. To speed this up, it might be worth investigating whether that can be used here. 223 | */ 224 | function adler32(offset, end, data) 225 | { 226 | 227 | var a = 1, b= 0; 228 | 229 | //adjust the end to make sure we don't exceed the extents of the data. 230 | if(end >= data.length) 231 | end = data.length - 1; 232 | 233 | for(i=offset; i <= end; i++) 234 | { 235 | a += data[i]; 236 | b += a; 237 | a %= MOD_ADLER; 238 | b %= MOD_ADLER; 239 | } 240 | 241 | return {a: a>>>0, b: b>>>0, checksum: ((b << 16) | a) >>>0}; 242 | 243 | } 244 | 245 | /** 246 | * Performs a very fast rolling checksum for incremental searching using Tridgell's modification of adler-32 for rolling checksum 247 | * Returns an object suitable for use in additional calls to rollingChecksum, same as the adler32 function. This needs to be called with an offset of at least 1! 248 | * It is the responsibility of the called to make sure we don't exceed the bounds of the data, i.e. end MUST be less than data.length 249 | */ 250 | function rollingChecksum(adlerInfo, offset, end, data) 251 | { 252 | newdata = 0 253 | if(end < data.length) 254 | newdata = data[end] 255 | else 256 | end = data.length-1 257 | var temp = data[offset - 1]; //this is the first byte used in the previous iteration 258 | var a = ((adlerInfo.a - temp + newdata) % MOD_ADLER + MOD_ADLER)%MOD_ADLER; 259 | var b = ((adlerInfo.b - ((end - offset + 1) * temp) + a - 1) % MOD_ADLER + MOD_ADLER)%MOD_ADLER; 260 | return {a: a>>>0, b: b>>>0, checksum: ((b << 16) | a)>>>0}; 261 | } 262 | 263 | /** 264 | * This is a function born of annoyance. You can't create a Uint32Array at a non 4 byte boundary. So this is necessary to read 265 | * a 32bit int from an arbitrary location. Lame. 266 | * 267 | * BTW: This assumes everything to be little endian. 268 | */ 269 | function readInt32(uint8View, offset) 270 | { 271 | return (uint8View[offset] | uint8View[++offset] << 8 | uint8View[++offset] << 16 | uint8View[++offset] << 24) >>> 0; 272 | } 273 | 274 | /** 275 | * Create a document that contains all of the checksum information for each block in the destination data. Everything is little endian 276 | * Document structure: 277 | * First 4 bytes = block size 278 | * Next 4 bytes = number of blocks 279 | * 4 byes = byte length of file 280 | * Repeat for number of blocks: 281 | * 4 bytes, adler32 checksum 282 | * 16 bytes, md5 checksum 283 | * 284 | */ 285 | function createChecksumDocument(blockSize, data) 286 | { 287 | var filebytelength = data.byteLength; 288 | var numBlocks = Math.ceil(data.byteLength / blockSize); 289 | var i=0; 290 | var docLength = ( numBlocks * //the number of blocks times 291 | ( 4 + //the 4 bytes for the adler32 plus 292 | 16) + //the 16 bytes for the md5 293 | 4 + //plus 4 bytes for block size 294 | 4 + 4); //plus 4 bytes for the number of blocks 295 | 296 | var doc = new ArrayBuffer(docLength); 297 | var dataView = new Uint8Array(data); 298 | var bufferView = new Uint32Array(doc); 299 | var offset = 3; 300 | var chunkSize = 5; //each chunk is 4 bytes for adler32 and 16 bytes for md5. for Uint32Array view, this is 20 bytes, or 5 4-byte uints 301 | 302 | bufferView[0] = blockSize; 303 | bufferView[1] = numBlocks; 304 | bufferView[2] = filebytelength; 305 | 306 | //spin through the data and create checksums for each block 307 | for(i=0; i < numBlocks; i++) 308 | { 309 | var start = i * blockSize; 310 | var end = (i * blockSize) + blockSize; 311 | 312 | //calculate the adler32 checksum 313 | bufferView[offset] = adler32(start, end - 1, dataView).checksum; 314 | offset++; 315 | 316 | //calculate the full md5 checksum 317 | var chunkLength = blockSize; 318 | if((start + blockSize) > data.byteLength) 319 | chunkLength = data.byteLength - start; 320 | 321 | 322 | var md5sum = md5(dataView,0,start,chunkLength); 323 | // var md5sum = [0,0,0,0] 324 | for(var j=0; j < 4; j++) bufferView[offset++] = md5sum[j]; 325 | 326 | } 327 | 328 | return doc; 329 | 330 | } 331 | 332 | /** 333 | * Parse the checksum document into a hash table 334 | * 335 | * The hash table will have 2^16 entries. Each entry will point to an array that has the following strucutre: 336 | * [ 337 | * [ [blockIndex, adler32sum, md5sum],[blockIndex, adler32sum, md5sum],... ] 338 | * [ [blockIndex, adler32sum, md5sum],[blockIndex, adler32sum, md5sum],... ] 339 | * ... 340 | * ] 341 | */ 342 | function parseChecksumDocument(checksumDocument) 343 | { 344 | var fplist = []; 345 | var fp_chunk_size = 1*1024*1024; 346 | 347 | 348 | 349 | var ret = []; 350 | var linked_checksum = []; 351 | var i=0; 352 | var view = new Uint32Array(checksumDocument); 353 | var blockIndex = 1; //blockIndex is 1 based, not zero based 354 | var numBlocks = view[1]; 355 | var filebytelength = view[2]; 356 | var row; 357 | var hash; 358 | 359 | var fp_all_num = Math.ceil(filebytelength/fp_chunk_size); 360 | 361 | var offset = 0; 362 | 363 | //each chunk in the document is 20 bytes long. 32 bit view indexes 4 bytes, so increment by 5. 364 | for(i = 3; i <= view.length - 5; i += 5) 365 | { 366 | checksumInfo = [ 367 | blockIndex, //the index of the block 368 | view[i], //the adler32sum 369 | [view[i+1],view[i+2],view[i+3],view[i+4]] //the md5sum 370 | ]; 371 | hash = hash16(checksumInfo[1]); 372 | if(!ret[hash]) ret[hash] = []; 373 | ret[hash].push(checksumInfo); 374 | linked_checksum[blockIndex] = [view[i+1],view[i+2],view[i+3],view[i+4]]; 375 | blockIndex++; 376 | } 377 | 378 | if(numBlocks != (blockIndex - 1)) 379 | { 380 | throw "Error parsing checksum document. Document states the number of blocks is: " + numBlocks + " however, " + blockIndex - 1 + " blocks were discovered"; 381 | } 382 | return [filebytelength,ret,linked_checksum]; 383 | 384 | } 385 | 386 | /** 387 | * create match document that contains all matched block index 388 | * 4 bytes - blockSize 389 | * 4 bytes - num of matched blocks 390 | * for each matched block 391 | * 4 bytes - the index of the matched block 392 | * 4 bytes - the offset in the old file 393 | */ 394 | function createMatchDocument(checksumDocument, data){ 395 | function appendBuffer( buffer1, buffer2 ) { 396 | var tmp = new Uint8Array( buffer1.byteLength + buffer2.byteLength ); 397 | tmp.set( new Uint8Array( buffer1 ), 0 ); 398 | tmp.set( new Uint8Array( buffer2 ), buffer1.byteLength ); 399 | return tmp.buffer; 400 | } 401 | 402 | /** 403 | * First, check to see if there's a match on the 16 bit hash 404 | * Then, look through all the entries in the hashtable row for an adler 32 match. 405 | * Finally, do a strong md5 comparison 406 | */ 407 | function checkMatch(adlerInfo, hashTable,start,chunksize, block) 408 | { 409 | var hash = hash16(adlerInfo.checksum); 410 | // return false; 411 | if(!(hashTable[hash])) { 412 | // console.log('adler 32 missing'); 413 | return false; 414 | } 415 | // var testblock = block.slice(start,start+chunksize); 416 | 417 | var row = hashTable[hash]; 418 | this.hash16_coll += row.length - 1; 419 | var i=0; 420 | var matchedIndex=0; 421 | 422 | for(i=0; i data_len) 479 | { 480 | chunkSize = data_len - i; 481 | // adlerInfo=null; //need to reset this because the rolling checksum doesn't work correctly on a final non-aligned block 482 | } 483 | else 484 | chunkSize = blockSize; 485 | 486 | //locality optimized 487 | // prematching = false; 488 | if(prematching){ 489 | var predict_index = preindex+1; 490 | //predict_checksum 491 | var md5sum2 = linked_checksum[predict_index]; 492 | if(md5sum2){ 493 | // console.log("predict success!"); 494 | md5sum1 = md5(dataUint8,0,i,chunkSize); 495 | 496 | if( 497 | md5sum1[0] == md5sum2[0] && 498 | md5sum1[1] == md5sum2[1] && 499 | md5sum1[2] == md5sum2[2] && 500 | md5sum1[3] == md5sum2[3] 501 | ){ 502 | var matchedBlock = predict_index; 503 | matchedBlocksUint32[2*matchCount] = matchedBlock; 504 | matchedBlocksUint32[2*matchCount+1] = i; 505 | matchCount++; 506 | //check to see if we need more memory for the matched blocks 507 | if(2*matchCount >= matchedBlocksUint32.length) 508 | { 509 | matchedBlocks = appendBuffer(matchedBlocks, new ArrayBuffer(10000)); 510 | matchedBlocksUint32 = new Uint32Array(matchedBlocks); 511 | } 512 | 513 | lastMatchIndex = matchedBlock; 514 | i+=blockSize; 515 | if(i >= dataUint8.length -1 ) break; 516 | adlerInfo=null; 517 | prematching = true; 518 | preindex = matchedBlock; 519 | continue; 520 | } 521 | } 522 | } 523 | // resuse md5 checksum 524 | chunkSize = blockSize; 525 | 526 | if(adlerInfo){ 527 | adlerInfo = rollingChecksum(adlerInfo, i, i + chunkSize - 1, dataUint8); 528 | } 529 | 530 | else 531 | adlerInfo = adler32(i, i + chunkSize - 1, dataUint8); 532 | 533 | var matchedBlock = checkMatch(adlerInfo, hashTable,i,chunkSize,dataUint8); 534 | if(matchedBlock) 535 | { 536 | //if we have a match, do the following: 537 | //1) add the matched block index to our tracking buffer 538 | //2) add match block into match cache 539 | //3) jump forward blockSize bytes and continue 540 | matchedBlocksUint32[2*matchCount] = matchedBlock; 541 | matchedBlocksUint32[2*matchCount+1] = i; 542 | matchCount++; 543 | //check to see if we need more memory for the matched blocks 544 | if(2*matchCount >= matchedBlocksUint32.length) 545 | { 546 | matchedBlocks = appendBuffer(matchedBlocks, new ArrayBuffer(10000)); 547 | matchedBlocksUint32 = new Uint32Array(matchedBlocks); 548 | } 549 | 550 | lastMatchIndex = matchedBlock; 551 | i+=blockSize; 552 | if(i >= dataUint8.length -1 ) break; 553 | adlerInfo=null; 554 | prematching = true; 555 | preindex = matchedBlock; 556 | continue; 557 | }else{ 558 | content_traffic++; 559 | } 560 | prematching = false; 561 | if((i) >= dataUint8.length -1) break; 562 | i++; 563 | } //end for each byte in the data 564 | 565 | 566 | var test1 = performance.now(); 567 | console.log("########## match doc create time: " + (test1 - startTime)); 568 | 569 | console.log('########## content traffic is',content_traffic); 570 | var patchDocumentView32 = new Uint32Array(patchDocument); 571 | patchDocumentView32[0] = blockSize; 572 | patchDocumentView32[1] = matchCount; 573 | patchDocument = appendBuffer(patchDocument, matchedBlocks.slice(0,matchCount * 4 * 2)); 574 | 575 | 576 | return [patchDocument,filebytelength]; 577 | } 578 | 579 | /** 580 | * Apply the patch to the destination data, making it into a duplicate of the source data 581 | * Due to the inability to modify the size of ArrayBuffers once they have been allocated, this function 582 | * will return a new ArrayBuffer with the update file data. Note that this will consume a good bit of extra memory. 583 | */ 584 | function applyPatch(patchDocument, data) 585 | { 586 | function appendBlock( buffer, blockUint8) { 587 | var tmp = new Uint8Array( buffer.byteLength + blockUint8.length); 588 | tmp.set( new Uint8Array( buffer ), 0 ); 589 | tmp.set( blockUint8, buffer.byteLength ); 590 | return tmp.buffer; 591 | } 592 | 593 | var patchDocumentView32 = new Uint32Array(patchDocument,0,3); 594 | var blockSize = patchDocumentView32[0]; 595 | var patchCount = patchDocumentView32[1]; 596 | var matchCount = patchDocumentView32[2]; 597 | var matchedBlockView32 = new Uint32Array(patchDocument,12,matchCount); 598 | var i=0; 599 | var j=0; 600 | 601 | //first, let's deal with the simple case where we fully match. This is just an optimization for the unchanged file case. 602 | //to determine this, the number of matches must exactly equal ceil of data / blockSize, and num patches must be zero 603 | //additionally, the matched block indexes must start with 1 and be in order. This is to deal with the extreme edge case of a block being relocated 604 | //on an exact block boundary 605 | if(patchCount == 0) 606 | if(Math.ceil(data.byteLength / blockSize) == matchCount) 607 | for(i = 1; i <= matchCount; i++) 608 | if(matchedBlockView32[i-1] != i) { break; } 609 | if((i - 1) == matchCount) return data; //exact match 610 | 611 | //there was a modification. We need to construct the new document. 612 | //the way this works is as follows: 613 | //1) for each patch, get the last index of the matching block 614 | //2) loop through the matchedBlocks, appending blocks up to the index from step 1 615 | //3) append the patch at that point 616 | //4) after all patches have been applied, continue to loop through the matchedBlocks appending each one in order 617 | var offset = 12 + (matchCount * 4); //offset to the start of the patches 618 | var lastMatchingBlockIndex=0; 619 | var patchSize=0; 620 | var patchView8; 621 | var matchIndex=0; //the index into the matching blocks array 622 | var blockIndex=0; //the index of the block in the matching blocks array 623 | var ret = new ArrayBuffer(0); 624 | var patchDocumentView8 = new Uint8Array(patchDocument); 625 | var chunkSize=0; 626 | for(i=0; i< patchCount; i++) 627 | { 628 | lastMatchingBlockIndex = readInt32(patchDocumentView8,offset); 629 | patchSize = readInt32(patchDocumentView8,offset + 4); 630 | patchView8 = new Uint8Array(patchDocument, offset + 8, patchSize); 631 | offset = offset + 8 + patchSize; 632 | 633 | for(;matchIndex < matchedBlockView32.length; matchIndex++) 634 | { 635 | blockIndex = matchedBlockView32[matchIndex]; 636 | if(blockIndex > lastMatchingBlockIndex) break; 637 | if((blockIndex * blockSize) > data.byteLength) 638 | chunkSize = data.byteLength % blockSize; 639 | else chunkSize = blockSize; 640 | ret = appendBlock(ret, new Uint8Array(data, (blockIndex-1) * blockSize, chunkSize)); 641 | } 642 | 643 | ret = appendBlock(ret, patchView8); 644 | } 645 | 646 | //we're done with all the patches, add the remaining blocks 647 | for(;matchIndex < matchedBlockView32.length; matchIndex++) 648 | { 649 | blockIndex = matchedBlockView32[matchIndex]; 650 | if((blockIndex * blockSize) > data.byteLength) 651 | chunkSize = data.byteLength % blockSize; 652 | else chunkSize = blockSize; 653 | ret = appendBlock(ret, new Uint8Array(data, (blockIndex-1) * blockSize, chunkSize)); 654 | } 655 | 656 | return ret; 657 | } 658 | /* 659 | * parse match document 660 | * use hash table to store match document: because js object is hash table 661 | */ 662 | function parseMatchDocument(matchDocument) 663 | { 664 | var ret = []; 665 | var view = new Uint32Array(matchDocument); 666 | var blockIndex = 1; //blockIndex is 1 based, not zero based 667 | var numBlocks = view[1]; 668 | var row; 669 | var hash; 670 | 671 | //each chunk in the document is 20 bytes long. 32 bit view indexes 4 bytes, so increment by 5. 672 | for(i = 2; i < view.length;) 673 | { 674 | match_blockIndex = view[i++]; 675 | match_blockoffset = view[i++]; 676 | ret[match_blockIndex] = match_blockoffset; 677 | } 678 | return ret; 679 | } 680 | 681 | /******** Public API ***********/ 682 | this.createChecksumDocument = createChecksumDocument; 683 | // this.createPatchDocument = createPatchDocument; 684 | this.createMatchDocument = createMatchDocument; 685 | this.parseMatchDocument = parseMatchDocument; 686 | this.applyPatch = applyPatch; 687 | this.hash16_coll = 0; 688 | this.util = {md5: md5, adler32: adler32, rollingChecksum: rollingChecksum, readInt32: readInt32}; //mostly exposing these for the purposes of unit tests, but hey, if they are useful to someone, have at it! 689 | }; 690 | 691 | 692 | if(((typeof require) != "undefined") && 693 | ((typeof module) != "undefined") && 694 | ((typeof module.exports) != "undefined")) 695 | module.exports = BSync; 696 | 697 | 698 | -------------------------------------------------------------------------------- /public/js/socket.io-1.4.5.js: -------------------------------------------------------------------------------- 1 | (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.io=f()}})(function(){var define,module,exports;return function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o0){this.extraHeaders=opts.extraHeaders}}this.open()}Socket.priorWebsocketSuccess=false;Emitter(Socket.prototype);Socket.protocol=parser.protocol;Socket.Socket=Socket;Socket.Transport=_dereq_("./transport");Socket.transports=_dereq_("./transports");Socket.parser=_dereq_("engine.io-parser");Socket.prototype.createTransport=function(name){debug('creating transport "%s"',name);var query=clone(this.query);query.EIO=parser.protocol;query.transport=name;if(this.id)query.sid=this.id;var transport=new transports[name]({agent:this.agent,hostname:this.hostname,port:this.port,secure:this.secure,path:this.path,query:query,forceJSONP:this.forceJSONP,jsonp:this.jsonp,forceBase64:this.forceBase64,enablesXDR:this.enablesXDR,timestampRequests:this.timestampRequests,timestampParam:this.timestampParam,policyPort:this.policyPort,socket:this,pfx:this.pfx,key:this.key,passphrase:this.passphrase,cert:this.cert,ca:this.ca,ciphers:this.ciphers,rejectUnauthorized:this.rejectUnauthorized,perMessageDeflate:this.perMessageDeflate,extraHeaders:this.extraHeaders});return transport};function clone(obj){var o={};for(var i in obj){if(obj.hasOwnProperty(i)){o[i]=obj[i]}}return o}Socket.prototype.open=function(){var transport;if(this.rememberUpgrade&&Socket.priorWebsocketSuccess&&this.transports.indexOf("websocket")!=-1){transport="websocket"}else if(0===this.transports.length){var self=this;setTimeout(function(){self.emit("error","No transports available")},0);return}else{transport=this.transports[0]}this.readyState="opening";try{transport=this.createTransport(transport)}catch(e){this.transports.shift();this.open();return}transport.open();this.setTransport(transport)};Socket.prototype.setTransport=function(transport){debug("setting transport %s",transport.name);var self=this;if(this.transport){debug("clearing existing transport %s",this.transport.name);this.transport.removeAllListeners()}this.transport=transport;transport.on("drain",function(){self.onDrain()}).on("packet",function(packet){self.onPacket(packet)}).on("error",function(e){self.onError(e)}).on("close",function(){self.onClose("transport close")})};Socket.prototype.probe=function(name){debug('probing transport "%s"',name);var transport=this.createTransport(name,{probe:1}),failed=false,self=this;Socket.priorWebsocketSuccess=false;function onTransportOpen(){if(self.onlyBinaryUpgrades){var upgradeLosesBinary=!this.supportsBinary&&self.transport.supportsBinary;failed=failed||upgradeLosesBinary}if(failed)return;debug('probe transport "%s" opened',name);transport.send([{type:"ping",data:"probe"}]);transport.once("packet",function(msg){if(failed)return;if("pong"==msg.type&&"probe"==msg.data){debug('probe transport "%s" pong',name);self.upgrading=true;self.emit("upgrading",transport);if(!transport)return;Socket.priorWebsocketSuccess="websocket"==transport.name;debug('pausing current transport "%s"',self.transport.name);self.transport.pause(function(){if(failed)return;if("closed"==self.readyState)return;debug("changing transport and sending upgrade packet");cleanup();self.setTransport(transport);transport.send([{type:"upgrade"}]);self.emit("upgrade",transport);transport=null;self.upgrading=false;self.flush()})}else{debug('probe transport "%s" failed',name);var err=new Error("probe error");err.transport=transport.name;self.emit("upgradeError",err)}})}function freezeTransport(){if(failed)return;failed=true;cleanup();transport.close();transport=null}function onerror(err){var error=new Error("probe error: "+err);error.transport=transport.name;freezeTransport();debug('probe transport "%s" failed because of error: %s',name,err);self.emit("upgradeError",error)}function onTransportClose(){onerror("transport closed")}function onclose(){onerror("socket closed")}function onupgrade(to){if(transport&&to.name!=transport.name){debug('"%s" works - aborting "%s"',to.name,transport.name);freezeTransport()}}function cleanup(){transport.removeListener("open",onTransportOpen);transport.removeListener("error",onerror);transport.removeListener("close",onTransportClose);self.removeListener("close",onclose);self.removeListener("upgrading",onupgrade)}transport.once("open",onTransportOpen);transport.once("error",onerror);transport.once("close",onTransportClose);this.once("close",onclose);this.once("upgrading",onupgrade);transport.open()};Socket.prototype.onOpen=function(){debug("socket open");this.readyState="open";Socket.priorWebsocketSuccess="websocket"==this.transport.name;this.emit("open");this.flush();if("open"==this.readyState&&this.upgrade&&this.transport.pause){debug("starting upgrade probes");for(var i=0,l=this.upgrades.length;i';iframe=document.createElement(html)}catch(e){iframe=document.createElement("iframe");iframe.name=self.iframeId;iframe.src="javascript:0"}iframe.id=self.iframeId;self.form.appendChild(iframe);self.iframe=iframe}initIframe();data=data.replace(rEscapedNewline,"\\\n");this.area.value=data.replace(rNewline,"\\n");try{this.form.submit()}catch(e){}if(this.iframe.attachEvent){this.iframe.onreadystatechange=function(){if(self.iframe.readyState=="complete"){complete()}}}else{this.iframe.onload=complete}}}).call(this,typeof self!=="undefined"?self:typeof window!=="undefined"?window:typeof global!=="undefined"?global:{})},{"./polling":8,"component-inherit":16}],7:[function(_dereq_,module,exports){(function(global){var XMLHttpRequest=_dereq_("xmlhttprequest-ssl");var Polling=_dereq_("./polling");var Emitter=_dereq_("component-emitter");var inherit=_dereq_("component-inherit");var debug=_dereq_("debug")("engine.io-client:polling-xhr");module.exports=XHR;module.exports.Request=Request;function empty(){}function XHR(opts){Polling.call(this,opts);if(global.location){var isSSL="https:"==location.protocol;var port=location.port;if(!port){port=isSSL?443:80}this.xd=opts.hostname!=global.location.hostname||port!=opts.port;this.xs=opts.secure!=isSSL}else{this.extraHeaders=opts.extraHeaders}}inherit(XHR,Polling);XHR.prototype.supportsBinary=true;XHR.prototype.request=function(opts){opts=opts||{};opts.uri=this.uri();opts.xd=this.xd;opts.xs=this.xs;opts.agent=this.agent||false;opts.supportsBinary=this.supportsBinary;opts.enablesXDR=this.enablesXDR;opts.pfx=this.pfx;opts.key=this.key;opts.passphrase=this.passphrase;opts.cert=this.cert;opts.ca=this.ca;opts.ciphers=this.ciphers;opts.rejectUnauthorized=this.rejectUnauthorized;opts.extraHeaders=this.extraHeaders;return new Request(opts)};XHR.prototype.doWrite=function(data,fn){var isBinary=typeof data!=="string"&&data!==undefined;var req=this.request({method:"POST",data:data,isBinary:isBinary});var self=this;req.on("success",fn);req.on("error",function(err){self.onError("xhr post error",err)});this.sendXhr=req};XHR.prototype.doPoll=function(){debug("xhr poll");var req=this.request();var self=this;req.on("data",function(data){self.onData(data)});req.on("error",function(err){self.onError("xhr poll error",err)});this.pollXhr=req};function Request(opts){this.method=opts.method||"GET";this.uri=opts.uri;this.xd=!!opts.xd;this.xs=!!opts.xs;this.async=false!==opts.async;this.data=undefined!=opts.data?opts.data:null;this.agent=opts.agent;this.isBinary=opts.isBinary;this.supportsBinary=opts.supportsBinary;this.enablesXDR=opts.enablesXDR;this.pfx=opts.pfx;this.key=opts.key;this.passphrase=opts.passphrase;this.cert=opts.cert;this.ca=opts.ca;this.ciphers=opts.ciphers;this.rejectUnauthorized=opts.rejectUnauthorized;this.extraHeaders=opts.extraHeaders;this.create()}Emitter(Request.prototype);Request.prototype.create=function(){var opts={agent:this.agent,xdomain:this.xd,xscheme:this.xs,enablesXDR:this.enablesXDR};opts.pfx=this.pfx;opts.key=this.key;opts.passphrase=this.passphrase;opts.cert=this.cert;opts.ca=this.ca;opts.ciphers=this.ciphers;opts.rejectUnauthorized=this.rejectUnauthorized;var xhr=this.xhr=new XMLHttpRequest(opts);var self=this;try{debug("xhr open %s: %s",this.method,this.uri);xhr.open(this.method,this.uri,this.async);try{if(this.extraHeaders){xhr.setDisableHeaderCheck(true);for(var i in this.extraHeaders){if(this.extraHeaders.hasOwnProperty(i)){xhr.setRequestHeader(i,this.extraHeaders[i])}}}}catch(e){}if(this.supportsBinary){xhr.responseType="arraybuffer"}if("POST"==this.method){try{if(this.isBinary){xhr.setRequestHeader("Content-type","application/octet-stream")}else{xhr.setRequestHeader("Content-type","text/plain;charset=UTF-8")}}catch(e){}}if("withCredentials"in xhr){xhr.withCredentials=true}if(this.hasXDR()){xhr.onload=function(){self.onLoad()};xhr.onerror=function(){self.onError(xhr.responseText)}}else{xhr.onreadystatechange=function(){if(4!=xhr.readyState)return;if(200==xhr.status||1223==xhr.status){self.onLoad()}else{setTimeout(function(){self.onError(xhr.status)},0)}}}debug("xhr data %s",this.data);xhr.send(this.data)}catch(e){setTimeout(function(){self.onError(e)},0);return}if(global.document){this.index=Request.requestsCount++;Request.requests[this.index]=this}};Request.prototype.onSuccess=function(){this.emit("success");this.cleanup()};Request.prototype.onData=function(data){this.emit("data",data);this.onSuccess()};Request.prototype.onError=function(err){this.emit("error",err);this.cleanup(true)};Request.prototype.cleanup=function(fromError){if("undefined"==typeof this.xhr||null===this.xhr){return}if(this.hasXDR()){this.xhr.onload=this.xhr.onerror=empty}else{this.xhr.onreadystatechange=empty}if(fromError){try{this.xhr.abort()}catch(e){}}if(global.document){delete Request.requests[this.index]}this.xhr=null};Request.prototype.onLoad=function(){var data;try{var contentType;try{contentType=this.xhr.getResponseHeader("Content-Type").split(";")[0]}catch(e){}if(contentType==="application/octet-stream"){data=this.xhr.response}else{if(!this.supportsBinary){data=this.xhr.responseText}else{try{data=String.fromCharCode.apply(null,new Uint8Array(this.xhr.response))}catch(e){var ui8Arr=new Uint8Array(this.xhr.response);var dataArray=[];for(var idx=0,length=ui8Arr.length;idxbytes){end=bytes}if(start>=bytes||start>=end||bytes===0){return new ArrayBuffer(0)}var abv=new Uint8Array(arraybuffer);var result=new Uint8Array(end-start);for(var i=start,ii=0;i>2]; 2 | base64+=chars[(bytes[i]&3)<<4|bytes[i+1]>>4];base64+=chars[(bytes[i+1]&15)<<2|bytes[i+2]>>6];base64+=chars[bytes[i+2]&63]}if(len%3===2){base64=base64.substring(0,base64.length-1)+"="}else if(len%3===1){base64=base64.substring(0,base64.length-2)+"=="}return base64};exports.decode=function(base64){var bufferLength=base64.length*.75,len=base64.length,i,p=0,encoded1,encoded2,encoded3,encoded4;if(base64[base64.length-1]==="="){bufferLength--;if(base64[base64.length-2]==="="){bufferLength--}}var arraybuffer=new ArrayBuffer(bufferLength),bytes=new Uint8Array(arraybuffer);for(i=0;i>4;bytes[p++]=(encoded2&15)<<4|encoded3>>2;bytes[p++]=(encoded3&3)<<6|encoded4&63}return arraybuffer}})("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")},{}],14:[function(_dereq_,module,exports){(function(global){var BlobBuilder=global.BlobBuilder||global.WebKitBlobBuilder||global.MSBlobBuilder||global.MozBlobBuilder;var blobSupported=function(){try{var a=new Blob(["hi"]);return a.size===2}catch(e){return false}}();var blobSupportsArrayBufferView=blobSupported&&function(){try{var b=new Blob([new Uint8Array([1,2])]);return b.size===2}catch(e){return false}}();var blobBuilderSupported=BlobBuilder&&BlobBuilder.prototype.append&&BlobBuilder.prototype.getBlob;function mapArrayBufferViews(ary){for(var i=0;i=31}exports.formatters.j=function(v){return JSON.stringify(v)};function formatArgs(){var args=arguments;var useColors=this.useColors;args[0]=(useColors?"%c":"")+this.namespace+(useColors?" %c":" ")+args[0]+(useColors?"%c ":" ")+"+"+exports.humanize(this.diff);if(!useColors)return args;var c="color: "+this.color;args=[args[0],c,"color: inherit"].concat(Array.prototype.slice.call(args,1));var index=0;var lastC=0;args[0].replace(/%[a-z%]/g,function(match){if("%%"===match)return;index++;if("%c"===match){lastC=index}});args.splice(lastC,0,c);return args}function log(){return"object"===typeof console&&console.log&&Function.prototype.apply.call(console.log,console,arguments)}function save(namespaces){try{if(null==namespaces){exports.storage.removeItem("debug")}else{exports.storage.debug=namespaces}}catch(e){}}function load(){var r;try{r=exports.storage.debug}catch(e){}return r}exports.enable(load());function localstorage(){try{return window.localStorage}catch(e){}}},{"./debug":18}],18:[function(_dereq_,module,exports){exports=module.exports=debug;exports.coerce=coerce;exports.disable=disable;exports.enable=enable;exports.enabled=enabled;exports.humanize=_dereq_("ms");exports.names=[];exports.skips=[];exports.formatters={};var prevColor=0;var prevTime;function selectColor(){return exports.colors[prevColor++%exports.colors.length]}function debug(namespace){function disabled(){}disabled.enabled=false;function enabled(){var self=enabled;var curr=+new Date;var ms=curr-(prevTime||curr);self.diff=ms;self.prev=prevTime;self.curr=curr;prevTime=curr;if(null==self.useColors)self.useColors=exports.useColors();if(null==self.color&&self.useColors)self.color=selectColor();var args=Array.prototype.slice.call(arguments);args[0]=exports.coerce(args[0]);if("string"!==typeof args[0]){args=["%o"].concat(args)}var index=0;args[0]=args[0].replace(/%([a-z%])/g,function(match,format){if(match==="%%")return match;index++;var formatter=exports.formatters[format];if("function"===typeof formatter){var val=args[index];match=formatter.call(self,val);args.splice(index,1);index--}return match});if("function"===typeof exports.formatArgs){args=exports.formatArgs.apply(self,args)}var logFn=enabled.log||exports.log||console.log.bind(console);logFn.apply(self,args)}enabled.enabled=true;var fn=exports.enabled(namespace)?enabled:disabled;fn.namespace=namespace;return fn}function enable(namespaces){exports.save(namespaces);var split=(namespaces||"").split(/[\s,]+/);var len=split.length;for(var i=0;i1){return{type:packetslist[type],data:data.substring(1)}}else{return{type:packetslist[type]}}}var asArray=new Uint8Array(data);var type=asArray[0];var rest=sliceBuffer(data,1);if(Blob&&binaryType==="blob"){rest=new Blob([rest])}return{type:packetslist[type],data:rest}};exports.decodeBase64Packet=function(msg,binaryType){var type=packetslist[msg.charAt(0)];if(!global.ArrayBuffer){return{type:type,data:{base64:true,data:msg.substr(1)}}}var data=base64encoder.decode(msg.substr(1));if(binaryType==="blob"&&Blob){data=new Blob([data])}return{type:type,data:data}};exports.encodePayload=function(packets,supportsBinary,callback){if(typeof supportsBinary=="function"){callback=supportsBinary;supportsBinary=null}var isBinary=hasBinary(packets);if(supportsBinary&&isBinary){if(Blob&&!dontSendBlobs){return exports.encodePayloadAsBlob(packets,callback)}return exports.encodePayloadAsArrayBuffer(packets,callback)}if(!packets.length){return callback("0:")}function setLengthHeader(message){return message.length+":"+message}function encodeOne(packet,doneCallback){exports.encodePacket(packet,!isBinary?false:supportsBinary,true,function(message){doneCallback(null,setLengthHeader(message))})}map(packets,encodeOne,function(err,results){return callback(results.join(""))})};function map(ary,each,done){var result=new Array(ary.length);var next=after(ary.length,done);var eachWithIndex=function(i,el,cb){each(el,function(error,msg){result[i]=msg;cb(error,result)})};for(var i=0;i0){var tailArray=new Uint8Array(bufferTail);var isString=tailArray[0]===0;var msgLength="";for(var i=1;;i++){if(tailArray[i]==255)break;if(msgLength.length>310){numberTooLong=true;break}msgLength+=tailArray[i]}if(numberTooLong)return callback(err,0,1);bufferTail=sliceBuffer(bufferTail,2+msgLength.length);msgLength=parseInt(msgLength);var msg=sliceBuffer(bufferTail,0,msgLength);if(isString){try{msg=String.fromCharCode.apply(null,new Uint8Array(msg))}catch(e){var typed=new Uint8Array(msg);msg="";for(var i=0;i1e4)return;var match=/^((?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec(str);if(!match)return;var n=parseFloat(match[1]);var type=(match[2]||"ms").toLowerCase();switch(type){case"years":case"year":case"yrs":case"yr":case"y":return n*y;case"days":case"day":case"d":return n*d;case"hours":case"hour":case"hrs":case"hr":case"h":return n*h;case"minutes":case"minute":case"mins":case"min":case"m":return n*m;case"seconds":case"second":case"secs":case"sec":case"s":return n*s;case"milliseconds":case"millisecond":case"msecs":case"msec":case"ms":return n}}function short(ms){if(ms>=d)return Math.round(ms/d)+"d";if(ms>=h)return Math.round(ms/h)+"h";if(ms>=m)return Math.round(ms/m)+"m";if(ms>=s)return Math.round(ms/s)+"s";return ms+"ms"}function long(ms){return plural(ms,d,"day")||plural(ms,h,"hour")||plural(ms,m,"minute")||plural(ms,s,"second")||ms+" ms"}function plural(ms,n,name){if(ms=55296&&value<=56319&&counter65535){value-=65536;output+=stringFromCharCode(value>>>10&1023|55296);value=56320|value&1023}output+=stringFromCharCode(value)}return output}function checkScalarValue(codePoint){if(codePoint>=55296&&codePoint<=57343){throw Error("Lone surrogate U+"+codePoint.toString(16).toUpperCase()+" is not a scalar value")}}function createByte(codePoint,shift){return stringFromCharCode(codePoint>>shift&63|128)}function encodeCodePoint(codePoint){if((codePoint&4294967168)==0){return stringFromCharCode(codePoint)}var symbol="";if((codePoint&4294965248)==0){symbol=stringFromCharCode(codePoint>>6&31|192)}else if((codePoint&4294901760)==0){checkScalarValue(codePoint);symbol=stringFromCharCode(codePoint>>12&15|224);symbol+=createByte(codePoint,6)}else if((codePoint&4292870144)==0){symbol=stringFromCharCode(codePoint>>18&7|240);symbol+=createByte(codePoint,12);symbol+=createByte(codePoint,6)}symbol+=stringFromCharCode(codePoint&63|128);return symbol}function utf8encode(string){var codePoints=ucs2decode(string);var length=codePoints.length;var index=-1;var codePoint;var byteString="";while(++index=byteCount){throw Error("Invalid byte index")}var continuationByte=byteArray[byteIndex]&255;byteIndex++;if((continuationByte&192)==128){return continuationByte&63}throw Error("Invalid continuation byte")}function decodeSymbol(){var byte1;var byte2;var byte3;var byte4;var codePoint;if(byteIndex>byteCount){throw Error("Invalid byte index")}if(byteIndex==byteCount){return false}byte1=byteArray[byteIndex]&255;byteIndex++;if((byte1&128)==0){return byte1}if((byte1&224)==192){var byte2=readContinuationByte();codePoint=(byte1&31)<<6|byte2;if(codePoint>=128){return codePoint}else{throw Error("Invalid continuation byte")}}if((byte1&240)==224){byte2=readContinuationByte();byte3=readContinuationByte();codePoint=(byte1&15)<<12|byte2<<6|byte3;if(codePoint>=2048){checkScalarValue(codePoint);return codePoint}else{throw Error("Invalid continuation byte")}}if((byte1&248)==240){byte2=readContinuationByte();byte3=readContinuationByte();byte4=readContinuationByte();codePoint=(byte1&15)<<18|byte2<<12|byte3<<6|byte4;if(codePoint>=65536&&codePoint<=1114111){return codePoint}}throw Error("Invalid UTF-8 detected")}var byteArray;var byteCount;var byteIndex;function utf8decode(byteString){byteArray=ucs2decode(byteString);byteCount=byteArray.length;byteIndex=0;var codePoints=[];var tmp;while((tmp=decodeSymbol())!==false){codePoints.push(tmp)}return ucs2encode(codePoints)}var utf8={version:"2.0.0",encode:utf8encode,decode:utf8decode};if(typeof define=="function"&&typeof define.amd=="object"&&define.amd){define(function(){return utf8})}else if(freeExports&&!freeExports.nodeType){if(freeModule){freeModule.exports=utf8}else{var object={};var hasOwnProperty=object.hasOwnProperty;for(var key in utf8){hasOwnProperty.call(utf8,key)&&(freeExports[key]=utf8[key])}}}else{root.utf8=utf8}})(this)}).call(this,typeof self!=="undefined"?self:typeof window!=="undefined"?window:typeof global!=="undefined"?global:{})},{}],30:[function(_dereq_,module,exports){"use strict";var alphabet="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_".split(""),length=64,map={},seed=0,i=0,prev;function encode(num){var encoded="";do{encoded=alphabet[num%length]+encoded;num=Math.floor(num/length)}while(num>0);return encoded}function decode(str){var decoded=0;for(i=0;i0&&!this.encoding){var pack=this.packetBuffer.shift();this.packet(pack)}};Manager.prototype.cleanup=function(){debug("cleanup");var sub;while(sub=this.subs.shift())sub.destroy();this.packetBuffer=[];this.encoding=false;this.lastPing=null;this.decoder.destroy()};Manager.prototype.close=Manager.prototype.disconnect=function(){debug("disconnect");this.skipReconnect=true;this.reconnecting=false;if("opening"==this.readyState){this.cleanup()}this.backoff.reset();this.readyState="closed";if(this.engine)this.engine.close()};Manager.prototype.onclose=function(reason){debug("onclose");this.cleanup();this.backoff.reset();this.readyState="closed";this.emit("close",reason);if(this._reconnection&&!this.skipReconnect){this.reconnect()}};Manager.prototype.reconnect=function(){if(this.reconnecting||this.skipReconnect)return this;var self=this;if(this.backoff.attempts>=this._reconnectionAttempts){debug("reconnect failed");this.backoff.reset();this.emitAll("reconnect_failed");this.reconnecting=false}else{var delay=this.backoff.duration();debug("will wait %dms before reconnect attempt",delay);this.reconnecting=true;var timer=setTimeout(function(){if(self.skipReconnect)return;debug("attempting reconnect");self.emitAll("reconnect_attempt",self.backoff.attempts);self.emitAll("reconnecting",self.backoff.attempts);if(self.skipReconnect)return;self.open(function(err){if(err){debug("reconnect attempt error");self.reconnecting=false;self.reconnect();self.emitAll("reconnect_error",err.data)}else{debug("reconnect success");self.onreconnect()}})},delay);this.subs.push({destroy:function(){clearTimeout(timer)}})}};Manager.prototype.onreconnect=function(){var attempt=this.backoff.attempts;this.reconnecting=false;this.backoff.reset();this.updateSocketIds();this.emitAll("reconnect",attempt)}},{"./on":33,"./socket":34,backo2:36,"component-bind":37,"component-emitter":38,debug:39,"engine.io-client":1,indexof:42,"socket.io-parser":47}],33:[function(_dereq_,module,exports){module.exports=on;function on(obj,ev,fn){obj.on(ev,fn);return{destroy:function(){obj.removeListener(ev,fn)}}}},{}],34:[function(_dereq_,module,exports){var parser=_dereq_("socket.io-parser");var Emitter=_dereq_("component-emitter");var toArray=_dereq_("to-array");var on=_dereq_("./on");var bind=_dereq_("component-bind");var debug=_dereq_("debug")("socket.io-client:socket");var hasBin=_dereq_("has-binary");module.exports=exports=Socket;var events={connect:1,connect_error:1,connect_timeout:1,connecting:1,disconnect:1,error:1,reconnect:1,reconnect_attempt:1,reconnect_failed:1,reconnect_error:1,reconnecting:1,ping:1,pong:1};var emit=Emitter.prototype.emit;function Socket(io,nsp){this.io=io;this.nsp=nsp;this.json=this;this.ids=0;this.acks={};this.receiveBuffer=[];this.sendBuffer=[];this.connected=false;this.disconnected=true;if(this.io.autoConnect)this.open()}Emitter(Socket.prototype);Socket.prototype.subEvents=function(){if(this.subs)return;var io=this.io;this.subs=[on(io,"open",bind(this,"onopen")),on(io,"packet",bind(this,"onpacket")),on(io,"close",bind(this,"onclose"))]};Socket.prototype.open=Socket.prototype.connect=function(){if(this.connected)return this;this.subEvents();this.io.open();if("open"==this.io.readyState)this.onopen();this.emit("connecting");return this};Socket.prototype.send=function(){var args=toArray(arguments);args.unshift("message");this.emit.apply(this,args);return this};Socket.prototype.emit=function(ev){if(events.hasOwnProperty(ev)){emit.apply(this,arguments);return this}var args=toArray(arguments);var parserType=parser.EVENT;if(hasBin(args)){parserType=parser.BINARY_EVENT}var packet={type:parserType,data:args};packet.options={};packet.options.compress=!this.flags||false!==this.flags.compress;if("function"==typeof args[args.length-1]){debug("emitting packet with ack id %d",this.ids);this.acks[this.ids]=args.pop();packet.id=this.ids++}if(this.connected){this.packet(packet)}else{this.sendBuffer.push(packet)}delete this.flags;return this};Socket.prototype.packet=function(packet){packet.nsp=this.nsp;this.io.packet(packet)};Socket.prototype.onopen=function(){debug("transport is open - connecting");if("/"!=this.nsp){this.packet({type:parser.CONNECT})}};Socket.prototype.onclose=function(reason){debug("close (%s)",reason);this.connected=false;this.disconnected=true;delete this.id;this.emit("disconnect",reason)};Socket.prototype.onpacket=function(packet){if(packet.nsp!=this.nsp)return;switch(packet.type){case parser.CONNECT:this.onconnect();break;case parser.EVENT:this.onevent(packet);break;case parser.BINARY_EVENT:this.onevent(packet);break;case parser.ACK:this.onack(packet);break;case parser.BINARY_ACK:this.onack(packet);break;case parser.DISCONNECT:this.ondisconnect();break;case parser.ERROR:this.emit("error",packet.data);break}};Socket.prototype.onevent=function(packet){var args=packet.data||[];debug("emitting event %j",args);if(null!=packet.id){debug("attaching ack callback to event");args.push(this.ack(packet.id))}if(this.connected){emit.apply(this,args)}else{this.receiveBuffer.push(args)}};Socket.prototype.ack=function(id){var self=this;var sent=false;return function(){if(sent)return;sent=true;var args=toArray(arguments);debug("sending ack %j",args);var type=hasBin(args)?parser.BINARY_ACK:parser.ACK;self.packet({type:type,id:id,data:args})}};Socket.prototype.onack=function(packet){var ack=this.acks[packet.id];if("function"==typeof ack){debug("calling ack %s with %j",packet.id,packet.data);ack.apply(this,packet.data);delete this.acks[packet.id]}else{debug("bad ack %s",packet.id)}};Socket.prototype.onconnect=function(){this.connected=true;this.disconnected=false;this.emit("connect");this.emitBuffered()};Socket.prototype.emitBuffered=function(){var i;for(i=0;i0&&opts.jitter<=1?opts.jitter:0;this.attempts=0}Backoff.prototype.duration=function(){var ms=this.ms*Math.pow(this.factor,this.attempts++);if(this.jitter){var rand=Math.random();var deviation=Math.floor(rand*this.jitter*ms);ms=(Math.floor(rand*10)&1)==0?ms-deviation:ms+deviation}return Math.min(ms,this.max)|0};Backoff.prototype.reset=function(){this.attempts=0};Backoff.prototype.setMin=function(min){this.ms=min};Backoff.prototype.setMax=function(max){this.max=max};Backoff.prototype.setJitter=function(jitter){this.jitter=jitter}},{}],37:[function(_dereq_,module,exports){var slice=[].slice;module.exports=function(obj,fn){if("string"==typeof fn)fn=obj[fn];if("function"!=typeof fn)throw new Error("bind() requires a function");var args=slice.call(arguments,2);return function(){return fn.apply(obj,args.concat(slice.call(arguments)))}}},{}],38:[function(_dereq_,module,exports){module.exports=Emitter;function Emitter(obj){if(obj)return mixin(obj)}function mixin(obj){for(var key in Emitter.prototype){obj[key]=Emitter.prototype[key]}return obj}Emitter.prototype.on=Emitter.prototype.addEventListener=function(event,fn){this._callbacks=this._callbacks||{};(this._callbacks["$"+event]=this._callbacks["$"+event]||[]).push(fn);return this};Emitter.prototype.once=function(event,fn){function on(){this.off(event,on);fn.apply(this,arguments)}on.fn=fn;this.on(event,on);return this};Emitter.prototype.off=Emitter.prototype.removeListener=Emitter.prototype.removeAllListeners=Emitter.prototype.removeEventListener=function(event,fn){this._callbacks=this._callbacks||{};if(0==arguments.length){this._callbacks={};return this}var callbacks=this._callbacks["$"+event];if(!callbacks)return this;if(1==arguments.length){delete this._callbacks["$"+event];return this}var cb;for(var i=0;i1)))/4)-floor((year-1901+month)/100)+floor((year-1601+month)/400)}}if(!(isProperty=objectProto.hasOwnProperty)){isProperty=function(property){var members={},constructor;if((members.__proto__=null,members.__proto__={toString:1},members).toString!=getClass){isProperty=function(property){var original=this.__proto__,result=property in(this.__proto__=null,this);this.__proto__=original;return result}}else{constructor=members.constructor;isProperty=function(property){var parent=(this.constructor||constructor).prototype;return property in this&&!(property in parent&&this[property]===parent[property])}}members=null;return isProperty.call(this,property)}}forEach=function(object,callback){var size=0,Properties,members,property;(Properties=function(){this.valueOf=0}).prototype.valueOf=0;members=new Properties;for(property in members){if(isProperty.call(members,property)){size++}}Properties=members=null;if(!size){members=["valueOf","toString","toLocaleString","propertyIsEnumerable","isPrototypeOf","hasOwnProperty","constructor"];forEach=function(object,callback){var isFunction=getClass.call(object)==functionClass,property,length;var hasProperty=!isFunction&&typeof object.constructor!="function"&&objectTypes[typeof object.hasOwnProperty]&&object.hasOwnProperty||isProperty;for(property in object){if(!(isFunction&&property=="prototype")&&hasProperty.call(object,property)){callback(property)}}for(length=members.length;property=members[--length];hasProperty.call(object,property)&&callback(property));}}else if(size==2){forEach=function(object,callback){var members={},isFunction=getClass.call(object)==functionClass,property;for(property in object){if(!(isFunction&&property=="prototype")&&!isProperty.call(members,property)&&(members[property]=1)&&isProperty.call(object,property)){callback(property)}}}}else{forEach=function(object,callback){var isFunction=getClass.call(object)==functionClass,property,isConstructor;for(property in object){if(!(isFunction&&property=="prototype")&&isProperty.call(object,property)&&!(isConstructor=property==="constructor")){callback(property)}}if(isConstructor||isProperty.call(object,property="constructor")){callback(property)}}}return forEach(object,callback)};if(!has("json-stringify")){var Escapes={92:"\\\\",34:'\\"',8:"\\b",12:"\\f",10:"\\n",13:"\\r",9:"\\t"};var leadingZeroes="000000";var toPaddedString=function(width,value){return(leadingZeroes+(value||0)).slice(-width)};var unicodePrefix="\\u00";var quote=function(value){var result='"',index=0,length=value.length,useCharIndex=!charIndexBuggy||length>10;var symbols=useCharIndex&&(charIndexBuggy?value.split(""):value);for(;index-1/0&&value<1/0){if(getDay){date=floor(value/864e5);for(year=floor(date/365.2425)+1970-1;getDay(year+1,0)<=date;year++);for(month=floor((date-getDay(year,0))/30.42);getDay(year,month+1)<=date;month++);date=1+date-getDay(year,month);time=(value%864e5+864e5)%864e5;hours=floor(time/36e5)%24;minutes=floor(time/6e4)%60;seconds=floor(time/1e3)%60;milliseconds=time%1e3}else{year=value.getUTCFullYear();month=value.getUTCMonth();date=value.getUTCDate();hours=value.getUTCHours();minutes=value.getUTCMinutes();seconds=value.getUTCSeconds();milliseconds=value.getUTCMilliseconds()}value=(year<=0||year>=1e4?(year<0?"-":"+")+toPaddedString(6,year<0?-year:year):toPaddedString(4,year))+"-"+toPaddedString(2,month+1)+"-"+toPaddedString(2,date)+"T"+toPaddedString(2,hours)+":"+toPaddedString(2,minutes)+":"+toPaddedString(2,seconds)+"."+toPaddedString(3,milliseconds)+"Z"}else{value=null}}else if(typeof value.toJSON=="function"&&(className!=numberClass&&className!=stringClass&&className!=arrayClass||isProperty.call(value,"toJSON"))){value=value.toJSON(property)}}if(callback){value=callback.call(object,property,value)}if(value===null){return"null"}className=getClass.call(value);if(className==booleanClass){return""+value}else if(className==numberClass){return value>-1/0&&value<1/0?""+value:"null"}else if(className==stringClass){return quote(""+value)}if(typeof value=="object"){for(length=stack.length;length--;){if(stack[length]===value){throw TypeError()}}stack.push(value);results=[];prefix=indentation;indentation+=whitespace;if(className==arrayClass){for(index=0,length=value.length;index0){for(whitespace="",width>10&&(width=10);whitespace.length=48&&charCode<=57||charCode>=97&&charCode<=102||charCode>=65&&charCode<=70)){abort()}}value+=fromCharCode("0x"+source.slice(begin,Index));break;default:abort()}}else{if(charCode==34){break}charCode=source.charCodeAt(Index);begin=Index;while(charCode>=32&&charCode!=92&&charCode!=34){charCode=source.charCodeAt(++Index)}value+=source.slice(begin,Index)}}if(source.charCodeAt(Index)==34){Index++;return value}abort();default:begin=Index;if(charCode==45){isSigned=true;charCode=source.charCodeAt(++Index)}if(charCode>=48&&charCode<=57){if(charCode==48&&(charCode=source.charCodeAt(Index+1),charCode>=48&&charCode<=57)){abort()}isSigned=false;for(;Index=48&&charCode<=57);Index++);if(source.charCodeAt(Index)==46){position=++Index;for(;position=48&&charCode<=57);position++);if(position==Index){abort()}Index=position}charCode=source.charCodeAt(Index);if(charCode==101||charCode==69){charCode=source.charCodeAt(++Index);if(charCode==43||charCode==45){Index++}for(position=Index;position=48&&charCode<=57);position++);if(position==Index){abort()}Index=position}return+source.slice(begin,Index)}if(isSigned){abort()}if(source.slice(Index,Index+4)=="true"){Index+=4;return true}else if(source.slice(Index,Index+5)=="false"){Index+=5;return false}else if(source.slice(Index,Index+4)=="null"){Index+=4;return null}abort()}}return"$"};var get=function(value){var results,hasMembers;if(value=="$"){abort()}if(typeof value=="string"){if((charIndexBuggy?value.charAt(0):value[0])=="@"){return value.slice(1)}if(value=="["){results=[];for(;;hasMembers||(hasMembers=true)){value=lex();if(value=="]"){break}if(hasMembers){if(value==","){value=lex();if(value=="]"){abort()}}else{abort()}}if(value==","){abort()}results.push(get(value))}return results}else if(value=="{"){results={};for(;;hasMembers||(hasMembers=true)){value=lex();if(value=="}"){break}if(hasMembers){if(value==","){value=lex();if(value=="}"){abort()}}else{abort()}}if(value==","||typeof value!="string"||(charIndexBuggy?value.charAt(0):value[0])!="@"||lex()!=":"){abort()}results[value.slice(1)]=get(lex()) 4 | }return results}abort()}return value};var update=function(source,property,callback){var element=walk(source,property,callback);if(element===undef){delete source[property]}else{source[property]=element}};var walk=function(source,property,callback){var value=source[property],length;if(typeof value=="object"&&value){if(getClass.call(value)==arrayClass){for(length=value.length;length--;){update(value,length,callback)}}else{forEach(value,function(property){update(value,property,callback)})}}return callback.call(source,property,value)};exports.parse=function(source,callback){var result,value;Index=0;Source=""+source;result=get(lex());if(lex()!="$"){abort()}Index=Source=null;return callback&&getClass.call(callback)==functionClass?walk((value={},value[""]=result,value),"",callback):result}}}exports["runInContext"]=runInContext;return exports}if(freeExports&&!isLoader){runInContext(root,freeExports)}else{var nativeJSON=root.JSON,previousJSON=root["JSON3"],isRestored=false;var JSON3=runInContext(root,root["JSON3"]={noConflict:function(){if(!isRestored){isRestored=true;root.JSON=nativeJSON;root["JSON3"]=previousJSON;nativeJSON=previousJSON=null}return JSON3}});root.JSON={parse:JSON3.parse,stringify:JSON3.stringify}}if(isLoader){define(function(){return JSON3})}}).call(this)}).call(this,typeof self!=="undefined"?self:typeof window!=="undefined"?window:typeof global!=="undefined"?global:{})},{}],51:[function(_dereq_,module,exports){module.exports=toArray;function toArray(list,index){var array=[];index=index||0;for(var i=index||0;i