diff --git a/data/userfiles.py b/data/userfiles.py index cc314a47f..c01e23568 100644 --- a/data/userfiles.py +++ b/data/userfiles.py @@ -55,11 +55,15 @@ class UserRequestFiles(object): return file_id - def get_file_url(self, file_id, expires_in=300): + def get_file_url(self, file_id, expires_in=300, mime_type=None): self._initialize_s3() full_key = os.path.join(self._prefix, file_id) k = Key(self._bucket, full_key) - return k.generate_url(expires_in) + headers = None + if mime_type: + headers={'Content-Type': mime_type} + + return k.generate_url(expires_in, headers=headers) def get_file_checksum(self, file_id): self._initialize_s3() diff --git a/endpoints/common.py b/endpoints/common.py index 4a8c03eb9..df233dfba 100644 --- a/endpoints/common.py +++ b/endpoints/common.py @@ -91,7 +91,7 @@ def random_string(): def render_page_template(name, **kwargs): resp = make_response(render_template(name, route_data=json.dumps(get_route_data()), - cache_buster=random_string(), **kwargs)) + cache_buster='foobarbaz', **kwargs)) resp.headers['X-FRAME-OPTIONS'] = 'DENY' return resp diff --git a/initdb.py b/initdb.py index a4b1709f0..0f6696b7d 100644 --- a/initdb.py +++ b/initdb.py @@ -349,6 +349,12 @@ def populate_database(): build.uuid = 'deadbeef-dead-beef-dead-beefdeadbeef' build.save() + build2 = model.create_repository_build(building, token, job_config, + '68daeebd-a5b9-457f-80a0-4363b882f8ea', + 'build-name', trigger) + build2.uuid = 'deadpork-dead-pork-dead-porkdeadpork' + build2.save() + org = model.create_organization('buynlarge', 'quay@devtable.com', new_user_1) org.stripe_id = TEST_STRIPE_ID diff --git a/static/js/app.js b/static/js/app.js index 87f73c724..302ffeaea 100644 --- a/static/js/app.js +++ b/static/js/app.js @@ -185,24 +185,147 @@ quayApp = angular.module('quay', ['ngRoute', 'chieffancypants.loadingBar', 'angu return service; }]); - $provide.factory('UtilService', ['$sanitize', function($sanitize) { - var utilService = {}; - utilService.textToSafeHtml = function(text) { - var adjusted = text.replace(/&/g, "&") - .replace(//g, ">") - .replace(/"/g, """) - .replace(/'/g, "'"); + $provide.factory('DataFileService', [function() { + var dataFileService = {}; - return $sanitize(adjusted); + dataFileService.getName_ = function(filePath) { + var parts = filePath.split('/'); + return parts[parts.length - 1]; }; + dataFileService.tryAsZip_ = function(buf, success, failure) { + var zip = null; + var zipFiles = null; + try { + var zip = new JSZip(buf); + zipFiles = zip.files; + } catch (e) { + failure(); + return; + } + + var files = []; + for (var filePath in zipFiles) { + if (zipFiles.hasOwnProperty(filePath)) { + files.push({ + 'name': dataFileService.getName_(filePath), + 'path': filePath, + 'canRead': true, + 'toBlob': (function(fp) { + return function() { + return new Blob([zip.file(fp).asArrayBuffer()]); + }; + }(filePath)) + }); + } + } + + success(files); + }; + + dataFileService.tryAsTarGz_ = function(buf, success, failure) { + var gunzip = new Zlib.Gunzip(buf); + var plain = gunzip.decompress(); + + var handler = new MultiFile(); + handler.files = []; + handler.processTarChunks(dataFileService.arrayToString(plain), 0); + if (!handler.files.length) { + failure(); + return; + } + + var files = []; + for (var i = 0; i < handler.files.length; ++i) { + var currentFile = handler.files[i]; + files.push({ + 'name': dataFileService.getName_(currentFile.filename), + 'path': currentFile.filename, + 'canRead': true, + 'toBlob': (function(currentFile) { + return function() { + return new Blob([currentFile.data], {type: 'application/octet-binary'}); + }; + }(currentFile)) + }); + } + success(files); + }; + + dataFileService.blobToString = function(blob, callback) { + var reader = new FileReader(); + reader.onload = function(event){ + callback(reader.result); + }; + reader.readAsText(blob); + }; + + dataFileService.arrayToString = function(buf) { + return String.fromCharCode.apply(null, new Uint16Array(buf)); + }; + + dataFileService.readDataArrayAsPossibleArchive = function(buf, success, failure) { + dataFileService.tryAsZip_(buf, success, function() { + dataFileService.tryAsTarGz_(buf, success, failure); + }); + }; + + dataFileService.downloadDataFileAsArrayBuffer = function($scope, url, progress, error, loaded) { + var request = new XMLHttpRequest(); + request.open('GET', url, true); + request.responseType = 'arraybuffer'; + + request.onprogress = function(e) { + $scope.$apply(function() { + var percentLoaded; + if (e.lengthComputable) { + progress(e.loaded / e.total); + } + }); + }; + + request.onerror = function() { + $scope.$apply(function() { + error(); + }); + }; + + request.onload = function() { + if (this.status == 200) { + $scope.$apply(function() { + var uint8array = new Uint8Array(request.response); + loaded(uint8array); + }); + return; + } + }; + + request.send(); + }; + + return dataFileService; + }]); + + + $provide.factory('UtilService', ['$sanitize', function($sanitize) { + var utilService = {}; + + utilService.textToSafeHtml = function(text) { + var adjusted = text.replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """) + .replace(/'/g, "'"); + + return $sanitize(adjusted); + }; + return utilService; }]); - $provide.factory('TriggerDescriptionBuilder', ['UtilService', '$sanitize', function(UtilService, $sanitize) { + $provide.factory('TriggerDescriptionBuilder', ['UtilService', '$sanitize', function(UtilService, $sanitize) { var builderService = {}; builderService.getDescription = function(name, config) { diff --git a/static/js/controllers.js b/static/js/controllers.js index b74071928..c2249773c 100644 --- a/static/js/controllers.js +++ b/static/js/controllers.js @@ -778,7 +778,7 @@ function RepoCtrl($scope, $sanitize, Restangular, ImageMetadataService, ApiServi loadViewInfo(); } -function BuildPackageCtrl($scope, Restangular, ApiService, $routeParams, $rootScope, $location, $timeout) { +function BuildPackageCtrl($scope, Restangular, ApiService, DataFileService, $routeParams, $rootScope, $location, $timeout) { var namespace = $routeParams.namespace; var name = $routeParams.name; var buildid = $routeParams.buildid; @@ -800,100 +800,90 @@ function BuildPackageCtrl($scope, Restangular, ApiService, $routeParams, $rootSc }, 10); }; - $scope.downloadForUser = function() { - var blob = $scope.zip.generate({type:"blob"}); - saveAs(blob, $scope.repobuild['display_name'] + '.zip'); + var determineDockerfilePath = function() { + var dockerfilePath = 'Dockerfile'; + if ($scope.repobuild['job_config']) { + var dockerfileFolder = ($scope.repobuild['job_config']['build_subdir'] || ''); + if (dockerfileFolder[0] == '/') { + dockerfileFolder = dockerfileFolder.substr(1); + } + if (dockerfileFolder && dockerfileFolder[dockerfileFolder.length - 1] != '/') { + dockerfileFolder += '/'; + } + dockerfilePath = dockerfileFolder + 'Dockerfile'; + } + return dockerfilePath; }; - var processBuildPack = function(response) { - // Try to load as a zip file. - var zipFiles = null; - var zip = null; - try { - var zip = new JSZip(response); - zipFiles = zip.files; - } catch (e) { - } - - // Find the Dockerfile in the zip file. If there isn't any zip file, then the response - // itself (should) be the Dockerfile. - if (zipFiles && Object.keys(zipFiles).length) { - // Load the dockerfile contents. - var dockerfilePath = 'Dockerfile'; - if ($scope.repobuild['job_config']) { - var dockerfileFolder = ($scope.repobuild['job_config']['build_subdir'] || ''); - if (dockerfileFolder[0] == '/') { - dockerfileFolder = dockerfileFolder.substr(1); - } - if (dockerfileFolder && dockerfileFolder[dockerfileFolder.length - 1] != '/') { - dockerfileFolder += '/'; - } - - dockerfilePath = dockerfileFolder + 'Dockerfile'; - } + var processBuildPack = function(uint8array) { + var archiveread = function(files) { + var getpath = function(file) { + return file.path; + }; - var dockerfile = zip.file(dockerfilePath); - if (dockerfile) { - $scope.dockerFileContents = dockerfile.asText(); - $scope.dockerFilePath = dockerfilePath; - } + var findFile = function(path) { + for (var i = 0; i < files.length; ++i) { + var file = files[i]; + if (file.path == path) { + return file; + } + } + return null; + }; - // Build the zip file tree. - $scope.zip = zip; - $scope.tree = new FileTree(Object.keys(zipFiles)); + $scope.tree = new FileTree($.map(files, getpath)); $($scope.tree).bind('fileClicked', function(e) { - var file = zip.file(e.path); - if (file) { - var blob = new Blob([file.asArrayBuffer()]); - saveAs(blob, file.name); + var file = findFile(e.path); + if (file && file.canRead) { + saveAs(file.toBlob(), file.name); } - }); - } else { - $scope.dockerFileContents = response; - $scope.dockerFilePath = 'Dockerfile'; - } + }); - $scope.loaded = true; + var dockerfilePath = determineDockerfilePath(); + var dockerfile = findFile(dockerfilePath); + if (dockerfile && dockerfile.canRead) { + DataFileService.blobToString(dockerfile.toBlob(), function(result) { + $scope.$apply(function() { + $scope.dockerFilePath = dockerfilePath; + $scope.dockerFileContents = result; + }); + }); + } + + $scope.loaded = true; + }; + + var notarchive = function() { + $scope.dockerFileContents = DataFileService.arrayToString(uint8array); + $scope.loaded = true; + }; + + DataFileService.readDataArrayAsPossibleArchive(uint8array, archiveread, notarchive); }; - + var downloadBuildPack = function(url) { $scope.downloadProgress = 0; $scope.downloading = true; - startDownload(url); }; var startDownload = function(url) { - var request = new XMLHttpRequest(); - request.open('GET', url, true); - if (request.overrideMimeType) { - request.overrideMimeType('text/plain; charset=x-user-defined'); - } - request.onprogress = function(e) { - $scope.$apply(function() { - var percentLoaded; - if (e.lengthComputable) { - $scope.downloadProgress = (e.loaded / e.total) * 100; - } - }); + var onprogress = function(p) { + $scope.downloadProgress = p * 100; }; - request.onerror = function() { - $scope.$apply(function() { - $scope.downloading = false; - $scope.downloadError = true; - }); + + var onerror = function() { + $scope.downloading = false; + $scope.downloadError = true; }; - request.onreadystatechange = function() { - var state = request.readyState; - if (state == 4) { - $scope.$apply(function() { - $scope.downloading = false; - processBuildPack(request.responseText); - }); - return; - } + + var onloaded = function(uint8array) { + $scope.downloading = false; + processBuildPack(uint8array); }; - request.send(); + + DataFileService.downloadDataFileAsArrayBuffer($scope, url, + onprogress, onerror, onloaded); }; var getBuildInfo = function() { diff --git a/static/lib/multifile.js b/static/lib/multifile.js new file mode 100644 index 000000000..02d1e006b --- /dev/null +++ b/static/lib/multifile.js @@ -0,0 +1,214 @@ +/* MultiFile - A JavaScript library to load multiple files from + tar archives and json_packed files (see http://gist.github.com/407595) + + Example: Loading multiple images from a tarball. + + MultiFile.load('images.tar', function(xhr) { + this.files.forEach(function(f) { + var e = document.createElement('div'); + document.body.appendChild(e); + var p = document.createElement('p'); + p.appendChild(document.createTextNode(f.filename + " (" + f.length + " bytes)")); + e.appendChild(p); + var img = new Image(); + img.src = f.toDataURL(); + e.appendChild(img); + }); + }); + + Example 2: Streaming images from a tarball. + + MultiFile.stream('images.tar', function(f) { + var e = document.createElement('div'); + document.body.appendChild(e); + var p = document.createElement('p'); + p.appendChild(document.createTextNode(f.filename + " (" + f.length + " bytes)")); + e.appendChild(p); + var img = new Image(); + img.src = f.toDataURL(); + e.appendChild(img); + }); + + +Copyright (c) 2010 Ilmari Heikkinen + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +MultiFile = function(){}; + +// Load and parse archive, calls onload after loading all files. +MultiFile.load = function(url, onload) { + var o = new MultiFile(); + o.onload = onload; + o.load(url); + return o; +} + +// Streams an archive from the given url, calling onstream after loading each file in archive. +// Calls onload after loading all files. +MultiFile.stream = function(url, onstream, onload) { + var o = new MultiFile(); + o.onload = onload; + o.onstream = onstream; + o.load(url); + return o; +} +MultiFile.prototype = { + onerror : null, + onload : null, + onstream : null, + + load : function(url) { + var xhr = new XMLHttpRequest(); + var self = this; + var offset = 0; + this.files = []; + var isTar = (/\.tar(\?.*)?$/i).test(url); + xhr.onreadystatechange = function() { + if (xhr.readyState == 4) { + if (xhr.status == 200 || xhr.status == 0) { + if (isTar) + offset = self.processTarChunks(xhr.responseText, offset); + else + self.processJSONChunks(xhr.responseText); + if (self.onload) + self.onload(xhr); + } else { + if (self.onerror) + self.onerror(xhr); + } + } else if (xhr.readyState == 3) { + if (xhr.status == 200 || xhr.status == 0) { + if (isTar) + offset = self.processTarChunks(xhr.responseText, offset); + else + self.processJSONChunks(xhr.responseText); + } + } + }; + xhr.open("GET", url, true); + xhr.overrideMimeType("text/plain; charset=x-user-defined"); + xhr.setRequestHeader("Content-Type", "text/plain"); + xhr.send(null); + }, + + onerror : function(xhr) { + alert("Error: "+xhr.status); + }, + + parseJSON : function(text) { + this.processJSONChunks(text); + }, + processJSONChunks : function(text) { + if (this.files.length == 0) { // processing headers + var idx = text.indexOf('\n'); + if (idx >= 0) { // got header + this.files = JSON.parse(text.substring(0,idx)); + this.files.forEach(function(f) { f.offset += idx + 1; }) + } + } + if (this.files.length > 0) { // processing data + var f = null; + var idx=0; + for (idx=0; idx= offset + 512) { + var header = this.files.length == 0 ? null : this.files[this.files.length-1]; + if (header && header.data == null) { + if (offset + header.length <= responseText.length) { + header.data = responseText.substring(offset, offset+header.length); + header.toDataURL = this.__toDataURL; + offset += 512 * Math.ceil(header.length / 512); + if (this.onstream) + this.onstream(header); + } else { // not loaded yet + break; + } + } else { + var header = this.parseTarHeader(responseText, offset); + if (header.length > 0 || header.filename != '') { + this.files.push(header); + offset += 512; + header.offset = offset; + } else { // empty header, stop processing + offset = responseText.length; + } + } + } + return offset; + }, + parseTarHeader : function(text, offset) { + var i = offset || 0; + var h = {}; + h.filename = text.substring(i, i+=100).split("\0", 1)[0]; + h.mode = text.substring(i, i+=8).split("\0", 1)[0]; + h.uid = text.substring(i, i+=8).split("\0", 1)[0]; + h.gid = text.substring(i, i+=8).split("\0", 1)[0]; + h.length = this.parseTarNumber(text.substring(i, i+=12)); + h.lastModified = text.substring(i, i+=12).split("\0", 1)[0]; + h.checkSum = text.substring(i, i+=8).split("\0", 1)[0]; + h.fileType = text.substring(i, i+=1).split("\0", 1)[0]; + h.linkName = text.substring(i, i+=100).split("\0", 1)[0]; + return h; + }, + + parseTarNumber : function(text) { + return parseInt('0'+text.replace(/[^\d]/g, ''), 8); + }, + + __toDataURL : function() { + if (this.data.substring(0,40).match(/^data:[^\/]+\/[^,]+,/)) { + return this.data; + } else if (MultiFile.prototype.cleanHighByte(this.data.substring(0,10)).match(/\377\330\377\340..JFIF/)) { + return 'data:image/jpeg;base64,'+btoa(MultiFile.prototype.cleanHighByte(this.data)); + } else if (MultiFile.prototype.cleanHighByte(this.data.substring(0,6)) == "\211PNG\r\n") { + return 'data:image/png;base64,'+btoa(MultiFile.prototype.cleanHighByte(this.data)); + } else if (MultiFile.prototype.cleanHighByte(this.data.substring(0,6)).match(/GIF8[79]a/)) { + return 'data:image/gif;base64,'+btoa(MultiFile.prototype.cleanHighByte(this.data)); + } else { + throw("toDataURL: I don't know how to handle " + this.filename); + } + } +} + diff --git a/static/lib/zlib.js b/static/lib/zlib.js new file mode 100644 index 000000000..7fe6bdbcd --- /dev/null +++ b/static/lib/zlib.js @@ -0,0 +1,51 @@ +/** @license zlib.js 2012 - imaya [ https://github.com/imaya/zlib.js ] The MIT License */(function() {'use strict';function q(b){throw b;}var t=void 0,u=!0,aa=this;function A(b,a){var c=b.split("."),d=aa;!(c[0]in d)&&d.execScript&&d.execScript("var "+c[0]);for(var e;c.length&&(e=c.shift());)!c.length&&a!==t?d[e]=a:d=d[e]?d[e]:d[e]={}};var B="undefined"!==typeof Uint8Array&&"undefined"!==typeof Uint16Array&&"undefined"!==typeof Uint32Array&&"undefined"!==typeof DataView;function F(b,a){this.index="number"===typeof a?a:0;this.m=0;this.buffer=b instanceof(B?Uint8Array:Array)?b:new (B?Uint8Array:Array)(32768);2*this.buffer.length<=this.index&&q(Error("invalid index"));this.buffer.length<=this.index&&this.f()}F.prototype.f=function(){var b=this.buffer,a,c=b.length,d=new (B?Uint8Array:Array)(c<<1);if(B)d.set(b);else for(a=0;a>>8&255]<<16|H[b>>>16&255]<<8|H[b>>>24&255])>>32-a:H[b]>>8-a);if(8>a+f)g=g<>a-k-1&1,8===++f&&(f=0,d[e++]=H[g],g=0,e===d.length&&(d=this.f()));d[e]=g;this.buffer=d;this.m=f;this.index=e};F.prototype.finish=function(){var b=this.buffer,a=this.index,c;0ca;++ca){for(var K=ca,da=K,ea=7,K=K>>>1;K;K>>>=1)da<<=1,da|=K&1,--ea;ba[ca]=(da<>>0}var H=ba;function ja(b,a,c){var d,e="number"===typeof a?a:a=0,f="number"===typeof c?c:b.length;d=-1;for(e=f&7;e--;++a)d=d>>>8^O[(d^b[a])&255];for(e=f>>3;e--;a+=8)d=d>>>8^O[(d^b[a])&255],d=d>>>8^O[(d^b[a+1])&255],d=d>>>8^O[(d^b[a+2])&255],d=d>>>8^O[(d^b[a+3])&255],d=d>>>8^O[(d^b[a+4])&255],d=d>>>8^O[(d^b[a+5])&255],d=d>>>8^O[(d^b[a+6])&255],d=d>>>8^O[(d^b[a+7])&255];return(d^4294967295)>>>0} +var ka=[0,1996959894,3993919788,2567524794,124634137,1886057615,3915621685,2657392035,249268274,2044508324,3772115230,2547177864,162941995,2125561021,3887607047,2428444049,498536548,1789927666,4089016648,2227061214,450548861,1843258603,4107580753,2211677639,325883990,1684777152,4251122042,2321926636,335633487,1661365465,4195302755,2366115317,997073096,1281953886,3579855332,2724688242,1006888145,1258607687,3524101629,2768942443,901097722,1119000684,3686517206,2898065728,853044451,1172266101,3705015759, +2882616665,651767980,1373503546,3369554304,3218104598,565507253,1454621731,3485111705,3099436303,671266974,1594198024,3322730930,2970347812,795835527,1483230225,3244367275,3060149565,1994146192,31158534,2563907772,4023717930,1907459465,112637215,2680153253,3904427059,2013776290,251722036,2517215374,3775830040,2137656763,141376813,2439277719,3865271297,1802195444,476864866,2238001368,4066508878,1812370925,453092731,2181625025,4111451223,1706088902,314042704,2344532202,4240017532,1658658271,366619977, +2362670323,4224994405,1303535960,984961486,2747007092,3569037538,1256170817,1037604311,2765210733,3554079995,1131014506,879679996,2909243462,3663771856,1141124467,855842277,2852801631,3708648649,1342533948,654459306,3188396048,3373015174,1466479909,544179635,3110523913,3462522015,1591671054,702138776,2966460450,3352799412,1504918807,783551873,3082640443,3233442989,3988292384,2596254646,62317068,1957810842,3939845945,2647816111,81470997,1943803523,3814918930,2489596804,225274430,2053790376,3826175755, +2466906013,167816743,2097651377,4027552580,2265490386,503444072,1762050814,4150417245,2154129355,426522225,1852507879,4275313526,2312317920,282753626,1742555852,4189708143,2394877945,397917763,1622183637,3604390888,2714866558,953729732,1340076626,3518719985,2797360999,1068828381,1219638859,3624741850,2936675148,906185462,1090812512,3747672003,2825379669,829329135,1181335161,3412177804,3160834842,628085408,1382605366,3423369109,3138078467,570562233,1426400815,3317316542,2998733608,733239954,1555261956, +3268935591,3050360625,752459403,1541320221,2607071920,3965973030,1969922972,40735498,2617837225,3943577151,1913087877,83908371,2512341634,3803740692,2075208622,213261112,2463272603,3855990285,2094854071,198958881,2262029012,4057260610,1759359992,534414190,2176718541,4139329115,1873836001,414664567,2282248934,4279200368,1711684554,285281116,2405801727,4167216745,1634467795,376229701,2685067896,3608007406,1308918612,956543938,2808555105,3495958263,1231636301,1047427035,2932959818,3654703836,1088359270, +936918E3,2847714899,3736837829,1202900863,817233897,3183342108,3401237130,1404277552,615818150,3134207493,3453421203,1423857449,601450431,3009837614,3294710456,1567103746,711928724,3020668471,3272380065,1510334235,755167117],O=B?new Uint32Array(ka):ka;function P(){}P.prototype.getName=function(){return this.name};P.prototype.getData=function(){return this.data};P.prototype.Y=function(){return this.Z};A("Zlib.GunzipMember",P);A("Zlib.GunzipMember.prototype.getName",P.prototype.getName);A("Zlib.GunzipMember.prototype.getData",P.prototype.getData);A("Zlib.GunzipMember.prototype.getMtime",P.prototype.Y);function la(b){this.buffer=new (B?Uint16Array:Array)(2*b);this.length=0}la.prototype.getParent=function(b){return 2*((b-2)/4|0)};la.prototype.push=function(b,a){var c,d,e=this.buffer,f;c=this.length;e[this.length++]=a;for(e[this.length++]=b;0e[d])f=e[c],e[c]=e[d],e[d]=f,f=e[c+1],e[c+1]=e[d+1],e[d+1]=f,c=d;else break;return this.length}; +la.prototype.pop=function(){var b,a,c=this.buffer,d,e,f;a=c[0];b=c[1];this.length-=2;c[0]=c[this.length];c[1]=c[this.length+1];for(f=0;;){e=2*f+2;if(e>=this.length)break;e+2c[e]&&(e+=2);if(c[e]>c[f])d=c[f],c[f]=c[e],c[e]=d,d=c[f+1],c[f+1]=c[e+1],c[e+1]=d;else break;f=e}return{index:b,value:a,length:this.length}};function ma(b){var a=b.length,c=0,d=Number.POSITIVE_INFINITY,e,f,g,k,h,l,s,p,m,n;for(p=0;pc&&(c=b[p]),b[p]>=1;n=g<<16|p;for(m=l;mS;S++)switch(u){case 143>=S:sa.push([S+48,8]);break;case 255>=S:sa.push([S-144+400,9]);break;case 279>=S:sa.push([S-256+0,7]);break;case 287>=S:sa.push([S-280+192,8]);break;default:q("invalid literal: "+S)} +na.prototype.g=function(){var b,a,c,d,e=this.input;switch(this.k){case 0:c=0;for(d=e.length;c>>8&255;m[n++]=l&255;m[n++]=l>>>8&255;if(B)m.set(f,n),n+=f.length,m=m.subarray(0,n);else{s=0;for(p=f.length;sz)for(;0< +z--;)I[G++]=0,M[0]++;else for(;0z?z:138,D>z-3&&D=D?(I[G++]=17,I[G++]=D-3,M[17]++):(I[G++]=18,I[G++]=D-11,M[18]++),z-=D;else if(I[G++]=J[w],M[J[w]]++,z--,3>z)for(;0z?z:6,D>z-3&&DC;C++)wa[C]=oa[pb[C]];for(Z=19;4=a:return[265,a-11,1];case 14>=a:return[266,a-13,1];case 16>=a:return[267,a-15,1];case 18>=a:return[268,a-17,1];case 22>=a:return[269,a-19,2];case 26>=a:return[270,a-23,2];case 30>=a:return[271,a-27,2];case 34>=a:return[272, +a-31,2];case 42>=a:return[273,a-35,3];case 50>=a:return[274,a-43,3];case 58>=a:return[275,a-51,3];case 66>=a:return[276,a-59,3];case 82>=a:return[277,a-67,4];case 98>=a:return[278,a-83,4];case 114>=a:return[279,a-99,4];case 130>=a:return[280,a-115,4];case 162>=a:return[281,a-131,5];case 194>=a:return[282,a-163,5];case 226>=a:return[283,a-195,5];case 257>=a:return[284,a-227,5];case 258===a:return[285,a-258,0];default:q("invalid length: "+a)}}var a=[],c,d;for(c=3;258>=c;c++)d=b(c),a[c]=d[2]<<24|d[1]<< +16|d[0];return a}(),Aa=B?new Uint32Array(ya):ya; +function ta(b,a){function c(a,c){var b=a.Q,d=[],e=0,f;f=Aa[a.length];d[e++]=f&65535;d[e++]=f>>16&255;d[e++]=f>>24;var g;switch(u){case 1===b:g=[0,b-1,0];break;case 2===b:g=[1,b-2,0];break;case 3===b:g=[2,b-3,0];break;case 4===b:g=[3,b-4,0];break;case 6>=b:g=[4,b-5,1];break;case 8>=b:g=[5,b-7,1];break;case 12>=b:g=[6,b-9,2];break;case 16>=b:g=[7,b-13,2];break;case 24>=b:g=[8,b-17,3];break;case 32>=b:g=[9,b-25,3];break;case 48>=b:g=[10,b-33,4];break;case 64>=b:g=[11,b-49,4];break;case 96>=b:g=[12,b- +65,5];break;case 128>=b:g=[13,b-97,5];break;case 192>=b:g=[14,b-129,6];break;case 256>=b:g=[15,b-193,6];break;case 384>=b:g=[16,b-257,7];break;case 512>=b:g=[17,b-385,7];break;case 768>=b:g=[18,b-513,8];break;case 1024>=b:g=[19,b-769,8];break;case 1536>=b:g=[20,b-1025,9];break;case 2048>=b:g=[21,b-1537,9];break;case 3072>=b:g=[22,b-2049,10];break;case 4096>=b:g=[23,b-3073,10];break;case 6144>=b:g=[24,b-4097,11];break;case 8192>=b:g=[25,b-6145,11];break;case 12288>=b:g=[26,b-8193,12];break;case 16384>= +b:g=[27,b-12289,12];break;case 24576>=b:g=[28,b-16385,13];break;case 32768>=b:g=[29,b-24577,13];break;default:q("invalid distance")}f=g;d[e++]=f[0];d[e++]=f[1];d[e++]=f[2];var h,k;h=0;for(k=d.length;h=f;)v[f++]=0;for(f=0;29>=f;)x[f++]=0}v[256]=1;d=0;for(e=a.length;d=e){p&&c(p,-1);f=0;for(g=e-d;fg&&a+gf&&(e=d,f=g);if(258===g)break}return new xa(f,a-e)} +function ua(b,a){var c=b.length,d=new la(572),e=new (B?Uint8Array:Array)(c),f,g,k,h,l;if(!B)for(h=0;h2*e[n-1]+f[n]&&(e[n]=2*e[n-1]+f[n]),k[n]=Array(e[n]),h[n]=Array(e[n]);for(m=0;mb[m]?(k[n][r]=v,h[n][r]=a,x+=2):(k[n][r]=b[m],h[n][r]=m,++m);l[n]=0;1===f[n]&&d(n)}return g} +function va(b){var a=new (B?Uint16Array:Array)(b.length),c=[],d=[],e=0,f,g,k,h;f=0;for(g=b.length;f>>=1}return a};function Da(b,a){this.input=b;this.b=this.c=0;this.i={};a&&(a.flags&&(this.i=a.flags),"string"===typeof a.filename&&(this.filename=a.filename),"string"===typeof a.comment&&(this.A=a.comment),a.deflateOptions&&(this.l=a.deflateOptions));this.l||(this.l={})} +Da.prototype.g=function(){var b,a,c,d,e,f,g,k,h=new (B?Uint8Array:Array)(32768),l=0,s=this.input,p=this.c,m=this.filename,n=this.A;h[l++]=31;h[l++]=139;h[l++]=8;b=0;this.i.fname&&(b|=Ea);this.i.fcomment&&(b|=Fa);this.i.fhcrc&&(b|=Ga);h[l++]=b;a=(Date.now?Date.now():+new Date)/1E3|0;h[l++]=a&255;h[l++]=a>>>8&255;h[l++]=a>>>16&255;h[l++]=a>>>24&255;h[l++]=0;h[l++]=Ha;if(this.i.fname!==t){g=0;for(k=m.length;g>>8&255),h[l++]=f&255;h[l++]=0}if(this.i.comment){g= +0;for(k=n.length;g>>8&255),h[l++]=f&255;h[l++]=0}this.i.fhcrc&&(c=ja(h,0,l)&65535,h[l++]=c&255,h[l++]=c>>>8&255);this.l.outputBuffer=h;this.l.outputIndex=l;e=new na(s,this.l);h=e.g();l=e.b;B&&(l+8>h.buffer.byteLength?(this.a=new Uint8Array(l+8),this.a.set(new Uint8Array(h.buffer)),h=this.a):h=new Uint8Array(h.buffer));d=ja(s,t,t);h[l++]=d&255;h[l++]=d>>>8&255;h[l++]=d>>>16&255;h[l++]=d>>>24&255;k=s.length;h[l++]=k&255;h[l++]=k>>>8&255;h[l++]=k>>>16&255;h[l++]= +k>>>24&255;this.c=p;B&&l>>=1;switch(b){case 0:var a=this.input,c=this.c,d=this.a,e=this.b,f=a.length,g=t,k=t,h=d.length,l=t;this.e=this.j=0;c+1>=f&&q(Error("invalid uncompressed block header: LEN"));g=a[c++]|a[c++]<<8;c+1>=f&&q(Error("invalid uncompressed block header: NLEN"));k=a[c++]|a[c++]<<8;g===~k&&q(Error("invalid uncompressed block header: length verify"));c+g>a.length&&q(Error("input buffer is broken"));switch(this.r){case Ja:for(;e+g>d.length;){l= +h-e;g-=l;if(B)d.set(a.subarray(c,c+l),e),e+=l,c+=l;else for(;l--;)d[e++]=a[c++];this.b=e;d=this.f();e=this.b}break;case Ia:for(;e+g>d.length;)d=this.f({F:2});break;default:q(Error("invalid inflate mode"))}if(B)d.set(a.subarray(c,c+g),e),e+=g,c+=g;else for(;g--;)d[e++]=a[c++];this.c=c;this.b=e;this.a=d;break;case 1:this.s(Za,$a);break;case 2:ab(this);break;default:q(Error("unknown BTYPE: "+b))}}return this.B()}; +var bb=[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15],cb=B?new Uint16Array(bb):bb,db=[3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258,258,258],eb=B?new Uint16Array(db):db,fb=[0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0],gb=B?new Uint8Array(fb):fb,hb=[1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577],ib=B?new Uint16Array(hb):hb,jb=[0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10, +10,11,11,12,12,13,13],kb=B?new Uint8Array(jb):jb,lb=new (B?Uint8Array:Array)(288),V,mb;V=0;for(mb=lb.length;V=V?8:255>=V?9:279>=V?7:8;var Za=ma(lb),nb=new (B?Uint8Array:Array)(30),ob,qb;ob=0;for(qb=nb.length;ob=g&&q(Error("input buffer is broken")),c|=e[f++]<>>a;b.e=d-a;b.c=f;return k} +function rb(b,a){for(var c=b.j,d=b.e,e=b.input,f=b.c,g=e.length,k=a[0],h=a[1],l,s;d=g);)c|=e[f++]<>>16;b.j=c>>s;b.e=d-s;b.c=f;return l&65535} +function ab(b){function a(a,b,c){var d,e=this.J,f,g;for(g=0;gf)d>=e&&(this.b=d,c=this.f(),d=this.b),c[d++]=f;else{g=f-257;h=eb[g];0=e&&(this.b=d,c=this.f(),d=this.b);for(;h--;)c[d]=c[d++-k]}for(;8<=this.e;)this.e-=8,this.c--;this.b=d}; +T.prototype.T=function(b,a){var c=this.a,d=this.b;this.C=b;for(var e=c.length,f,g,k,h;256!==(f=rb(this,b));)if(256>f)d>=e&&(c=this.f(),e=c.length),c[d++]=f;else{g=f-257;h=eb[g];0e&&(c=this.f(),e=c.length);for(;h--;)c[d]=c[d++-k]}for(;8<=this.e;)this.e-=8,this.c--;this.b=d}; +T.prototype.f=function(){var b=new (B?Uint8Array:Array)(this.b-32768),a=this.b-32768,c,d,e=this.a;if(B)b.set(e.subarray(32768,b.length));else{c=0;for(d=b.length;cc;++c)e[c]=e[a+c];this.b=32768;return e}; +T.prototype.U=function(b){var a,c=this.input.length/this.c+1|0,d,e,f,g=this.input,k=this.a;b&&("number"===typeof b.F&&(c=b.F),"number"===typeof b.P&&(c+=b.P));2>c?(d=(g.length-this.c)/this.C[2],f=258*(d/2)|0,e=fa&&(this.a.length=a),b=this.a);return this.buffer=b};function sb(b){this.input=b;this.c=0;this.t=[];this.D=!1}sb.prototype.X=function(){this.D||this.h();return this.t.slice()}; +sb.prototype.h=function(){for(var b=this.input.length;this.c>>0;ja(e,t,t)!==s&&q(Error("invalid CRC-32 checksum: 0x"+ja(e,t,t).toString(16)+ +" / 0x"+s.toString(16)));a.da=c=(p[m++]|p[m++]<<8|p[m++]<<16|p[m++]<<24)>>>0;(e.length&4294967295)!==c&&q(Error("invalid input size: "+(e.length&4294967295)+" / "+c));this.t.push(a);this.c=m}this.D=u;var n=this.t,r,v,x=0,Q=0,y;r=0;for(v=n.length;r>>0;b=a}for(var e=1,f=0,g=b.length,k,h=0;0>>0};function ub(b,a){var c,d;this.input=b;this.c=0;if(a||!(a={}))a.index&&(this.c=a.index),a.verify&&(this.$=a.verify);c=b[this.c++];d=b[this.c++];switch(c&15){case vb:this.method=vb;break;default:q(Error("unsupported compression method"))}0!==((c<<8)+d)%31&&q(Error("invalid fcheck flag:"+((c<<8)+d)%31));d&32&&q(Error("fdict flag is not supported"));this.L=new T(b,{index:this.c,bufferSize:a.bufferSize,bufferType:a.bufferType,resize:a.resize})} +ub.prototype.h=function(){var b=this.input,a,c;a=this.L.h();this.c=this.L.c;this.$&&(c=(b[this.c++]<<24|b[this.c++]<<16|b[this.c++]<<8|b[this.c++])>>>0,c!==tb(a)&&q(Error("invalid adler-32 checksum")));return a};var vb=8;function wb(b,a){this.input=b;this.a=new (B?Uint8Array:Array)(32768);this.k=W.o;var c={},d;if((a||!(a={}))&&"number"===typeof a.compressionType)this.k=a.compressionType;for(d in a)c[d]=a[d];c.outputBuffer=this.a;this.K=new na(this.input,c)}var W=ra; +wb.prototype.g=function(){var b,a,c,d,e,f,g,k=0;g=this.a;b=vb;switch(b){case vb:a=Math.LOG2E*Math.log(32768)-8;break;default:q(Error("invalid compression method"))}c=a<<4|b;g[k++]=c;switch(b){case vb:switch(this.k){case W.NONE:e=0;break;case W.v:e=1;break;case W.o:e=2;break;default:q(Error("unsupported compression type"))}break;default:q(Error("invalid compression method"))}d=e<<6|0;g[k++]=d|31-(256*c+d)%31;f=tb(this.input);this.K.b=k;g=this.K.g();k=g.length;B&&(g=new Uint8Array(g.buffer),g.length<= +k+4&&(this.a=new Uint8Array(g.length+4),this.a.set(g),g=this.a),g=g.subarray(0,k+4));g[k++]=f>>24&255;g[k++]=f>>16&255;g[k++]=f>>8&255;g[k++]=f&255;return g};function xb(b,a){var c,d,e,f;if(Object.keys)c=Object.keys(a);else for(d in c=[],e=0,a)c[e++]=d;e=0;for(f=c.length;e -
- - - -
diff --git a/templates/index.html b/templates/index.html index 8fe543e64..83b87457f 100644 --- a/templates/index.html +++ b/templates/index.html @@ -34,6 +34,8 @@ + + {% endblock %} diff --git a/test/data/test.db b/test/data/test.db index a69b3b9fb..38f50c490 100644 Binary files a/test/data/test.db and b/test/data/test.db differ