\ Untitled - /g/pasta 2.4
From Gruff Partdridge, 4 Years ago, written in JavaScript.
Embed
  1. // ==UserScript==
  2. // @name           4chan Sound Player
  3. // @namespace      ms11
  4. // @description    Plays sounds posted on 4chan.  Supports various methods of circumventing the filter.  Based on ms11 version.
  5. // @match          *://boards.4chan.org/*
  6. // @match          *://images.4chan.org/*
  7. // @match          *://archive.foolz.us/*
  8. // @grant          GM_xmlhttpRequest
  9. // ==/UserScript==
  10.  
  11. // The sound script has been developed by many people; I do not know who all the authors are.
  12. // This script is based on the version at http://ms11.github.com/4chanSoundPlayer/
  13. // (itself based on Triangle's 4chan Sound Script dev)
  14. // In addition, code has been included from
  15. // https://raw.github.com/devongovett/png.js/ (decoding PNG images)
  16. // https://github.com/dnsev/4cs/ (decoding steganographic data in PNG images)
  17.  
  18. var chrome = (navigator.userAgent+'').indexOf(' Chrome/') != -1;
  19. var archive = (document.location+'').indexOf('boards.4chan.org') == -1;
  20.  
  21. function insertAfter(referenceNode, newNode)
  22. {
  23.     referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
  24. }
  25. function byClass(items, cl)
  26. {
  27.     for (var i = 0; i < items.length; i++)
  28.     {
  29.         if (items[i].classList.contains(cl))
  30.         {
  31.             return items[i];
  32.         }
  33.     }
  34.     return null;
  35. }
  36.  
  37. function s2ab(text)
  38. {
  39.     var foo = new ArrayBuffer(text.length);
  40.     var bar = new Uint8Array(foo);
  41.     for (var a = 0; a < text.length; a++)
  42.     {
  43.         bar[a] = text.charCodeAt(a);
  44.     }
  45.     return foo;
  46. }
  47.  
  48. function getPostID(o)
  49. {
  50.     var o = o.getAttribute('id');
  51.     if (!archive)
  52.     {
  53.         o = o.substr(1);
  54.     }
  55.     var ret = Number(o);
  56.     if(!ret){
  57.         ret = Number(o.split('_')[1].substr(1));
  58.     }
  59.     return ret;
  60. }
  61. function create(type, parent, attributes)
  62. {
  63.     var element = document.createElement(type);
  64.     for (var attr in attributes) {
  65.         element.setAttribute(attr, attributes[attr]);
  66.     }
  67.     if (parent) {
  68.         parent.appendChild(element);
  69.     }
  70.     return element;
  71. }
  72. function sectos(sec) {
  73.     var m = Math.floor(sec/60);
  74.     var s = +(sec-m*60);
  75.     return m+(s<10?":0":":")+s;
  76. }
  77. String.prototype.replaceAll = function(replaceTo,replaceWith) {
  78.     return this.replace(new RegExp(replaceTo,'g'),replaceWith);
  79. };
  80.  
  81. function get_chrome(url, callback, progressCb, userState)
  82. {
  83.     var xhr = new XMLHttpRequest();
  84.     xhr.open('GET', url, true);
  85.     xhr.overrideMimeType('text/plain; charset=x-user-defined');
  86.     xhr.responseType = 'arraybuffer';
  87.     if(progressCb)
  88.         xhr.onprogress = function(e){progressCb(e,userState);};
  89.     xhr.onload = function(e) {
  90.         if (this.status == 200) {
  91.             callback(this.response,userState);
  92.         }
  93.     };
  94.     xhr.send();
  95. }
  96.  
  97. function get_grease(url, callback, progressCb, userState) {
  98.     var arg = {
  99.         method: "GET",
  100.         url: url,
  101.         overrideMimeType: 'text/plain; charset=x-user-defined',
  102.         onload: function(e)
  103.         {
  104.             if (e.status == 200)
  105.             {
  106.                 var text = e.responseText;
  107.                 var foo = s2ab(text);
  108.                 callback(foo,userState);
  109.             }
  110.         }
  111.     };
  112.     if(progressCb)
  113.         arg.onprogress = function(e){progressCb(e,userState);};
  114.     GM_xmlhttpRequest(arg);
  115. }
  116. var xmlhttp = chrome ? get_chrome:get_grease;
  117. function loadAll(file,isUrl,cb) {
  118.     if (cb === undefined) cb = function() {};
  119.     if(isUrl){
  120.         xmlhttp(file,function(data,link) {
  121.             loadAllFromData(data,link,cb);
  122.         },onprogress, file);
  123.     }else{
  124.         for(var i = 0; i < file.length;i++){
  125.             var reader = new FileReader();
  126.             reader.onload = function() {
  127.                 loadAllFromData(this.result,"",cb);
  128.             };
  129.             reader.readAsArrayBuffer(file[i]);
  130.         }
  131.     }
  132. }
  133.  
  134. function loadSplitSounds(arr,cb,userState){
  135.     var data = {links:arr.slice(),sounddata:[]};
  136.     realLoadSplitSounds(data,arr[0].realhref,arr[0].splittag,cb,userState);
  137. }
  138. function realLoadSplitSounds(data,url,tag,cb,userState){
  139.     if(data.links.length < 1){
  140.         var len = 0;
  141.         for(var i = 0; i < data.sounddata.length;i++){
  142.             len += data.sounddata[i].byteLength;
  143.         }
  144.         var raw = new ArrayBuffer(len);
  145.         var rawa = new Uint8Array(raw);
  146.         var offs = 0;
  147.         for(var i = 0; i < data.sounddata.length;i++){
  148.             var sa = new Uint8Array(data.sounddata[i]);
  149.             rawa.set(sa,offs);
  150.             offs+=sa.length;
  151.         }
  152.         showPlayer();
  153.         if(cb)
  154.             cb(userState);
  155.         addMusic({data:raw,tag:tag},tag,url);
  156.     }else{
  157.         xmlhttp(data.links[0].realhref,function(resp){
  158.             findOgg(resp,data.links[0].tag, function(sound) {
  159.                 data.sounddata.push(sound.data);
  160.                 data.links = data.links.splice(1);
  161.                 realLoadSplitSounds(data,url,tag,cb,userState);
  162.             });
  163.         });
  164.     }
  165. }
  166. function rehyperlink(target,second) {
  167.     var list = target.getElementsByClassName('playerLoadAllLink');
  168.     for(var i = 0; i < list.length;i++){
  169.         if(list[i].rehypered) continue;
  170.         list[i].rehypered = true;
  171.         list[i].addEventListener('click',function(e) {
  172.             e.preventDefault();
  173.             e.target.innerHTML = " loading...";
  174.             if(this.splittag){
  175.                 var arr = playerSplitImages[this.splittag];
  176.                 loadSplitSounds(arr,function(rlink){
  177.                     rlink.innerHTML = " Load all sounds";
  178.                 },this);
  179.             }else{
  180.                 var a = null;
  181.                 if(!archive){
  182.                     var a = e.target.parentNode.parentNode.getElementsByClassName('fileThumb')[0];
  183.                 }else{
  184.                     a = byClass(e.target.parentNode.parentNode.parentNode.parentNode.getElementsByTagName('a'), 'thread_image_link');
  185.                 }
  186.                 if(a) {
  187.                     loadAll(a.href,true,function(){e.target.innerHTML = " Load all sounds"},
  188.                         function(pe){
  189.                             e.target.innerHTML = ' loading';
  190.                             if(pe.lengthComputable){
  191.                                 e.target.innerHTML += '(' + ~~((pe.loaded/pe.total)*100) + '%)';
  192.                             }
  193.                     });
  194.                 }
  195.             }
  196.         });
  197.     }
  198.     var links = target.getElementsByClassName('soundlink');
  199.     if(links.length < 1) {
  200.         if(second) return;
  201.         else
  202.         setTimeout(function() {rehyperlink(target, true); },200);
  203.     }
  204.     var post = target.getElementsByTagName(archive ? 'article':'blockquote')[0];
  205.     var a = null;
  206.     var p = null;
  207.     if (!archive) {
  208.         p = post;
  209.         a = byClass(target.getElementsByTagName('a'), 'fileThumb');
  210.         if (!a) return;
  211.     }else{
  212.         a = byClass(post.getElementsByTagName('a'), 'thread_image_link');
  213.         p = byClass(post.getElementsByTagName('div'), 'text');      
  214.         if (!a || !p) return;
  215.     }
  216.     for(var i = 0;i < links.length;i++){
  217.    
  218.         var link = links[i];
  219.  
  220.        
  221.         if(link.rehypered) continue;
  222.         link.rehypered = true;
  223.        
  224.         var sp = null;
  225.         if(sp = link.innerHTML.match(/(.*?)\.([0-9].*)/)){
  226.  
  227.             link.splittag = sp[1];
  228.             link.splitid = sp[2];
  229.             p.splittag = sp[1];
  230.         }
  231.  
  232.         link.realhref = a.href;
  233.         link.tag = link.innerHTML.replace("[","").replace("]","");
  234.         link.addEventListener('click', function(e) {
  235.             e.preventDefault();
  236.             if(this.splittag){
  237.                 var arr = playerSplitImages[this.splittag];
  238.                 loadSplitSounds(arr);
  239.             }else{
  240.                 this.innerHTML = '[loading]';
  241.                 xmlhttp(this.realhref, function(data,rlink) {
  242.                     rlink.innerHTML = '[' + rlink.tag + ']';
  243.                     showPlayer();
  244.                     findOgg(data, rlink.tag, function(sound) {
  245.                         addMusic(sound,rlink.tag,rlink.realhref);
  246.                     });
  247.                 },function(e,rlink){
  248.                     rlink.innerHTML = '[loading';
  249.                     if(e.lengthComputable){
  250.                         rlink.innerHTML += '(' + ~~((e.loaded/e.total)*100) + '%)';
  251.                     }
  252.                     rlink.innerHTML += ']';
  253.                 },this);
  254.             }
  255.         });
  256.     }
  257. }
  258. function hyperlinkone(target) {
  259.     var postname = archive ? 'article':'blockquote';
  260.     if(target.nodeName.toLowerCase() != postname) {
  261.         var elems = target.getElementsByTagName(postname);
  262.         for(var i = 0; i < elems.length; i++) {
  263.             hyperlinkone(elems[i]);
  264.         }
  265.     }else{
  266.         var repeat = true;
  267.         while (repeat) {
  268.             repeat = false;
  269.             var a = null;
  270.             var p = null;
  271.             if (!archive) {
  272.                 p = target;
  273.                 a = byClass(target.parentNode.getElementsByTagName('a'), 'fileThumb');
  274.                 if (!a) continue;
  275.             }else{
  276.                 a = byClass(target.getElementsByTagName('a'), 'thread_image_link');
  277.                 p = byClass(target.getElementsByTagName('div'), 'text');
  278.                
  279.                 if (!a || !p) continue;
  280.             }
  281.             for (var j = 0; j < p.childNodes.length; j++) {
  282.                 var match = null;
  283.                 var node = p.childNodes[j];
  284.                 if (node.nodeType != 3) {
  285.                     if(node.className != "spoiler" && node.className != 'quote') {
  286.                         continue;
  287.                     }else{
  288.                         for(var k = 0; k < node.childNodes.length; k++) {
  289.                            
  290.                             var subnode = node.childNodes[k];
  291.                             if(subnode.nodeType != 3) {continue;}
  292.                             if (!(match = subnode.nodeValue.match(/(.*)\[([^\]]+)\](.*)/))) {
  293.                                 continue;
  294.                             }
  295.                             repeat = true;
  296.                             var href = a.href;
  297.                             var code = match[2];
  298.                             var link = document.createElement('a');
  299.                             link.innerHTML = '[' + code + ']';
  300.                             link.className = 'soundlink';
  301.                             //link.href = href;
  302.                             link.href = "#";
  303.                             link.realhref = href;
  304.                             link.tag = code;
  305.                             var sp = null;
  306.                             if(sp = code.match(/(.*?)\.([0-9].*)/)){
  307.                                 if(!playerSplitImages.hasOwnProperty(sp[1])){
  308.                                     playerSplitImages[sp[1]] = [];
  309.                                 }
  310.                                
  311.                                 link.splittag = sp[1];
  312.                                 link.splitid = sp[2];
  313.                                 playerSplitImages[sp[1]].push(link);
  314.                                 p.splittag = sp[1];
  315.                             }
  316.                            
  317.                            
  318.                             addLoadAllLink(p);
  319.                             link.addEventListener('click', function(e) {
  320.                                
  321.                                 e.preventDefault();
  322.  
  323.                                 if(link.splittag){
  324.                                     var arr = playerSplitImages[link.splittag];
  325.                                     loadSplitSounds(arr);
  326.                                 }else{
  327.                                     this.innerHTML = '[loading]';
  328.                                     xmlhttp(link.realhref, function(data, rlink) {  
  329.                                         rlink.innerHTML = '[' + rlink.tag + ']';
  330.                                         showPlayer();
  331.                                         findOgg(data, rlink.tag, function(sound) {
  332.                                             addMusic(sound,rlink.tag,rlink.realhref);
  333.                                         });
  334.                                     },function(e,rlink){
  335.                                         rlink.innerHTML = '[loading';
  336.                                         if(e.lengthComputable){
  337.                                             rlink.innerHTML += '(' + ~~((e.loaded/e.total)*100) + '%)';
  338.                                         }
  339.                                         rlink.innerHTML += ']';
  340.                                     },this);
  341.                                 }
  342.                             });
  343.                             subnode.nodeValue = match[1];
  344.                             insertAfter(subnode, link);
  345.                             var text = document.createTextNode(match[3]);
  346.                             insertAfter(link, text);
  347.                         }
  348.                     }
  349.                 }else{
  350.                     if (!(match = node.nodeValue.match(/(.*)\[([^\]]+)\](.*)/))) {
  351.                         continue;
  352.                     }
  353.                     repeat = true;
  354.                    
  355.                    
  356.                     var href = a.href;
  357.                     var code = match[2];
  358.                     var link = document.createElement('a');
  359.                     link.innerHTML = '[' + code + ']';
  360.                     link.className = 'soundlink';
  361.    
  362.                     link.href = "#";
  363.                     link.realhref = href;
  364.                     link.tag = code;
  365.                     var sp = null;
  366.                     if(sp = code.match(/(.*?)\.([0-9].*)/)){
  367.                         if(!playerSplitImages.hasOwnProperty(sp[1])){
  368.                             playerSplitImages[sp[1]] = [];
  369.                         }
  370.                        
  371.                         link.splittag = sp[1];
  372.                         link.splitid = sp[2];
  373.                         playerSplitImages[sp[1]].push(link);
  374.                         p.splittag = sp[1];
  375.                     }
  376.                     addLoadAllLink(p);
  377.                    
  378.                     link.addEventListener('click', function(e) {    
  379.                         e.preventDefault();
  380.                         if(link.splittag){
  381.                             var arr = playerSplitImages[link.splittag];
  382.                             loadSplitSounds(arr);
  383.                         }else{
  384.                             this.innerHTML = '[loading]';
  385.                             xmlhttp(this.realhref, function(data, rlink) {
  386.                                 rlink.innerHTML = '[' + rlink.tag + ']';
  387.                                 showPlayer();
  388.                                 findOgg(data, rlink.tag, function(sound) {
  389.                                     addMusic(sound,rlink.tag,rlink.realhref);
  390.                                 });
  391.                             },function(e,rlink){
  392.                                 rlink.innerHTML = '[loading';
  393.                                 if(e.lengthComputable){
  394.                                     rlink.innerHTML += '(' + ~~((e.loaded/e.total)*100) + '%)';
  395.                                 }
  396.                                 rlink.innerHTML += ']';
  397.                             },this);
  398.                         }
  399.                        
  400.                     });
  401.                     node.nodeValue = match[1];
  402.                     insertAfter(node, link);
  403.                     var text = document.createTextNode(match[3]);
  404.                     insertAfter(link, text);
  405.                 }
  406.             }
  407.         }
  408.     }
  409. }
  410.  
  411.  
  412. function hyperlink() {
  413.     var posts = archive? 'article':'blockquote';
  414.     posts = document.getElementsByTagName(posts);
  415.     for (var i = 0; i < posts.length; i++) {
  416.         hyperlinkone(posts[i]);
  417.     }
  418. }
  419.  
  420. function addLoadAllLink(post) {
  421.     if(!post.hasAllLink){
  422.         var to = null;
  423.         if(!archive) {
  424.             var id = getPostID(post);
  425.            
  426.             var pi = document.getElementById('f'+id);
  427.             if(!pi && post.id.indexOf('_') > -1) {
  428.                 pi = document.getElementById(post.id.split('_')[0] + '_f'+id);
  429.             }
  430.             to = pi.getElementsByClassName('fileInfo')[0];
  431.         }else{
  432.             var head = post.parentNode.getElementsByTagName('header')[0];
  433.             head = head.getElementsByClassName('post_data')[0];
  434.             to = head.getElementsByClassName('post_controls')[0];
  435.         }
  436.         var loadAllLink = create('a',to, {"href":"#","class":"playerLoadAllLink"});
  437.         loadAllLink.innerHTML = " Load all sounds";
  438.         if(archive){
  439.             loadAllLink.classList.add('btnr');
  440.             loadAllLink.classList.add('parent');
  441.         }
  442.         loadAllLink.splittag = post.splittag;
  443.         loadAllLink.addEventListener('click',function(e) {
  444.             e.preventDefault();
  445.             e.target.innerHTML = " loading";
  446.             if(this.splittag){
  447.                 var arr = playerSplitImages[this.splittag];
  448.                 loadSplitSounds(arr,function(rlink){
  449.                     rlink.innerHTML = " Load all sounds";
  450.                 },this);
  451.             }else{
  452.                 var a = null;
  453.                 if(!archive){
  454.                     var a = e.target.parentNode.parentNode.getElementsByClassName('fileThumb')[0];
  455.                 }else{
  456.                     a = byClass(e.target.parentNode.parentNode.parentNode.parentNode.getElementsByTagName('a'), 'thread_image_link');
  457.                 }
  458.                 if(a) {
  459.                     loadAll(a.href,true,function(){e.target.innerHTML = " Load all sounds"},
  460.                     function(pe){
  461.                         e.target.innerHTML = ' loading';
  462.                         if(pe.lengthComputable){
  463.                             e.target.innerHTML += '(' + ~~((pe.loaded/pe.total)*100) + '%)';
  464.                         }
  465.                     });
  466.                 }
  467.             }
  468.         });
  469.         post.hasAllLink = true;
  470.     }
  471. }
  472.  
  473. var lastPost = null;    // last post that was hyperlink()ed
  474. var lastHyper = 0;      // unixtime*1000 for last hyperlink()
  475. var isPlayer = false;
  476. var playerDiv = null;
  477. var playerList = null;
  478. var playerTitle = null;
  479. var playerTime = null;
  480. var playerPlayer = null;
  481. var newWindow = null;
  482. var playerCurrentDuration = 0;
  483. var playerMovingListItem = null;
  484. var playerSaveData = null;
  485. var playerSettings = null;
  486. var playerStyle = null;
  487.  
  488. var playerListItemMenu = null;
  489. var playerVolume = null;
  490. var playerCurrentVolume = null;
  491. var playerSeekbar = null;
  492. var playerSeekbarCurrent = null;
  493.  
  494. var playerUserStyle = null;
  495. var playerSplitImages = {};
  496. var playerDefault = {right:0,bottom:0,shuffle:0,repeat:0,volume:1,compact:false,userCSS:{}};
  497. var playerSettingsHeader = null;
  498. function fixFFbug() {
  499.     if (!chrome && !playerPlayer.paused) {
  500.         // Workaround for Firefox bug #583444 [it's fixed in 19]
  501.         try { playerCurrentDuration = playerPlayer.buffered.end(0); }
  502.         catch(ex) { playerCurrentDuration = 0; }
  503.     }
  504. }
  505. function documentMouseDown(e) {
  506.     if(playerListMenu.parentNode) {
  507.         var parent = e.target.parentNode;
  508.         var hide = false;
  509.         do{
  510.             if(parent === playerListMenu) {
  511.                 hide = false;
  512.                 break;
  513.             }else if(parent === document.body) {
  514.                 hide = true;
  515.                 break;
  516.             }else{
  517.                 parent = parent.parentNode;
  518.             }
  519.         }while( true );
  520.         if(hide){
  521.             playerListMenu.parentNode.removeChild(playerListMenu);
  522.         }
  523.     }
  524.     if(playerListItemMenu.parentNode) {
  525.         var parent = e.target.parentNode;
  526.         var hide = false;
  527.         do{
  528.             if(parent == playerListItemMenu) {
  529.                 hide = false;
  530.                 break;
  531.             }else if(parent == document.body) {
  532.                 hide = true;
  533.                 break;
  534.             }else{
  535.                 parent = parent.parentNode;
  536.             }
  537.         }while(true);
  538.         if(hide){
  539.             playerListItemMenu.parentNode.removeChild(playerListItemMenu);
  540.         }
  541.     }
  542.     if(e.target == playerTitle || e.target==playerTime || e.target==playerHeader){
  543.         e.preventDefault();
  544.         playerHeader.down = true;
  545.         playerHeader.oldx = e.clientX;
  546.         playerHeader.oldy = e.clientY;
  547.     }else if(e.target == playerSettingsHeader){
  548.         e.preventDefault();
  549.         playerSettingsHeader.down = true;
  550.         playerSettingsHeader.oldx = e.clientX;
  551.         playerSettingsHeader.oldy = e.clientY;
  552.     }else if(e.target == playerCurrentVolume && !playerPlayer.error) {
  553.         e.preventDefault();
  554.         playerCurrentVolume.down = true;
  555.         playerCurrentVolume.oldx = e.clientX;
  556.     }else if(e.target == playerSeekbarCurrent && !playerPlayer.error) {
  557.         e.preventDefault();
  558.         playerSeekbarCurrent.down = true;
  559.         playerSeekbarCurrent.oldx = e.clientX;
  560.     }
  561. }
  562. function documentMouseUp(e) {
  563.     if(playerHeader.down){
  564.         e.preventDefault();
  565.         playerHeader.down = false;
  566.         putInsidePage();
  567.         setConf("right",playerDiv.style.right.replace("px",""));
  568.         setConf("bottom",playerDiv.style.bottom.replace("px",""));
  569.     }
  570.     if(playerSettingsHeader.down) {
  571.         e.preventDefault();
  572.         playerSettingsHeader.down = false;
  573.     }
  574.     if(playerCurrentVolume.down) {
  575.         e.preventDefault();
  576.         playerCurrentVolume.down = false;
  577.         setConf("volume",playerPlayer.volume);
  578.     }
  579.     if(playerSeekbarCurrent.down) {
  580.         e.preventDefault();
  581.         playerSeekbarCurrent.down = false;
  582.         var cl = Number(playerSeekbarCurrent.style.left.replace("px",""));
  583.         var max = Number(window.getComputedStyle(playerSeekbar).width.replace("px",""));
  584.         var width = Number(window.getComputedStyle(playerSeekbarCurrent).width.replace("px",""));
  585.         var n = cl/(max-width);
  586.         if ((chrome?playerPlayer.duration:playerCurrentDuration) !== 0) {
  587.                     playerPlayer.currentTime = (chrome?playerPlayer.duration:playerCurrentDuration) * n;
  588.         }      
  589.     }
  590. }
  591. function documentMouseMove(e) {
  592.     if(e.target == playerHeader || e.target == playerSettingsHeader){
  593.         e.preventDefault();
  594.     }
  595.     if(playerHeader.down) {
  596.         var cr = Number(playerDiv.style.right.replace("px",""));
  597.         var cb = Number(playerDiv.style.bottom.replace("px",""));
  598.         playerDiv.style.right = (cr + playerHeader.oldx - e.clientX) + "px";
  599.         playerDiv.style.bottom = (cb + playerHeader.oldy - e.clientY) + "px";
  600.         playerHeader.oldx = e.clientX;
  601.         playerHeader.oldy = e.clientY;
  602.  
  603.     }
  604.     if(playerSettingsHeader.down){
  605.         var cr = Number(playerSettings.style.right.replace("px",""));
  606.         var ct = Number(playerSettings.style.top.replace("px",""));
  607.         playerSettings.style.right = (cr + (playerSettingsHeader.oldx - e.clientX)) + "px";
  608.         playerSettings.style.top = (ct - (playerSettingsHeader.oldy - e.clientY)) + "px";
  609.         playerSettingsHeader.oldx = e.clientX;
  610.         playerSettingsHeader.oldy = e.clientY;
  611.     }
  612.     if(playerCurrentVolume.down) {
  613.         var cl = Number(playerCurrentVolume.style.left.replace("px",""));
  614.         var nl = (cl - (playerCurrentVolume.oldx - e.clientX));
  615.        
  616.         var max = Number(window.getComputedStyle(playerVolume).width.replace("px",""));
  617.         var width = Number(window.getComputedStyle(playerCurrentVolume).width.replace("px",""));
  618.         if(nl < 0 || nl > max-width) return;
  619.         playerPlayer.volume = nl/(max-width);
  620.         playerCurrentVolume.style.left = nl + "px";
  621.         playerCurrentVolume.oldx = e.clientX;
  622.     }
  623.    
  624.     if(playerSeekbarCurrent.down) {
  625.         var cl = Number(playerSeekbarCurrent.style.left.replace("px",""));
  626.         var nl = (cl - (playerSeekbarCurrent.oldx - e.clientX));
  627.        
  628.         var max = Number(window.getComputedStyle(playerSeekbar).width.replace("px",""));
  629.         var width = Number(window.getComputedStyle(playerSeekbarCurrent).width.replace("px",""));
  630.         if(nl < 0 || nl > max-width) return;
  631.         playerSeekbarCurrent.style.left = nl + "px";
  632.         playerSeekbarCurrent.oldx = e.clientX;
  633.     }
  634. }
  635.  
  636. function putInsidePage() {
  637.     if(playerDiv.clientHeight + Number(playerDiv.style.bottom.replace("px","")) > window.innerHeight) {
  638.         playerDiv.style.bottom = (window.innerHeight - playerDiv.clientHeight) + "px";
  639.     }else if(Number(playerDiv.style.bottom.replace("px","")) < 0) {
  640.         playerDiv.style.bottom = "0px";
  641.     }
  642.     if(playerDiv.clientWidth + Number(playerDiv.style.right.replace("px","")) > window.innerWidth) {
  643.         playerDiv.style.right = (window.innerWidth - playerDiv.clientWidth) + "px";
  644.     }else if(Number(playerDiv.style.right.replace("px","")) < 0) {
  645.         playerDiv.style.right = "0px";
  646.     }
  647. }
  648. function setConf(name,value) {
  649.     playerSaveData[name] = value;
  650.     localStorage.setItem('4chanSP', JSON.stringify(playerSaveData));
  651. }
  652. function loadConf() {
  653.     playerSaveData = JSON.parse(localStorage.getItem("4chanSP")||'null');
  654.     if(!playerSaveData) {
  655.         playerSaveData = playerDefault;
  656.     }else if(playerSaveData.css) {
  657.         setConf("css",undefined);
  658.         setConf("saveVer",undefined);
  659.     }else if(playerSaveData.userCSS && (playerSaveData.userCSS.length)){
  660.         setConf("userCSS",{});
  661.     }
  662.     if(!playerSaveData.compact){
  663.         setConf("compact",false);
  664.     }
  665. }
  666.  
  667.  
  668. function showPlayer() {
  669.     if(!isPlayer) {
  670.         loadConf();
  671.         playerDiv = create('div', undefined, {"id":"playerDiv","class":"playerWindow"});
  672.         playerDiv.style.right = playerSaveData.right+'px';
  673.         playerDiv.style.bottom = playerSaveData.bottom+'px';
  674.        
  675.        
  676.         playerHeader = create('div', playerDiv, {"id": "playerHeader"});
  677.         playerTitle = create('div', playerHeader, {"id": "playerTitle"});
  678.         playerTime = create('div', playerHeader, {"id": "playerTime"});
  679.         playerImageDiv = create('div', playerDiv, {"id": "playerImageDiv"});
  680.        
  681.         playerControls = create('div', playerDiv, {"id": "playerControls"});
  682.         playerVolumeSeekHeader = create('div', playerDiv, {"id": "playerVolumeSeekHeader"});
  683.         playerVolume = create('div', playerVolumeSeekHeader, {"id": "playerVolume"});
  684.         playerCurrentVolume = create('div',playerVolume, {"id": "playerCurrentVolume"});
  685.    
  686.         var scrollfunc = function(e) {
  687.             e.preventDefault();
  688.             var n = Number(playerCurrentVolume.style.left.replace("px",""));
  689.             if(e.detail < 0 || e.wheelDelta > 0) {
  690.                 n+=1;
  691.             }else if(e.detail > 0 || e.wheelDelta < 0) {
  692.                 n-=1;
  693.             }
  694.            
  695.            
  696.             var max = Number(window.getComputedStyle(playerVolume).width.replace("px",""));
  697.             var width = Number(window.getComputedStyle(playerCurrentVolume).width.replace("px",""));
  698.            
  699.             if(n < 0 || n > max-width)return;
  700.             playerCurrentVolume.style.left = n +"px";
  701.             playerPlayer.volume=n/(max-width);
  702.         };
  703.        
  704.        
  705.         playerVolume.addEventListener("DOMMouseScroll",scrollfunc);
  706.         playerVolume.addEventListener("mousewheel",scrollfunc);
  707.        
  708.         playerSeekbar = create('div', playerVolumeSeekHeader, {"id":"playerSeekbar"});
  709.         playerSeekbarCurrent = create('div', playerSeekbar, {"id":"playerSeekbarCurrent"});
  710.        
  711.         //
  712.         playerList = create('div', playerDiv, {"id":"playerList"});
  713.         playerControls2 = create('div',playerDiv, {"id": "playerControls2"});
  714.         playerList.addEventListener('dragover', function(e){
  715.             e.preventDefault();
  716.             e.dataTransfer.dropEffect = "move";
  717.             return false;    
  718.         });  
  719.         playerList.addEventListener('drop', function(e) {
  720.             e.stopPropagation();
  721.             e.preventDefault();
  722.             if(e.dataTransfer.files.length > 0) {
  723.                 loadAll(e.dataTransfer.files,false);
  724.             }else{
  725.                 loadAll(e.dataTransfer.getData("text/plain"),true);
  726.             }
  727.         });
  728.         playerControls2.addEventListener('dragover', function(e){
  729.             e.preventDefault();
  730.             e.dataTransfer.dropEffect = "move";
  731.             return false;    
  732.         });  
  733.         playerControls2.addEventListener('drop', function(e) {
  734.             e.stopPropagation();
  735.             e.preventDefault();
  736.             if(e.dataTransfer.files.length > 0) {
  737.                 loadAll(e.dataTransfer.files,false);
  738.             }else{
  739.                 loadAll(e.dataTransfer.getData("text/plain"),true);
  740.             }
  741.         });
  742.         playerPlayer = create('audio', playerDiv, {"id": "playerPlayer"});
  743.         //playerCurrentVolume.style.left = (playerPlayer.volume*170) + "px";
  744.         playerPlayer.addEventListener('ended', function() {playerPlayPause.innerHTML = ">"; nextMusic(true);});
  745.         playerPlayer.volume = playerSaveData.volume;
  746.         //copy from Triangle's script
  747.         playerPlayer.addEventListener('play', function(e) {
  748.             fixFFbug();
  749.         });
  750.         //end
  751.         fixFFbug();
  752.         playerPlayer.addEventListener('timeupdate', function(e) {
  753.             if(!playerSeekbarCurrent.down){
  754.             if(this.currentTime > 0){
  755.                 var max = Number(window.getComputedStyle(playerSeekbar).width.replace("px",""));
  756.                 var width = Number(window.getComputedStyle(playerSeekbarCurrent).width.replace("px",""));
  757.                
  758.                 var x = (this.currentTime/(chrome?this.duration:playerCurrentDuration)) * (max-width);
  759.                 if(x > max-width) {
  760.                     fixFFbug();
  761.                     playerSeekbarCurrent.style.left = "0px";
  762.                     return;
  763.                 }
  764.                 playerSeekbarCurrent.style.left = x + "px";
  765.                 playerTime.innerHTML = sectos(Math.round(this.currentTime)) + "/" + sectos(Math.round(chrome?this.duration:playerCurrentDuration)) || "[unknown]";
  766.             }
  767.             }
  768.         });
  769.        
  770.         playerPlayer.addEventListener('play', function() {playerPlayPause.innerHTML="| |";});
  771.         playerPlayer.addEventListener('pause', function() {playerPlayPause.innerHTML=">";});
  772.         playerRepeat = create('a', playerControls2, {"href": "#"});
  773.         switch(playerSaveData.repeat){
  774.             case 1: playerRepeat.innerHTML = "[RA]"; playerRepeat.title = "Repeat all"; break;
  775.             case 2: playerRepeat.innerHTML = "[R1]"; playerRepeat.title = "Repeat one"; break;
  776.             case 0: playerRepeat.innerHTML = "[RO]"; playerRepeat.title = "Repeat off"; break;
  777.         }
  778.         playerRepeat.addEventListener('click', function(e) {
  779.             e.preventDefault();
  780.             switch(playerSaveData.repeat){
  781.                 case 0: setConf("repeat",1); playerRepeat.innerHTML = "[RA]"; playerRepeat.title = "Repeat all"; break;
  782.                 case 1: setConf("repeat",2); playerRepeat.innerHTML = "[R1]"; playerRepeat.title = "Repeat one"; break;
  783.                 case 2: setConf("repeat",0); playerRepeat.innerHTML = "[RO]"; playerRepeat.title = "Repeat off"; break;
  784.             }
  785.         });
  786.        
  787.        
  788.         playerShuffle = create('a', playerControls2, {"href": "#"});
  789.         playerShuffle.title = playerSaveData.shuffle ? "Shuffle" : "By order";
  790.         playerShuffle.innerHTML = playerSaveData.shuffle ? "[SH]" : "[BO]";
  791.         playerShuffle.addEventListener('click', function(e) {
  792.             e.preventDefault();
  793.             setConf("shuffle",!playerSaveData.shuffle);
  794.             if(playerSaveData.shuffle) {
  795.                 playerShuffle.title = "Shuffle";
  796.                 playerShuffle.innerHTML = "[SH]";
  797.             }else{
  798.                 playerShuffle.title = "By order";
  799.                 playerShuffle.innerHTML = "[BO]";
  800.             }
  801.         });
  802.        
  803.        
  804.         playerClose = create('a', playerDiv, {"id":"playerClose","href":"#"});
  805.         playerClose.innerHTML="[X]";
  806.         playerClose.addEventListener('click', function(e) {
  807.             e.preventDefault();
  808.            
  809.             //localStorage.setItem('4chanSP', JSON.stringify(playerSaveData));
  810.                    
  811.             document.body.removeChild(playerDiv);
  812.             playerDiv = null;
  813.             isPlayer = false;
  814.         });
  815.        
  816.        
  817.    
  818.        
  819.         playerChangeMode = create('a', playerControls2, {"id": "playerChangeMode", "href": "#"});
  820.         playerChangeMode.innerHTML = "[M]";
  821.         playerChangeMode.title = "Change view";
  822.         playerChangeMode.addEventListener('click', function(e) {e.preventDefault(); swmode();});
  823.  
  824.        
  825.        
  826.         playerPrev = create('a', playerControls, {"href": "#", "class":"playerControlLink"});
  827.         playerPrev.innerHTML = "|<<";
  828.         playerPrev.addEventListener('click', function(e) {
  829.             e.preventDefault();
  830.             prevMusic();
  831.         });
  832.         playerBackward = create('a', playerControls, {"href": "#", "class":"playerControlLink"});
  833.         playerBackward.innerHTML = "<<";
  834.         playerBackward.addEventListener('click', function(e) {
  835.             e.preventDefault();
  836.             playerPlayer.currentTime -= 5;
  837.         });
  838.         playerPlayPause = create('a', playerControls, {"href": "#", "class":"playerControlLink"});
  839.         playerPlayPause.innerHTML = ">";
  840.         playerPlayPause.addEventListener('click', function(e) {
  841.             e.preventDefault();
  842.             if(playerPlayer.paused)
  843.                 playerPlayer.play();
  844.             else
  845.                 playerPlayer.pause();
  846.         });
  847.         playerForward = create('a', playerControls, {"href": "#", "class":"playerControlLink"});
  848.         playerForward.innerHTML = ">>";
  849.         playerForward.addEventListener('click', function(e) {
  850.             e.preventDefault();
  851.             playerPlayer.currentTime += 5;
  852.         });
  853.         playerNext = create('a', playerControls, {"href": "#", "class":"playerControlLink"});
  854.         playerNext.innerHTML = ">>|";
  855.         playerNext.addEventListener('click', function(e) {
  856.             e.preventDefault();
  857.             nextMusic(false);
  858.         });
  859.        
  860.         playerStyleSettingsButton = create('a', playerDiv, {"id":"playerStyleSettingsButton","href":"#"});
  861.         playerStyleSettingsButton.innerHTML="[S]";
  862.         playerStyleSettingsButton.addEventListener('click', function(e) {
  863.             e.preventDefault();
  864.             if(playerSettings.style.display == "none")
  865.                 playerSettings.style.display = "block";
  866.             else{
  867.                 playerSettings.style.display = "none";
  868.                 localStorage.setItem('4chanSP', JSON.stringify(playerSaveData));
  869.             }
  870.         });
  871.         playerSettings = create('table', playerDiv, {"id":"playerSettings","class":"playerWindow"});
  872.         playerSettings.style.right = "210px";
  873.         playerSettings.style.top = "0px";
  874.         playerSettings.style.display = "none";
  875.         var tbody = create('tbody', playerSettings);
  876.         var headerrow = create('tr', tbody);
  877.         playerSettingsHeader = create('td', headerrow,{"colspan":2});
  878.         playerSettingsHeader.innerHTML = "4chan Sounds Player Style Settings";
  879.         playerSettingsHeader.style.textAlign="center";
  880.         playerSettingsHeader.style.cursor = "move";
  881.  
  882.         var data = [{name:"Text color",format:"CSS color value",id:"LinkColor",sets:"#playerCurrentVolume, #playerSeekbarCurrent {background-color:%1} .playerWindow > * > * {color:%1 !important;} .playerWindow > * {color:%1 !important;} .playerWindow a {color:%1 !important;} .playerWindow a:visited {color:%1 !important;}"},
  883.                     {name:"Control hover color",format:"CSS color value",id:"HoverColor",sets:".playerWindow a:hover, .playerListItemTag:hover{color:%1 !important;} #playerCurrentVolume:hover, #playerSeekbarCurrent:hover {background: %1;}"},
  884.                     {name:"Background color",format: "CSS color value",id:"BGColor",sets:".playerWindow {background-color:%1 !important}"},
  885.                     {name:"Playlist size",format:"Width x Height",id:"PlaylistSize",func: function(value) {var data=value.split('x'); data[0]=data[0].trim(); data[1]=data[1].trim(); return '#playerList {'+(data[0]?'width:'+data[0]+'px;':'') + (data[1]?' height:'+data[1]+'px;}':'}');}},
  886.                     {name:"Playlist margins",format:"left,right,top,bottom", id:"PlaylistMargins", func: function(value) {var data=value.split(','); return '#playerList {'+(data[0]?'margin-left:'+data[0]+'px;':'') + (data[1]?'margin-right:'+data[1]+'px;':'') + (data[2]?'margin-top:'+data[2]+'px;':'') + (data[3]?'margin-bottom:'+data[3]+'px;':'')+'}';}},
  887.                     {name:"List item background color", format:"CSS color value", id:"ListItemBGColor",sets:".playerListItem{background-color:%1}"},
  888.                     {name:"Played list item bg color", format:"CSS color value", id:"PlayedListItemBGColor",sets:".playerListItem[playing=true]{background-color:%1}"},
  889.                     {name:"Volume slider width", id:"VolumeSliderWidth", sets:"#playerCurrentVolume{width:%1px}"},
  890.                     {name:"Seekbar slider width", id:"SeekbarCurrentWidth", sets:"#playerSeekbarCurrent{width:%1px}"}];
  891.         for(var i = 0; i < data.length;i++){
  892.             var tr = create('tr',tbody);
  893.             var td = create('td', tr,{"class":"playerSettingLabel"});
  894.             td.innerHTML = data[i].name;
  895.             if(!data[i].sets && !data[i].func) continue;
  896.             if(data[i].format) {
  897.                 td.style.cursor = "help";
  898.                 td.title = data[i].format;
  899.             }
  900.             td = create('td',tr);
  901.             var input = create('input', td);
  902.             input.classList.add('playerSettingsInput');
  903.             input.id = "playerSettings"+data[i].id;
  904.             input.realid = data[i].id;
  905.             if(playerSaveData.userCSS && playerSaveData.userCSS[input.realid]){
  906.                 input.value = playerSaveData.userCSS[input.realid];
  907.             }
  908.             input.sets = data[i].sets;
  909.             input.func = data[i].func;
  910.             input.addEventListener('change',function(){
  911.                 updateUserCSS(this);
  912.             });
  913.         }
  914.        
  915.        
  916.         playerListMenu = create('div', null, {"id": "playerListMenu","class":"playerWindow"});
  917.         playerListMenuDelete = create('a', playerListMenu, {"href":"#","class":"playerListItemMenuLink"});
  918.         playerListMenuDelete.innerHTML = "Remove all...";
  919.         playerListMenuDelete.addEventListener('click', function(e) {
  920.             e.preventDefault();
  921.             if(confirm('Are you sure?')){
  922.                 var items = playerList.getElementsByTagName('li');
  923.                 while(items.length > 0){
  924.                     items[items.length-1].remove();
  925.                 }
  926.             }
  927.             playerListMenu.parentNode.removeChild(playerListMenu);
  928.         });
  929.         playerListMenuAddLocal = create('a', playerListMenu, {"class":"playerListItemMenuLink"});
  930.         playerListMenuAddLocal.innerHTML = "Add local files...";
  931.         playerListMenuAddLocalInput = create('input', playerListMenuAddLocal, {"type":"file","id":"playerListMenuAddLocalInput","multiple":"true"});
  932.         playerListMenuAddLocalInput.addEventListener('change', function(e) {
  933.             loadAll(e.target.files,false);
  934.             playerListMenu.parentNode.removeChild(playerListMenu);
  935.         });
  936.         playerList.addEventListener('contextmenu', function(e) {
  937.             if(e.target == playerList){
  938.                 e.preventDefault();
  939.                 if(playerListMenu.parentNode) playerListMenu.parentNode.removeChild(playerListMenu);
  940.                 document.body.appendChild(playerListMenu);
  941.                 playerListMenu.style.left = e.clientX + 5 + "px";
  942.                 playerListMenu.style.top = e.clientY + 5 + "px";
  943.             }
  944.         });
  945.        
  946.         playerControls2.addEventListener('contextmenu', function(e) {
  947.             if(e.target == playerControls2){
  948.                 e.preventDefault();
  949.                 if(playerListMenu.parentNode) playerListMenu.parentNode.removeChild(playerListMenu);
  950.                 document.body.appendChild(playerListMenu);
  951.                 playerListMenu.style.left = e.clientX + 5 + "px";
  952.                 playerListMenu.style.top = e.clientY + 5 + "px";
  953.             }
  954.         });
  955.         playerListItemMenu = create('div', null, {"id": "playerListItemMenu","class":"playerWindow"});
  956.         playerListItemMenuDelete = create('a', playerListItemMenu, {"href":"#","class":"playerListItemMenuLink"});
  957.  
  958.         playerListItemMenuDelete.innerHTML = "Delete";
  959.         playerListItemMenuDelete.addEventListener('click',function(e) {
  960.             e.preventDefault();
  961.             playerListItemMenu.item.remove();
  962.             playerListItemMenu.parentNode.removeChild(playerListItemMenu);
  963.         });
  964.  
  965.         playerListItemMenuMove = create('a', playerListItemMenu, {"href":"#","class":"playerListItemMenuLink"});
  966.         playerListItemMenuMove.innerHTML = "Move";
  967.         playerListItemMenuMove.addEventListener('click',function(e) {
  968.             e.preventDefault();
  969.             playerListItemMenu.item.move();
  970.             playerListItemMenu.parentNode.removeChild(playerListItemMenu);
  971.         });
  972.        
  973.         playerListItemMenu.save = create('a', playerListItemMenu, {"href":"#","class":"playerListItemMenuLink"});
  974.         playerListItemMenu.save.innerHTML = "Save...";
  975.         playerListItemMenu.save.addEventListener('click',function(e) {
  976.             if(!chrome){
  977.             e.preventDefault();
  978.             window.open(this.href);
  979.             }
  980.         });
  981.        
  982.        
  983.        
  984.         playerHeader.down = false;
  985.         playerSettingsHeader.down = false;
  986.         document.addEventListener('mousedown',documentMouseDown);
  987.         document.addEventListener('mouseup',documentMouseUp);
  988.         document.addEventListener('mousemove',documentMouseMove);
  989.        
  990.        
  991.         isPlayer = true;
  992.         document.body.appendChild(playerDiv);
  993.         addCSS();
  994.         swmode(playerSaveData.compact);
  995.        
  996.     }
  997. }
  998.  
  999. function swmode(tocompact) {
  1000.     if(tocompact === undefined) {
  1001.         tocompact = !playerSaveData.compact;
  1002.         playerSaveData.compact = !playerSaveData.compact;
  1003.     }
  1004.     var s = tocompact ? "none" : "block";
  1005.     playerImageDiv.style.display = s;
  1006.     playerList.style.display = s;
  1007.     playerControls2.style.marginTop = tocompact ? "15px" : "0px";
  1008.     putInsidePage();
  1009. }
  1010. function showMoverTargets(show) {
  1011.     if(show === undefined) {
  1012.         show = true;
  1013.     }
  1014.     var mvs = document.getElementsByClassName('playerListItemMoveTarget');
  1015.     for(var i = 0; i < mvs.length;i++) {
  1016.         if(show && mvs[i].parentNode == playerMovingListItem) continue;
  1017.         mvs[i].style.display = (show ? "block" : "none");
  1018.     }
  1019. }
  1020.  
  1021. function addMusic(resp,tag,url) {
  1022.     data = resp.data;
  1023.     var list = playerList;
  1024.     var item = create('li',list, {"class":"playerListItem"});
  1025.     //item.innerHTML = tag;
  1026.     var tagelem = create('span',item,{"class":"playerListItemTag"});
  1027.     tagelem.innerHTML = tag;
  1028.     tagelem.title = tag;
  1029.     if(resp.tag) {
  1030.         var realtag = tag.replace(' ','');
  1031.         if(resp.tag != realtag && resp.tag != tag){
  1032.         tagelem.innerHTML = "(!) " + tag;
  1033.         tagelem.title = "'" + tag + "' was not found, playing '" + resp.tag + "' instead.";
  1034.         }
  1035.     }
  1036.     item.move = function() {
  1037.         playerMovingListItem = this;
  1038.         showMoverTargets(false);
  1039.         showMoverTargets();
  1040.     };
  1041.     item.remove = function() {
  1042.         if(this.getAttribute('playing') == "true") {
  1043.             playerPlayer.pause();
  1044.             playerPlayer.src = "";
  1045.             playerImageDiv.innerHTML = "";
  1046.             playerTitle.innerHTML = "";
  1047.             playerTime.innerHTML = "";
  1048.             playerSeekbarCurrent.style.left = "0px";
  1049.         }
  1050.         (window.webkitURL || window.URL).revokeObjectURL(this.bloburl);
  1051.         if (/^blob:/.test(this.img.src)) (window.webkitURL || window.URL).revokeObjectURL(this.img.src);
  1052.         this.parentNode.removeChild(this);
  1053.     };
  1054.     item.addEventListener('contextmenu',function(e) {
  1055.         e.preventDefault();
  1056.         if(playerListItemMenu.parentNode)
  1057.             playerListItemMenu.parentNode.removeChild(playerListItemMenu);
  1058.  
  1059.         document.body.appendChild(playerListItemMenu);
  1060.         playerListItemMenu.style.left = e.clientX + 5 + "px";
  1061.         playerListItemMenu.style.top = e.clientY + 5 + "px";
  1062.         playerListItemMenu.item = this;
  1063.         playerListItemMenu.save.href = this.bloburl;
  1064.         playerListItemMenu.save.setAttribute("download",this.tag + ".ogg");
  1065.     });
  1066.     var mover = create('div', item, {"class":"playerListItemMoveTarget"});
  1067.     mover.style.display = "none";
  1068.     var mvl = create('a', mover, {"href":"#"});
  1069.     mvl.addEventListener('click',function(e) {
  1070.         e.preventDefault();
  1071.         var li = e.target.parentNode.parentNode;
  1072.         playerMovingListItem.parentNode.removeChild(playerMovingListItem);
  1073.         insertAfter(li,playerMovingListItem);
  1074.         showMoverTargets(false);
  1075.     });
  1076.     mvl.innerHTML = "[here]";
  1077.     var blob = new Blob([data],{type: 'audio/ogg'});
  1078.     item.bloburl = (window.webkitURL || window.URL).createObjectURL(blob);
  1079.     item.tag = tag;
  1080.     item.img = resp.img || create('img', null, {"id": "playerImage", "src": url});
  1081.     item.tagelem = tagelem;
  1082.     tagelem.addEventListener('click', function(e) {
  1083.         if(e.target.parentNode.bloburl){
  1084.             var items = list.getElementsByTagName('li');
  1085.             for(var i in items) {
  1086.                 if(items[i].setAttribute)
  1087.                 items[i].setAttribute("playing",false);
  1088.             }
  1089.             e.target.parentNode.setAttribute("playing",true);
  1090.            
  1091.             playerPlayer.src = e.target.parentNode.bloburl;
  1092.             playerTitle.innerHTML = e.target.parentNode.tag;
  1093.             playerTitle.title = e.target.parentNode.tag;
  1094.             playerPlayer.play();
  1095.             playerCurrentVolume.style.left = (playerPlayer.volume * 55)+"px";
  1096.             playerImageDiv.innerHTML = "";
  1097.             playerImageDiv.appendChild(e.target.parentNode.img);
  1098.            
  1099.         }
  1100.     });
  1101.     if(playerPlayer.paused) { tagelem.click(); }
  1102. }
  1103.    
  1104. function prevMusic() {
  1105.     var items = playerList.getElementsByTagName('li');
  1106.     for(var i = 0; i < items.length;i++)
  1107.     {
  1108.         if(items[i].getAttribute("playing") == "true")
  1109.         {
  1110.             if(playerPlayer.currentTime < 3) {
  1111.                 if(i === 0)
  1112.                     items[items.length-1].tagelem.click();
  1113.                 else
  1114.                     items[i-1].tagelem.click();
  1115.                 return;
  1116.             }else{
  1117.                 items[i].tagelem.click();
  1118.                 return;
  1119.             }
  1120.         }
  1121.     }
  1122.     if(items.length > 0) items[0].tagelem.click();
  1123. }
  1124.  
  1125. function nextMusic(auto) {
  1126.     var items = playerList.getElementsByTagName('li');
  1127.     for(var i = 0; i < items.length;i++)
  1128.     {
  1129.         if(items[i].getAttribute("playing") == "true")
  1130.         {
  1131.             if(auto && playerSaveData.repeat == 2) {
  1132.                 items[i].tagelem.click(); return;
  1133.             }
  1134.             if(playerSaveData.shuffle && items.length > 1) {
  1135.                 var rnd = Math.floor(Math.random()*items.length);
  1136.                 while(rnd == i) {
  1137.                     rnd = Math.floor(Math.random()*items.length);
  1138.                 }
  1139.                 items[rnd].tagelem.click(); return;
  1140.             }
  1141.             if(i == (items.length - 1)) {
  1142.                 if(auto && playerSaveData.repeat === 0){ return;}
  1143.                 items[0].tagelem.click();
  1144.             } else {
  1145.                 items[i+1].tagelem.click();
  1146.             }
  1147.             return;
  1148.         }
  1149.     }
  1150.     if(items.length > 0) items[0].tagelem.click();
  1151. }
  1152. function updateUserCSS(input) {
  1153.     if(input){
  1154.         if(!playerSaveData.userCSS) {
  1155.             playerSaveData.userCSS = {};
  1156.         }
  1157.         playerSaveData.userCSS[input.realid] = input.value;
  1158.     }
  1159.     if(!playerUserStyle && playerSaveData.userCSS) {
  1160.         playerUserStyle = document.createElement('style');
  1161.         playerUserStyle.setAttribute('type', 'text/css');
  1162.         document.getElementsByTagName('head')[0].appendChild(playerUserStyle);
  1163.     }
  1164.     if(playerUserStyle){
  1165.         playerUserStyle.innerHTML = "";
  1166.         var table = document.getElementById('playerSettings');
  1167.         var elems = table.getElementsByTagName('input');
  1168.         for(var i = 0; i < elems.length;i++){
  1169.             if(elems[i].value){
  1170.                 if(elems[i].sets && playerSaveData.userCSS[elems[i].realid]){
  1171.                     var add = (playerSaveData.userCSS.length<1?"":" ")+elems[i].sets.replaceAll('%1',playerSaveData.userCSS[elems[i].realid]);
  1172.                     playerUserStyle.innerHTML += add;
  1173.                 }
  1174.                 else if(elems[i].func && playerSaveData.userCSS[elems[i].realid]){
  1175.                     playerUserStyle.innerHTML += (playerSaveData.userCSS.length<1?"":" ")+ elems[i].func(playerSaveData.userCSS[elems[i].realid]);
  1176.                 }
  1177.             }
  1178.         }
  1179.     }
  1180. }
  1181.  
  1182. function addCSS() {
  1183.     if(!playerStyle){
  1184.     playerStyle = document.createElement('style');
  1185.     playerStyle.setAttribute('type', 'text/css');
  1186.     playerStyle.innerHTML ='#playerList {margin-top: 15px; width: 180px; height: 200px; overflow: auto; margin-left:10px; margin-right:10px;}'+
  1187.             '.playerWindow {font-size: 12px; line-height:15px; color: darkgrey; background: #e7e7e7; position: fixed; z-index: 20;}'+
  1188.             '#playerHeader {height: 30px; cursor: move; text-align:center; position: relative; right: 0px; top: 0px;}'+
  1189.             '#playerControls {display: block; text-align: center;}'+
  1190.             '.playerListItem {cursor:pointer;, padding-top: 1px; list-style: none;}'+
  1191.             '.playerListItemMoveTarget {width:180px; height: 10px; font-size: 10px !important; text-align: center; margin-top: -2px;}'+
  1192.             '#playerImage {max-height: 120px; max-width: 180px; display: block; margin-left: auto; margin-right: auto;}'+
  1193.             '#playerClose {top: 0px; right: 0px; position: absolute; font-size: 10px; display: block; text-align: right; z-index: 10;}'+
  1194.             '#playerStyleSettingsButton {top: 0px; left: 0px; position: absolute; font-size: 10px; display: block; text-align: right; z-index: 10;}'+
  1195.             '#playerToggleSet {top: 0px; left: 0px; position: absolute; font-size: 10px; display: block; text-align: right; z-index: 10;}'+
  1196.             '#playerChangeMode, .playerListItemDelete, .playerListItemMove {float:right;}'+
  1197.             '.playerWindow a {color: darkgray !important; text-decoration: none !important;} .playerWindow a:visited {color: darkgray !important;} .playerWindow a:hover {color: black !important;}'+
  1198.             '#playerVolume {padding-top: 7px; height: 14px; width: 60px; display:inline-block;}'+
  1199.             '#playerVolumeSeekHeader {margin-left: auto; margin-right:auto; width:180px; background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALQAAAAHCAYAAAChk2fpAAAAAXNSR0IArs4c6QAAAJpJREFUWMPtV0kKACEMq4N/7Bv7SudUKOKCjo5UkpvWrTGGGkQkERExc6BFEJFk18vb2lfatzR25blW53oCmkeei+0fjWl7J/9/cBJPbe6dwJMX1+JrNnbLXTw7FmXmoC8GggXcCxrYBzWLvISwLjsbu8F4IiTiU9Q1EX4pR1ByDHxeAMC1Q7devK2xS/HaLx7oc9OK9+be4NIvFNCOIPRVVS4AAAAASUVORK5CYII="); background-repeat: no-repeat;}'+
  1200.             '#playerCurrentVolume {height: 14px; width: 5px; position:relative; display:block; background: darkgrey;}'+
  1201.             '#playerSeekbar {padding-top: 7px; height: 14px; width: 120px; display:inline-block;}'+
  1202.             '#playerSeekbarCurrent {height: 14px; width: 5px; position:relative; display:block; background: darkgrey;}'+
  1203.             '#playerCurrentVolume:hover, #playerSeekbarCurrent:hover {background: black;}'+
  1204.             '.playerControlLink {margin-left: 2px; margin-right:2px;}'+
  1205.             '.playerListItemTag:hover {color: black}'+
  1206.             '.playerListItemTag {margin-left: 4px; margin-right: 4px; display:block;}'+
  1207.             '#playerTitle {width: 160px; height:15px; overflow:hidden; margin-left:auto; margin-right:auto;}'+
  1208.             '#playerTime {width:160px; height:15px; overflow:hidden; margin-left:auto; margin-right:auto;}'+
  1209.             '#playerSettings {background: #e7e7e7; position: absolute; max-width:none;}'+
  1210.             '#playerSettings > tbody {display:block; padding: 0 10px 10px;}'+
  1211.             '#playerListMenu, #playerListItemMenu {padding: 2px 3px; position: fixed; background: #e7e7e7;}'+
  1212.             '.playerListItemMenuLink {width: 85px; height: 14px; display:block; overflow:hidden; overflow:hidden;}'+
  1213.             '#playerListMenuAddLocalInput{-moz-transform: scale(5) translateX(-140%); opacity: 0; width: 100%;}';
  1214.     document.getElementsByTagName('head')[0].appendChild(playerStyle);
  1215.     }
  1216.     updateUserCSS();
  1217. }
  1218.  
  1219. // General file extraction code
  1220. function s2a(text) {
  1221.     var a = [];
  1222.     for (var i = 0; i < text.length; i++) a.push(text.charCodeAt(i));
  1223.     return a;
  1224. }
  1225.  
  1226. function matches(x, pos, y) {
  1227.     if (pos + y.length > x.length) return false;
  1228.     for (var i = 0; i < y.length; i++) {
  1229.         if (x[pos+i] != y[i]) return false;
  1230.     }
  1231.     return true;
  1232. }
  1233.  
  1234. function SoundsArchive(files, imgDefault) {
  1235.     this.files = files;
  1236.     this.imgs = {};
  1237.     this.sounds = [];
  1238.     for (var i = 0; i < files.length; i++) {
  1239.         var tagImg = files[i].Name.match(/^(.*)\.(gif|jpe?g|png)$/i);
  1240.         if (tagImg) {
  1241.             var img = blobImage(files[i].Data);
  1242.             this.imgDefault = this.imgDefault || img;
  1243.             this.imgs[tagImg[1]] = img;
  1244.         }
  1245.         var tag = files[i].Name.match(/^(.*)\.og[ag]$/i);
  1246.         if (tag) {
  1247.             this.sounds.push({data: files[i].Data, tag: tag[1]});
  1248.         }
  1249.     }
  1250.     this.imgDefault = this.imgDefault || imgDefault;
  1251. }
  1252.  
  1253. SoundsArchive.prototype.getImg = function(tags) {
  1254.     for (var i = 0; i < tags.length; i++) {
  1255.         if (tags[i] in this.imgs) return this.imgs[tags[i]]();
  1256.     }
  1257.     if (typeof(this.imgDefault) == "function") this.imgDefault = this.imgDefault();
  1258.     return this.imgDefault;
  1259. }
  1260.  
  1261. SoundsArchive.prototype.getSound = function(tag) {
  1262.     for (var i = 0; i < this.sounds.length; i++) {
  1263.         if (this.sounds[i].tag == tag) {
  1264.             this.sounds[i].img = this.getImg([this.sounds[i].tag, tag]);
  1265.             return this.sounds[i];
  1266.         }
  1267.     }
  1268.     this.sounds[0].img = this.getImg([this.sounds[0].tag, tag]);
  1269.     return this.sounds[0];
  1270. }
  1271.  
  1272. function blobImage(data, cb) {
  1273.     return function() {
  1274.         var type = "image/jpeg";
  1275.         if (data[0] == 0x47) type = "image/gif";
  1276.         if (data[0] == 0x89) type = "image/png";
  1277.         var blob = new Blob([data], {type: type});
  1278.         var img = new Image();
  1279.         img.id = "playerImage";
  1280.         img.src = URL.createObjectURL(blob);
  1281.         return img;
  1282.     };
  1283. }
  1284.  
  1285. function loadAllFromData(raw, link, cb) {
  1286.     findFiles(new Uint8Array(raw), function(sa) {
  1287.         if (sa.sounds.length > 0) {
  1288.             showPlayer();
  1289.             for (var i = 0; i < sa.sounds.length; i++) {
  1290.                 sa.sounds[i].img = sa.getImg([sa.sounds[i].tag]);
  1291.                 addMusic(sa.sounds[i], sa.sounds[i].tag, link);
  1292.             }
  1293.             cb();
  1294.         }
  1295.     });
  1296. }
  1297.  
  1298. function findOgg(raw, tag, cb) {
  1299.     findFiles(new Uint8Array(raw), function(sa) {
  1300.         if (sa.sounds.length > 0) cb(sa.getSound(tag));
  1301.     });
  1302. }
  1303.  
  1304. // Determine type of embedding
  1305. function findFiles(data, cb) {
  1306.     var data2 = new Uint8Array(data.length);
  1307.     var streams = [data, data2];
  1308.     var head = [s2a("OggS\0"), s2a("Krni\0"), s2a("moot\0"), s2a("79\x06\x08\0")];
  1309.     var startedHead = [];
  1310.     for (var k = 0; k < 3; k++) startedHead[head[k][0]] = k;
  1311.     var state = unmaskLCG(data.subarray(0, 6), data2); // unmask 6 bytes ahead
  1312.     for (var i = 0; i < data.length - 6; i++) {
  1313.         for (var m = 0; m < 2; m++) { // search both unmasked and masked data
  1314.             var k = startedHead[streams[m][i]];
  1315.             if (k != undefined && matches(streams[m], i, head[k])) {
  1316.                 // Unmask remainder of file
  1317.                 if (m != 0) {
  1318.                     unmaskLCG(data.subarray(i+6), data2.subarray(i+6), state);
  1319.                 }
  1320.                 // Krni/moot replacement starting at beginning of 4chan sounds archive
  1321.                 if (k != 0) {
  1322.                     var start = readTag(data, i).pos;
  1323.                     if (k == 1) replaceHeader(streams[m], s2a("Krni"), s2a("OggS"), start);
  1324.                     if (k == 2) replaceHeader(streams[m], s2a("moot"), s2a("OggS"), start);
  1325.                     if (k == 3) replaceHeader(streams[m], s2a("79\x06\x08"), s2a("OggS"), start);
  1326.                 }
  1327.                 findFilesClassic(streams[m], cb);
  1328.                 return;
  1329.             }
  1330.         }
  1331.         state = (1664525 * state + 1013904223) & 0xFFFFFFFF;
  1332.         var mask = state >>> 24;
  1333.         data2[i+6] = data[i+6] ^ mask;
  1334.         state += data2[i+6];
  1335.     }
  1336.     decodePNG(data, function(pixelData) {
  1337.         if (!findFilesDnsev(pixelData, cb)) {
  1338.             findFilesCornelia(pixelData, cb);
  1339.         }
  1340.     });
  1341. }
  1342.  
  1343. function decodePNG(data, cb) {
  1344.     var pngMagic = s2a("\x89PNG\r\n\x1A\n");
  1345.     if (!matches(data, 0, pngMagic)) return;
  1346.     var png = new PNG(data);
  1347.     if (png.hasAlphaChannel) {
  1348.         // Canvas doesn't always work for images with alpha channel;
  1349.         // use Devon Govett's Javascript PNG decoder
  1350.         var ctx = document.createElement("canvas").getContext("2d");
  1351.         var pixelData = ctx.createImageData(png.width, png.height);
  1352.         png.copyToImageData(pixelData, png.decodePixels());
  1353.         cb(pixelData);
  1354.     } else {
  1355.         // Use canvas for images without alpha channel (much faster)
  1356.         var img = new Image();
  1357.         img.onload = function() {
  1358.             var can = document.createElement("canvas");
  1359.             can.width = img.width;
  1360.             can.height = img.height;
  1361.             var ctx = can.getContext("2d");
  1362.             ctx.globalCompositeOperation  = "copy";
  1363.             ctx.drawImage(img, 0, 0);
  1364.             URL.revokeObjectURL(blob);
  1365.             var pixelData = ctx.getImageData(0, 0, can.width, can.height);
  1366.             cb(pixelData);
  1367.         }
  1368.         var blob = new Blob([data], {type: "image/png"});
  1369.         img.src = URL.createObjectURL(blob);
  1370.     }
  1371. }
  1372.  
  1373. // Krni/moot replacement
  1374. function replaceHeader(data, oldh, newh, i) {
  1375.     for (; i < data.length - 4; i++) {
  1376.         if (data[i] == oldh[0]) {
  1377.             var match = true;
  1378.             for (var j = 1; j < 4; j++) {
  1379.                 if (data[i+j] != oldh[j]) {
  1380.                     match = false;
  1381.                     break;
  1382.                 }
  1383.             }
  1384.             if (match) {
  1385.                 for (var j = 0; j < 4; j++) {
  1386.                     data[i+j] = newh[j];
  1387.                 }
  1388.             }
  1389.         }
  1390.     }
  1391. }
  1392.  
  1393. // LCG unmasking
  1394. function unmaskLCG(src, dest, state) {
  1395.     if (dest == undefined) dest = src;
  1396.     if (state == undefined) state = 0;
  1397.     for (var i = 0; i < src.length; i++) {
  1398.         state = (1664525 * state + 1013904223) & 0xFFFFFFFF;
  1399.         var mask = state >>> 24;
  1400.         dest[i] = src[i] ^ mask;
  1401.         state += dest[i];
  1402.     }
  1403.     return state;
  1404. }
  1405.  
  1406. // Find files in 4chan Sounds archive
  1407. function readTag(data, pos) {
  1408.     var def = {tag: "", pos: pos};
  1409.     var skip = s2a(' "\r\n');
  1410.     var tagEnd = pos - 1;
  1411.     while (tagEnd >= 0 && skip.indexOf(data[tagEnd]) != -1) tagEnd--;
  1412.     if (tagEnd < 0 || data[tagEnd] != "]".charCodeAt(0)) return def;
  1413.     var tagBegin = tagEnd - 1;
  1414.     while (tagBegin >= 0 && data[tagBegin] != "[".charCodeAt(0)) tagBegin--;
  1415.     if (tagBegin < 0) return def;
  1416.     var tag = "";
  1417.     for (var i = tagBegin + 1; i < tagEnd; i++) {
  1418.         tag += String.fromCharCode(data[i]);
  1419.     }
  1420.     return {tag: tag, pos: tagBegin};
  1421. }
  1422.  
  1423. function endOfPage(data, pos) {
  1424.     if (pos + 26 >= data.length) return data.length;
  1425.     var nSegments = data[pos+26];
  1426.     if (pos + 27 + nSegments > data.length) return data.length;
  1427.     var segLength = 0;
  1428.     for (var i = 0; i < nSegments; i++) {
  1429.         segLength += data[pos+27+i];
  1430.     }
  1431.     if (pos + 27 + nSegments + segLength > data.length) return data.length;
  1432.     return pos + 27 + nSegments + segLength;
  1433. }
  1434.  
  1435. function findFilesClassic(data, cb) {
  1436.     var head = s2a("OggS\0");
  1437.     var pages = [];
  1438.     for (var i = 0; i < data.length - 6; i++) {
  1439.         if (data[i] == head[0]) {
  1440.             var match = true;
  1441.             for (var j = 1; j < 5; j++) {
  1442.                 if (data[i+j] != head[j]) {
  1443.                     match = false;
  1444.                     break;
  1445.                 }
  1446.             }
  1447.             if (match) pages.push({start: i, flags: data[i+5]});
  1448.         }
  1449.     }
  1450.     // Lone "OggS\x00\x02" is probably hack to indicate end of data
  1451.     if (pages.length > 0 && pages[pages.length - 1].flags == 2) {
  1452.         pages.splice(-1);
  1453.     }
  1454.     var files = [];
  1455.     var nTags = 0;
  1456.     var j;
  1457.     for (var i = 0; i < pages.length; i = j) {
  1458.         j = i + 1;
  1459.         while (j < pages.length && (pages[j].flags & 2) == 0) j++;
  1460.         var tag = readTag(data, pages[i].start).tag;
  1461.         if (tag.length > 0) nTags++;
  1462.         var endPos = endOfPage(data, pages[j-1].start);
  1463.         files.push({Name: tag + ".ogg", Data: data.subarray(pages[i].start, endPos)});
  1464.     }
  1465.     if (nTags == 0 && files.length > 0) {
  1466.         var foot = s2a("4SPF");
  1467.         for (var i = pages[pages.length-1].start; i < data.length; i++) {
  1468.             if (data[i] == 0x34 && matches(data, i, foot)) {
  1469.                 var files2 = findFilesFooter(data, i);
  1470.                 if (files2.length > 0) {
  1471.                     cb(new SoundsArchive(files2));
  1472.                     return;
  1473.                 }
  1474.             }
  1475.         }
  1476.     }
  1477.     cb(new SoundsArchive(files));
  1478. }
  1479.  
  1480. function findFilesFooter(data, pos) {
  1481.     if (pos - 2 < 0) return [];
  1482.     var i = pos - 2 - (data[pos-2] + 0x100*data[pos-1]);
  1483.     if (i < 0) return [];
  1484.     var files = [];
  1485.     while (i < pos - 2) {
  1486.         var n = data[i++];
  1487.         if (i + n + 8 > pos - 2) return [];
  1488.         var tag = "";
  1489.         for (var j = 0; j < n; j++) {
  1490.             tag += String.fromCharCode(data[i++]);
  1491.         }
  1492.         var fStart = data[i] + 0x100*data[i+1] + 0x10000*data[i+2] + 0x1000000*data[i+3];
  1493.         var fEnd = data[i+4] + 0x100*data[i+5] + 0x10000*data[i+6] + 0x1000000*data[i+7];
  1494.         files.push({Name: tag + ".ogg", Data: data.subarray(fStart, fEnd)});
  1495.         i += 8;
  1496.     }
  1497.     return files;
  1498. }
  1499.  
  1500. // Find files in Cornelia-style archive (7z in 24-bit BMP converted to PNG)
  1501. function findFilesCornelia(pixelData, cb) {
  1502.     var result7z = extract7z(pixelData);
  1503.     if (result7z == null) return;
  1504.     var files = [];
  1505.     try {
  1506.         parse7z(result7z.data, files);
  1507.     } catch(e) {
  1508.         return;
  1509.     }
  1510.     var imgDefault = cropCornelia(pixelData, result7z.firstRow);
  1511.     cb(new SoundsArchive(files, imgDefault));
  1512. }
  1513.  
  1514. function cropCornelia(pixelData, firstRow) {
  1515.     return function() {
  1516.         var can0 = document.createElement("canvas");
  1517.         can0.width = pixelData.width;
  1518.         can0.height = pixelData.height;
  1519.         can0.getContext("2d").putImageData(pixelData, 0, 0);
  1520.         var can;
  1521.         if (firstRow != 0) {
  1522.             can = document.createElement("canvas");
  1523.             can.width = can0.width;
  1524.             can.height = firstRow;
  1525.             can.getContext("2d").drawImage(can0, 0, 0, can0.width, firstRow, 0, 0, can0.width, firstRow);
  1526.         } else {
  1527.             can = can0;
  1528.         }
  1529.         can.id = "playerImage";
  1530.         return can;
  1531.     };
  1532. }
  1533.  
  1534. function extract7z(pixelData) {
  1535.     var data = new BMPdata(pixelData);
  1536.     var start = data.indexOf(MAGIC_7Z);
  1537.     if (start != -1) {
  1538.         var size = 32 + data.readInt(start + 12, 8) + data.readInt(start + 20, 8);
  1539.         var data7z = data.slice(start, start + size);
  1540.         var firstRow = data.height - 1 - Math.floor((start + size - 1) / data.rowSize);
  1541.         return {data: data7z, firstRow: firstRow};
  1542.     } else {
  1543.         return null;
  1544.     }
  1545. }
  1546.  
  1547. function BMPdata(pixelData) {
  1548.     this.data = pixelData.data;
  1549.     this.width = pixelData.width;
  1550.     this.height = pixelData.height;
  1551.     this.rowSize = Math.ceil(pixelData.width * 3 / 4) * 4;
  1552. }
  1553.  
  1554. BMPdata.prototype.slice = function(istart, iend) {
  1555.     var row = this.height - 1 - Math.floor(istart / this.rowSize);
  1556.     var col = Math.floor((istart % this.rowSize) / 3);
  1557.     var ch = (istart % this.rowSize) % 3;
  1558.     var a = new Uint8Array(iend - istart);
  1559.     var i = 0;
  1560.     while (row >= 0) {
  1561.         while (3*col + ch < this.rowSize) {
  1562.             a[i++] = (col < this.width) ? this.data[4*this.width*row + 4*col + 2 - ch] : 0;
  1563.             if (i >= iend - istart) return a;
  1564.             ch++;
  1565.             if (ch >= 3) {
  1566.                 ch = 0;
  1567.                 col++;
  1568.             }
  1569.         }
  1570.         col = 0;
  1571.         ch = 0;
  1572.         row--;
  1573.     }
  1574.     return a;
  1575. }
  1576.  
  1577. BMPdata.prototype.indexOf = function(s) {
  1578.     var row = this.height - 1;
  1579.     var col = 0;
  1580.     var ch = 0;
  1581.     var x = "";
  1582.     var offset = 0;
  1583.     while (row >= 0) {
  1584.         offset += x.length;
  1585.         x = x.substring(x.length - s.length + 1);
  1586.         offset -= x.length;
  1587.         while (3*col + ch < this.rowSize) {
  1588.             x += String.fromCharCode((col < this.width) ? this.data[4*this.width*row + 4*col + 2 - ch] : 0);
  1589.             ch++;
  1590.             if (ch >= 3) {
  1591.                 ch = 0;
  1592.                 col++;
  1593.             }
  1594.         }
  1595.         var i = x.indexOf(s);
  1596.         if (i != -1) return offset + i;
  1597.         col = 0;
  1598.         ch = 0;
  1599.         row--;
  1600.     }
  1601.     return -1;
  1602. }
  1603.  
  1604. BMPdata.prototype.readByte = function(pos) {
  1605.     var row = this.height - 1 - Math.floor(pos / this.rowSize);
  1606.     if (row < 0) throw new Error("end of file");
  1607.     var col = Math.floor((pos % this.rowSize) / 3);
  1608.     var ch = (pos % this.rowSize) % 3;
  1609.     return (col < this.width) ? this.data[4*this.width*row + 4*col + 2 - ch] : 0;
  1610. }
  1611.  
  1612. BMPdata.prototype.readInt = function(pos, len) {
  1613.     var n = 0;
  1614.     var x = 1;
  1615.     for (var i = 0; i < len; i++) {
  1616.         n += this.readByte(pos + i) * x;
  1617.         x *= 256;
  1618.     }
  1619.     if (n > (1<<30) * (1<<23)) throw new Error("integer overflow");
  1620.     return n;
  1621. }
  1622.  
  1623. // Find files in archive embedded in PNG using least-significant-bits steganography
  1624. // See https://github.com/dnsev/4cs
  1625.  
  1626. // Glue code
  1627. function findFilesDnsev(pixelData, cb) {
  1628.     var result = new DataImageReader(new DataImage(pixelData)).unpack();
  1629.     if (typeof(result) == "string") return false;
  1630.     var files = [];
  1631.     for (var i = 0; i < result[0].length; i++) {
  1632.         files.push({Name: result[0][i], Data: result[1][i]});
  1633.     }
  1634.     cb(new SoundsArchive(files));
  1635.     return true;
  1636. }
  1637.  
  1638. function DataImage(pixelData) {
  1639.     this.pixels = pixelData.data;
  1640.     this.width = pixelData.width;
  1641.     this.height = pixelData.height;
  1642. }
  1643. DataImage.prototype.get_pixel = function(x, y, c) {
  1644.     return this.pixels[(x + y * this.width) * 4 + c];
  1645. }
  1646.  
  1647. // Code from https://github.com/dnsev/4cs
  1648. function DataImageReader (image) {
  1649.     this.image = image;
  1650.     this.bitmask = 0;
  1651.     this.value_mask = 0;
  1652.     this.pixel_mask = 0xFF;
  1653.     this.x = 0;
  1654.     this.y = 0;
  1655.     this.c = 0;
  1656.     this.bit_value = 0;
  1657.     this.bit_count = 0;
  1658.     this.pixel_pos = 0;
  1659.     this.scatter_pos = 0;
  1660.     this.scatter_range = 0;
  1661.     this.scatter_full_range = 0;
  1662.     this.scatter = false;
  1663.     this.channels = 0;
  1664.     this.hashmasking = false;
  1665.     this.hashmask_length = 0;
  1666.     this.hashmask_index = 0;
  1667.     this.hashmask_value = null;
  1668. }
  1669. DataImageReader.prototype.unpack = function () {
  1670.     try {
  1671.         return this.__unpack();
  1672.     }
  1673.     catch (e) {
  1674.         return "Error extracting data; image file likely doesn't contain data";
  1675.     }
  1676. }
  1677. DataImageReader.prototype.__unpack = function () {
  1678.     // Init
  1679.     this.x = 0;
  1680.     this.y = 0;
  1681.     this.c = 0;
  1682.     this.bit_value = 0;
  1683.     this.bit_count = 0;
  1684.     this.pixel_pos = 0;
  1685.     this.scatter_pos = 0;
  1686.     this.scatter_range = 0;
  1687.     this.scatter_full_range = 0;
  1688.     this.scatter = false;
  1689.     this.channels = 3;
  1690.     this.hashmasking = false;
  1691.     this.hashmask_length = 0;
  1692.     this.hashmask_index = 0;
  1693.     this.hashmask_value = null;
  1694.  
  1695.     // Read bitmask
  1696.     this.bitmask = 1 + this.__read_pixel(0x07);
  1697.     this.value_mask = (1 << this.bitmask) - 1;
  1698.     this.pixel_mask = 0xFF - this.value_mask;
  1699.  
  1700.     // Flags
  1701.     var flags = this.__read_pixel(0x07);
  1702.     // Bit depth
  1703.     if ((flags & 4) != 0) this.channels = 4;
  1704.  
  1705.     // Exflags
  1706.     var metadata = false;
  1707.     if ((flags & 1) != 0) {
  1708.         // Flags
  1709.         var flags2 = this.__data_to_int(this.__extract_data(1));
  1710.         // Evaluate
  1711.         if ((flags2 & 2) != 0) metadata = true;
  1712.         if ((flags2 & 4) != 0) {
  1713.             this.__complete_pixel();
  1714.             this.__init_hashmask();
  1715.         }
  1716.     }
  1717.  
  1718.     // Scatter
  1719.     if ((flags & 2) != 0) {
  1720.         // Read
  1721.         this.scatter_range = this.__data_to_int(this.__extract_data(4));
  1722.         this.__complete_pixel();
  1723.  
  1724.         // Enable scatter
  1725.         if (this.scatter_range > 0) {
  1726.             this.scatter_pos = 0;
  1727.             this.scatter_full_range = ((this.image.width * this.image.height * this.channels) - this.pixel_pos - 1);
  1728.             this.scatter = true;
  1729.         }
  1730.     }
  1731.  
  1732.     // Metadata
  1733.     if (metadata) {
  1734.         var meta_length = this.__data_to_int(this.__extract_data(2));
  1735.         var meta = this.__extract_data(meta_length);
  1736.     }
  1737.  
  1738.     // File count
  1739.     var file_count = this.__data_to_int(this.__extract_data(2));
  1740.  
  1741.     // Filename lengths and file lengths
  1742.     var filename_lengths = new Array();
  1743.     var file_sizes = new Array();
  1744.     var v;
  1745.     var total_size = 0;
  1746.     var size_limit;
  1747.     for (var i = 0; i < file_count; ++i) {
  1748.         // Filename length
  1749.         v = this.__data_to_int(this.__extract_data(2));
  1750.         filename_lengths.push(v);
  1751.         total_size += v;
  1752.         // File length
  1753.         v = this.__data_to_int(this.__extract_data(4));
  1754.         file_sizes.push(v);
  1755.         total_size += v;
  1756.  
  1757.         // Error checking
  1758.         size_limit = Math.ceil(((((this.image.width * (this.image.height - this.y) - this.x) * this.channels) - this.c) * this.bitmask) / 8);
  1759.         if (total_size > size_limit) {
  1760.             throw "Data overflow";
  1761.         }
  1762.     }
  1763.  
  1764.     // Filenames
  1765.     var filenames = new Array();
  1766.     for (var i = 0; i < file_count; ++i) {
  1767.         // Filename
  1768.         var fn = this.__data_to_string(this.__extract_data(filename_lengths[i]));
  1769.         try {
  1770.             fn = decodeURIComponent(escape(fn)); // UTF-8 decode
  1771.         } catch(e) {}
  1772.         // Add to list
  1773.         filenames.push(fn);
  1774.     }
  1775.  
  1776.     // Sources
  1777.     var sources = new Array();
  1778.     for (var i = 0; i < file_count; ++i) {
  1779.         // Read source
  1780.         var src = this.__extract_data(file_sizes[i]);
  1781.         sources.push(src);
  1782.     }
  1783.  
  1784.     // Done
  1785.     this.hashmasking = false;
  1786.     this.hashmask_value = null;
  1787.     return [ filenames , sources ];
  1788. }
  1789. DataImageReader.prototype.next_pixel_component = function (count) {
  1790.     while (count > 0) {
  1791.         count -= 1;
  1792.  
  1793.         this.c = (this.c + 1) % this.channels;
  1794.         if (this.c == 0) {
  1795.             this.x = (this.x + 1) % this.image.width;
  1796.             if (this.x == 0) {
  1797.                 this.y = (this.y + 1) % this.image.height;
  1798.                 if (this.y == 0) {
  1799.                     throw "Pixel overflow";
  1800.                 }
  1801.             }
  1802.         }
  1803.     }
  1804. }
  1805. DataImageReader.prototype.__extract_data = function (byte_length) {
  1806.     var src = new Uint8Array(byte_length);
  1807.     var j = 0;
  1808.     for (var i = this.bit_count; i < byte_length * 8; i += this.bitmask) {
  1809.         this.bit_value = this.bit_value | (this.__read_pixel(this.value_mask) << this.bit_count);
  1810.         this.bit_count += this.bitmask;
  1811.         while (this.bit_count >= 8) {
  1812.             src[j] = (this.bit_value & 0xFF);
  1813.             j += 1;
  1814.             this.bit_value = this.bit_value >> 8;
  1815.             this.bit_count -= 8;
  1816.         }
  1817.     }
  1818.     if (j != byte_length) {
  1819.         throw "Length mismatch";
  1820.     }
  1821.     return src;
  1822. }
  1823. DataImageReader.prototype.__data_to_int = function (data) {
  1824.     var val = 0;
  1825.     for (var i = 0; i < data.length; ++i) {
  1826.         val = val * 256 + data[i];
  1827.     }
  1828.     return val;
  1829. }
  1830. DataImageReader.prototype.__data_to_string = function (data) {
  1831.     var val = "";
  1832.     for (var i = 0; i < data.length; ++i) {
  1833.         val += String.fromCharCode(data[i]);
  1834.     }
  1835.     return val;
  1836. }
  1837. DataImageReader.prototype.__read_pixel = function (value_mask) {
  1838.     var value = (this.image.get_pixel(this.x, this.y, this.c) & value_mask);
  1839.     if (this.hashmasking) {
  1840.         value = this.__decode_hashmask(value, this.bitmask);
  1841.     }
  1842.  
  1843.     if (this.scatter) {
  1844.         this.scatter_pos += 1;
  1845.         // integer division sure is fun
  1846.         var v = ((Math.floor(this.scatter_pos * this.scatter_full_range / this.scatter_range) - Math.floor((this.scatter_pos - 1) * this.scatter_full_range / this.scatter_range)));
  1847.         this.pixel_pos += v;
  1848.         this.next_pixel_component(v);
  1849.     }
  1850.     else {
  1851.         this.pixel_pos += 1;
  1852.         this.next_pixel_component(1);
  1853.     }
  1854.  
  1855.     return value;
  1856. }
  1857. DataImageReader.prototype.__complete_pixel = function () {
  1858.     if (this.bit_count > 0) {
  1859.         this.bit_count = 0;
  1860.         this.bit_value = 0;
  1861.     }
  1862. }
  1863. DataImageReader.prototype.__init_hashmask = function () {
  1864.     this.hashmasking = true;
  1865.     this.hashmask_length = 32 * 8;
  1866.     this.hashmask_index = 0;
  1867.     this.hashmask_value = new Uint8Array(this.hashmask_length / 8);
  1868.     for (var i = 0; i < this.hashmask_length / 8; ++i) {
  1869.         this.hashmask_value[i] = (1 << ((i % 8) + 1)) - 1;
  1870.     }
  1871.     this.__calculate_hashmask();
  1872.     this.hashmask_index = 0;
  1873. }
  1874. DataImageReader.prototype.__calculate_hashmask = function () {
  1875.     // Vars
  1876.     var x = 0;
  1877.     var y = 0;
  1878.     var c = 0;
  1879.     var w = this.image.width;
  1880.     var h = this.image.height;
  1881.     var cc = this.channels;
  1882.  
  1883.     // First 2 flag pixels
  1884.     this.__update_hashmask(this.image.get_pixel(x, y, c) >> 3, 5);
  1885.     if ((c = (c + 1) % cc) == 0 && (x = (x + 1) % w) == 0 && (y = (y + 1) % h) == 0) return;
  1886.     this.__update_hashmask(this.image.get_pixel(x, y, c) >> 3, 5);
  1887.     if ((c = (c + 1) % cc) == 0 && (x = (x + 1) % w) == 0 && (y = (y + 1) % h) == 0) return;
  1888.  
  1889.     // All other pixels
  1890.     if (this.bitmask != 8) {
  1891.         while (true) {
  1892.             // Update
  1893.             this.__update_hashmask(this.image.get_pixel(x, y, c) >> this.bitmask, 8 - this.bitmask);
  1894.             // Next
  1895.             if ((c = (c + 1) % cc) == 0 && (x = (x + 1) % w) == 0 && (y = (y + 1) % h) == 0) return;
  1896.         }
  1897.     }
  1898. }
  1899. DataImageReader.prototype.__update_hashmask = function (value, bits) {
  1900.     // First 2 flag pixels
  1901.     var b;
  1902.     while (true) {
  1903.         // Number of bits that can be used on this index
  1904.         b = 8 - (this.hashmask_index % 8);
  1905.         if (bits <= b) {
  1906.             // Apply
  1907.             this.hashmask_value[Math.floor(this.hashmask_index / 8)] ^= (value) << (this.hashmask_index % 8);
  1908.             // Done
  1909.             this.hashmask_index = (this.hashmask_index + bits) % (this.hashmask_length);
  1910.             return;
  1911.         }
  1912.         else {
  1913.             // Partial apply
  1914.             this.hashmask_value[Math.floor(this.hashmask_index / 8)] ^= (value & ((1 << b) - 1)) << (this.hashmask_index % 8);
  1915.             // Done
  1916.             this.hashmask_index = (this.hashmask_index + b) % (this.hashmask_length);
  1917.             bits -= b;
  1918.             value >>= b;
  1919.         }
  1920.     }
  1921. }
  1922. DataImageReader.prototype.__decode_hashmask = function (value, bits) {
  1923.     var b;
  1924.     var off = 0;
  1925.     while (true) {
  1926.         b = 8 - (this.hashmask_index % 8);
  1927.         if (bits <= b) {
  1928.             // Apply
  1929.             value ^= (this.hashmask_value[Math.floor(this.hashmask_index / 8)] & ((1 << bits) - 1)) << off;
  1930.             // Done
  1931.             this.hashmask_index = (this.hashmask_index + bits) % (this.hashmask_length);
  1932.             return value;
  1933.         }
  1934.         else {
  1935.             // Partial apply
  1936.             value ^= (this.hashmask_value[Math.floor(this.hashmask_index / 8)] & ((1 << b) - 1)) << off;
  1937.             // Done
  1938.             this.hashmask_index = (this.hashmask_index + b) % (this.hashmask_length);
  1939.             bits -= b;
  1940.             off += b;
  1941.         }
  1942.     }
  1943. }
  1944. // End code from https://github.com/dnsev/4cs
  1945.  
  1946. // 7z extraction code
  1947. var MAGIC_7Z = "7z\xBC\xAF\x27\x1C";
  1948.  
  1949. function parse7z(data, files, headers) {
  1950.     if (files == undefined) files = [];
  1951.     if (headers == undefined) headers = {};
  1952.  
  1953.     var kNames =
  1954.         "End Header ArchiveProperties AdditionalStreamsInfo MainStreamsInfo FilesInfo PackInfo UnPackInfo "
  1955.         + "SubStreamsInfo Size CRC Folder CodersUnPackSize NumUnPackStream EmptyStream EmptyFile "
  1956.         + "Anti Names CTime ATime MTime Attributes Comment EncodedHeader StartPos Dummy";
  1957.     kNames = kNames.split(" ");
  1958.     var k = {};
  1959.     for (var i = 0; i < kNames.length; i++) k[kNames[i]] = i;
  1960.  
  1961.     var pos = 0;
  1962.     var nBits = 0;
  1963.     var bits = 0;
  1964.  
  1965.     function ParseError(message) {
  1966.         this.name = "ParseError";
  1967.         this.message = message;
  1968.         this.pos = pos;
  1969.     }
  1970.     ParseError.prototype = new Error();
  1971.     ParseError.prototype.constructor = ParseError;
  1972.  
  1973.     function readN(f, n) {
  1974.         var x = [];
  1975.         for (var i = 0; i < n; i++) x.push(f());
  1976.         return x;
  1977.     }
  1978.  
  1979.     function nextByte() {
  1980.         if (pos >= data.length) throw new ParseError("passed end of file");
  1981.         return data[pos++];
  1982.     }
  1983.  
  1984.     function nextBit() {
  1985.         if (nBits == 0) {
  1986.             bits = nextByte();
  1987.             nBits = 8;
  1988.         }
  1989.         nBits -= 1;
  1990.         var b = (bits >> nBits) & 1;
  1991.         return b;
  1992.     }
  1993.  
  1994.     function nextInt(len) {
  1995.         var n = 0;
  1996.         var x = 1;
  1997.         for (var i = 0; i < len; i++) {
  1998.             n += nextByte() * x;
  1999.             x *= 256;
  2000.         }
  2001.         if (n > (1<<30) * (1<<23)) throw new ParseError("integer overflow");
  2002.         return n;
  2003.     }
  2004.  
  2005.     function nextInt7z() {
  2006.         var b0 = nextByte();
  2007.         var thresh = 128;
  2008.         var n = 0;
  2009.         var x = 1;
  2010.         while (b0 >= thresh && thresh > 0) {
  2011.             n += nextByte() * x;
  2012.             x *= 256;
  2013.             b0 -= thresh;
  2014.             thresh >>= 1;
  2015.         }
  2016.         n += b0 * x;
  2017.         if (n > (1<<30) * (1<<23)) throw new ParseError("integer overflow");
  2018.         return n;
  2019.     }
  2020.  
  2021.     function nextBinString(len) {
  2022.         var s = "";
  2023.         for (var i = 0; i < len; i++) {
  2024.             s += String.fromCharCode(nextByte());
  2025.         }
  2026.         return s;
  2027.     }
  2028.  
  2029.     function ArchiveProperties() {
  2030.         var x = {};
  2031.         while (true) {
  2032.             var ID = nextByte();
  2033.             if (ID == 0) return x;
  2034.             var n = nextInt7z();
  2035.             x["Property"+ID] = readN(nextByte, n);
  2036.         }
  2037.     }
  2038.  
  2039.     function Digests(NumStreams) {
  2040.         var x = [];
  2041.         var AllAreDefined = nextByte();
  2042.         if (!AllAreDefined) var Defined = readN(nextBit, NumStreams);
  2043.         for (var i = 0; i < NumStreams; i++) {
  2044.             if (AllAreDefined || Defined[i]) x[i] = nextInt(4);
  2045.         }
  2046.         return x;
  2047.     }
  2048.  
  2049.     function PackInfo() {
  2050.         var x = [];
  2051.         var Pos = 32 + nextInt7z();
  2052.         var NumStreams = nextInt7z();
  2053.         for (var i = 0; i < NumStreams; i++) x.push({});
  2054.         if (NumStreams >= 1) x[0].Start = Pos;
  2055.  
  2056.         var ID = nextByte();
  2057.         if (ID == k.Size) {
  2058.             for (var i = 0; i < NumStreams; i++) {
  2059.                 x[i].Start = Pos;
  2060.                 x[i].Size = nextInt7z();
  2061.                 Pos += x[i].Size;
  2062.             }
  2063.             ID = nextByte();
  2064.         }
  2065.         if (ID == k.CRC) {
  2066.             for (var i = 0; i < NumStreams; i++) x[i].Digest = nextInt7z();
  2067.             ID = nextByte();
  2068.         }
  2069.         if (ID != 0) throw new ParseError("unexpected block id " + ID);
  2070.  
  2071.         return x;
  2072.     }
  2073.  
  2074.     function Folder() {
  2075.         var x = {}
  2076.  
  2077.         var NumCoders = nextInt7z();
  2078.         x.Coders = [];
  2079.         x.NumInStreams = 0;
  2080.         x.NumOutStreams = 0;
  2081.         for (var i = 0; i < NumCoders; i++) {
  2082.             var y = {};
  2083.             var b = nextByte();
  2084.             var CodecIdSize = b & 0xF;
  2085.             var IsComplexCoder = b & 0x10;
  2086.             var ThereAreAttributes = b & 0x20;
  2087.             var AlternativeMethods = b & 0x80;
  2088.             if (AlternativeMethods) throw new ParseError("not supported");
  2089.             y.CodecId = nextBinString(CodecIdSize);
  2090.             if (IsComplexCoder) {
  2091.                 y.NumInStreams = nextInt7z();
  2092.                 y.NumOutStreams = nextInt7z();
  2093.             } else {
  2094.                 y.NumInStreams = 1;
  2095.                 y.NumOutStreams = 1;
  2096.             }
  2097.             x.NumInStreams += y.NumInStreams;
  2098.             x.NumOutStreams += y.NumOutStreams;
  2099.             if (ThereAreAttributes) {
  2100.                 var PropertiesSize = nextInt7z();
  2101.                 y.Properties = readN(nextByte, PropertiesSize);
  2102.             }
  2103.             x.Coders.push(y);
  2104.         }
  2105.  
  2106.         var NumBindPairs = x.NumOutStreams - 1;
  2107.         x.BindInStream = []
  2108.         x.BindOutStream = []
  2109.         for (var i = 0; i < NumBindPairs; i++) {
  2110.             var InIndex = nextInt7z();
  2111.             var OutIndex = nextInt7z();
  2112.             x.BindInStream[OutIndex] = InIndex;
  2113.             x.BindOutStream[InIndex] = OutIndex;
  2114.         }
  2115.        
  2116.         var NumPackStreams = x.NumInStreams - NumBindPairs;
  2117.         if (NumPackStreams == 1) {
  2118.             for (var i = 0; i < x.NumInStreams; i++) {
  2119.                 if (x.BindOutStream[i] == undefined) x.PackStreams = [i];
  2120.             }
  2121.         } else {
  2122.             x.PackStreams = readN(nextInt7z, NumPackStreams);
  2123.         }
  2124.  
  2125.         return x;
  2126.     }
  2127.  
  2128.     function CodersInfo(Folders) {
  2129.         var ID = nextByte();
  2130.  
  2131.         if (ID != k.Folder) throw new ParseError("unexpected block id " + ID);
  2132.         var NumFolders = nextInt7z();
  2133.         var External = nextByte();
  2134.         if (External) throw new ParseError("not supported");
  2135.         for (var i = 0; i < NumFolders; i++) {
  2136.             Folders.push(Folder());
  2137.         }
  2138.         ID = nextByte();
  2139.  
  2140.         if (ID != k.CodersUnPackSize) throw new ParseError("unexpected block id " + ID);
  2141.         for (var i = 0; i < NumFolders; i++) {
  2142.             Folders[i].OutSizes = readN(nextInt7z, Folders[i].NumOutStreams);
  2143.             for (var j = 0; j < Folders[i].NumOutStreams; j++) {
  2144.                 if (Folders[i].BindInStream[j] == undefined) {
  2145.                     Folders[i].UnPackSize = Folders[i].OutSizes[j];
  2146.                 }
  2147.             }
  2148.         }
  2149.         ID = nextByte();
  2150.  
  2151.         if (ID == k.CRC) {
  2152.             var UnPackDigests = Digests(NumFolders);
  2153.             for (var i = 0; i < NumFolders; i++) {
  2154.                 if (i in UnPackDigests) Folders[i].UnPackDigest = UnPackDigests[i];
  2155.             }
  2156.             ID = nextByte();
  2157.         }
  2158.  
  2159.         if (ID != 0) throw new ParseError("unexpected block id " + ID);
  2160.     }
  2161.  
  2162.     function SubStreamsInfo(Folders, SubStreams, NoBlock) {
  2163.         var ID = NoBlock ? 0 : nextByte();
  2164.  
  2165.         if (ID == k.NumUnPackStream) {
  2166.             for (var i = 0; i < Folders.length; i++) Folders[i].NumSubStreams = nextInt7z();
  2167.             ID = nextByte();
  2168.         } else {
  2169.             for (var i = 0; i < Folders.length; i++) Folders[i].NumSubStreams = 1;
  2170.         }
  2171.         for (var i = 0; i < Folders.length; i++) {
  2172.             for (var j = 0; j < Folders[i].NumSubStreams; j++) SubStreams.push({});
  2173.         }
  2174.  
  2175.         if (ID == k.Size) {
  2176.             var iSubStream = 0;
  2177.             for (var i = 0; i < Folders.length; i++) {
  2178.                 if (Folders[i].NumSubStreams != 0) {
  2179.                     var SumSizes = 0;
  2180.                     for (var j = 0; j < Folders[i].NumSubStreams - 1; j++) {
  2181.                         var n = nextInt7z();
  2182.                         SubStreams[iSubStream++].Size = n;
  2183.                         SumSizes += n;
  2184.                     }
  2185.                     SubStreams[iSubStream++].Size = Folders[i].UnPackSize - SumSizes;
  2186.                 }
  2187.             }
  2188.             ID = nextByte();
  2189.         } else {
  2190.             var iSubStream = 0;
  2191.             for (var i = 0; i < Folders.length; i++) {
  2192.                 if (Folders[i].NumSubStreams == 1) {
  2193.                     SubStreams[iSubStream].Size = Folders[i].UnPackSize;
  2194.                 }
  2195.                 iSubStream += Folders[i].NumSubStreams;
  2196.             }
  2197.         }
  2198.  
  2199.         if (ID == k.CRC) {
  2200.             var NumUnknownDigests = 0;
  2201.             for (var i = 0; i < Folders.length; i++) {
  2202.                 if (!(Folders[i].NumSubStreams == 1 && Folders[i].UnPackDigest != undefined)) {
  2203.                     NumUnknownDigests += Folders[i].NumSubStreams;
  2204.                 }
  2205.             }
  2206.             var UnknownDigests = Digests(NumUnknownDigests);
  2207.             var iDigest = 0;
  2208.             iSubStream = 0;
  2209.             for (var i = 0; i < Folders.length; i++) {
  2210.                 if (Folders[i].NumSubStreams == 1 && Folders[i].UnPackDigest != undefined) {
  2211.                     SubStreams[iSubStream++].Digest = Folders[i].UnPackDigest;
  2212.                 } else {
  2213.                     for (var j = 0; j < Folders[i].NumSubStreams; j++) {
  2214.                         if (iDigest in UnknownDigests) {
  2215.                             SubStreams[iSubStream++].Digest = UnknownDigests[iDigest++];
  2216.                         }
  2217.                     }
  2218.                 }
  2219.             }
  2220.             ID = nextByte();
  2221.         }
  2222.  
  2223.         if (ID != 0) throw new ParseError("unexpected block id " + ID);
  2224.     }
  2225.  
  2226.     function StreamsInfo(x, NoBlock) {
  2227.         var ID = NoBlock ? 0 : nextByte();
  2228.  
  2229.         if (ID == k.PackInfo) {
  2230.             x.PackStreams = PackInfo();
  2231.             ID = nextByte();
  2232.         } else {
  2233.             x.PackStreams = [];
  2234.         }
  2235.  
  2236.         x.Folders = [];
  2237.         if (ID == k.UnPackInfo) {
  2238.             CodersInfo(x.Folders);
  2239.             ID = nextByte();
  2240.         }
  2241.  
  2242.         x.SubStreams = [];
  2243.         if (ID == k.SubStreamsInfo) {
  2244.             SubStreamsInfo(x.Folders, x.SubStreams);
  2245.             ID = nextByte();
  2246.         } else {
  2247.             SubStreamsInfo(x.Folders, x.SubStreams, true);
  2248.         }
  2249.  
  2250.         if (ID != 0) throw new ParseError("unexpected block id " + ID);
  2251.     }
  2252.  
  2253.     function FilesInfo() {
  2254.         var x = {};
  2255.         x.NumFiles = nextInt7z();
  2256.         x.Names = [];
  2257.         x.EmptyStream = [];
  2258.         while (true) {
  2259.             var ID = nextByte();
  2260.             if (ID == 0) return x;
  2261.             var PropertySize = nextInt7z();
  2262.             if (ID == k.Names && PropertySize > 1) {
  2263.                 var External = nextByte();