Advertisement
Guest User

Untitled

a guest
Nov 23rd, 2014
72
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // album cover panel by Jensen
  2.  
  3. // Flags, used by Menu -----------------
  4. var MF_STRING = 0x00000000;
  5. var MF_DISABLED = 0x00000002;
  6. var MF_CHECKED = 0x00000008;
  7. var MF_POPUP = 0x00000010;
  8.  
  9. var window_id = window.ID;
  10.  
  11. var AlbumArtId = {
  12.     front: 0,
  13.     back: 1,
  14.     disc: 2,
  15.     icon: 3,
  16.     artist: 4,
  17.    
  18.     GetName: function(value){
  19.         for(var i in this){
  20.             if(this[i]==value)
  21.                 return i;
  22.         }
  23.         return null;
  24.     }
  25. };
  26.  
  27. // Some functions =======================================
  28. function CalcNewImgSize (img, dstW, dstH, srcW,srcH, strch, kar, fill) {        // Calculate image's new size and offsets in new width and height range.
  29.     if (!img) return;
  30.     if (!srcW) srcW = img.width;
  31.     if (!srcH) srcH = img.height;
  32.     if (strch==undefined) strch = Properties.Image.Stretch;
  33.     if (kar==undefined) kar = Properties.Image.KeepAspectRatio;
  34.     if (fill==undefined) fill = Properties.Image.Fill;
  35.    
  36.     var size;
  37.     if(fill){
  38.         size = {x:0, y:0, w:dstW, h:dstH};
  39.         if(srcH/srcW < dstH/dstW)
  40.             size.w = Math.ceil(srcW*dstH/srcH);
  41.         else
  42.             size.h = Math.ceil(srcH*dstW/srcW);
  43.     } else if (strch) {
  44.         size = {x:0, y:0, w:dstW, h:dstH};
  45.         if (kar) {
  46.             size.w = Math.ceil(srcW*dstH/srcH);
  47.             if (size.w>dstW) {
  48.                 size.w = dstW;
  49.                 size.h = Math.ceil(srcH*dstW/srcW);
  50.             }
  51.         }
  52.     } else {
  53.         size = {x:0, y:0, w:srcW, h:srcH};
  54.         if (kar) {
  55.             if (srcH>dstH) {
  56.                 size.h = dstH;
  57.                 size.w = Math.ceil(srcW*dstH/srcH);
  58.             }
  59.             if (size.w>dstW) {
  60.                 size.w = dstW;
  61.                 size.h = Math.ceil(srcH*dstW/srcW);
  62.             }
  63.         } else {
  64.             size.w = Math.min(srcW, dstW);
  65.             size.h = Math.min(srcH, dstH);
  66.         }
  67.     }
  68.     size.x = Math.floor((dstW-size.w)/2);
  69.     size.y = Math.floor((dstH-size.h)/2);
  70.     return size;
  71. }
  72.  
  73. //-------------------------------------
  74. function ResizeImage(img, w, h){
  75.     if(!img || !w || !h) return null;
  76.     var newImg = gdi.CreateImage(w, h);
  77.     var g = newImg.GetGraphics();
  78.     g.SetInterpolationMode(Properties.Image.InterpolationMode);
  79.     g.DrawImage(img, 0, 0, w, h, 0, 0, img.width, img.height);
  80.     newImg.ReleaseGraphics(g);
  81.     return newImg;
  82. }
  83.  
  84. //-------------------------------------
  85. function CreateRawBitmap2(img){
  86.     var bmp = img.CreateRawBitmap();
  87.     img.Dispose();
  88.     return bmp;
  89. }
  90.  
  91. //-------------------------------------
  92. function RGBA(r,g,b,a){ return ((a<<24)|(r<<16)|(g<<8)|(b)); }
  93.  
  94. //-------------------------------------
  95. String.prototype.capitalize = function(){
  96.     return this.charAt(0).toUpperCase() + this.slice(1);
  97. }
  98.  
  99.  
  100. // Misc Classes =========================================
  101. function MatchLogger(){        // Debug class. The log info is not always correct.
  102.     this.pathFormatGroup;
  103.     this.pathStringGroup;
  104.     this.pathArray;
  105.     this.pathGroups = [];
  106.     this.consuming;
  107.     this.profiler = fb.CreateProfiler();
  108.    
  109.     this.Reset = function(){
  110.         this.profiler.Reset();
  111.         this.pathGroups = [];
  112.         this.pathArray = null;
  113.     }
  114.        
  115.     this.Print = function(toPopup){
  116.         var count = 0;
  117.         var string = [];
  118.        
  119.         string.push(toPopup ? "" : "\n");
  120.         string.push("WSH Cover match log -------------------------------\n");
  121.         string.push("The info below are debug infomation, they are not absolutely correct.\n");
  122.        
  123.         for (var i=0; i<this.pathGroups.length; i++){
  124.             string.push("\n" + (i==0 ? "Build-in Sources" : "Additional Sources") + " -----------------------\n");
  125.             string.push("PathFormat:  " + this.pathFormatGroup[i] + "\n");
  126.             i && string.push("PathString:  " + this.pathStringGroup[i] + "\n");
  127.             string.push("\n");
  128.            
  129.             var pg = this.pathGroups[i];
  130.             for(var j=0; j<pg.length; j++){
  131.                 var pi = pg[j];
  132.                 if(pi.artId==-1)
  133.                     string.push("Path: " + pi.path + "\n");
  134.                 else
  135.                     string.push("Path: <" + AlbumArtId.GetName(pi.artId) + ">\n");
  136.                 string.push("\tMatched: " + pi.results.length + " files");
  137.                 string.push("\tScan used: " + (pi.results.scanConsuming==-1?"n/a":pi.results.scanConsuming) + " ms" + (pi.results.pathCacheHit ? "\t(Cache hit)" : "") + "\n");
  138.                 for(var k=0; k<pi.results.length; k++){
  139.                     var pi2 = pi.results[k];
  140.                     string.push("\t");
  141.                     if (pi2.embed)
  142.                         string.push("<Embed Image: " + AlbumArtId.GetName(pi2.artId) + ">");
  143.                     else
  144.                         string.push(pi2.path);
  145.                     if(pi2.duplicate)
  146.                         string.push("\t(Duplicate)");
  147.                     else
  148.                         count++;
  149.                     string.push((pi2.loadConsuming==-1?"":("\tLoad used: "+pi2.loadConsuming+" ms")) + (pi2.cacheHit ? "\t(Cache hit)" : "") + "\n");
  150.                 }
  151.             }
  152.         }
  153.         string.push("\nTotal ---------------");
  154.         string.push("\nMatched: " + count + " files");
  155.         string.push("\tScan used: " + this.consuming + " ms");
  156.        
  157.         if(toPopup)
  158.             PopMessage(0, string.join(""), 0);
  159.         else
  160.             fb.trace(string.join(""));
  161.     }
  162. }
  163. var Logger = new MatchLogger();        // Debug object.
  164.  
  165.  
  166. //============================================
  167. function PathItem(path, artid, metadb, index){
  168.     this.path = path ? path : "";
  169.     this.artId = artid;
  170.     this.embed = false;
  171.     this.metadb = metadb;
  172.     this.index = index;
  173.     this.results = [];
  174.     // These below are for match logs.
  175.     this.time;
  176.     this.loadConsuming = -1;
  177.     this.cacheHit = false;
  178.     this.results.scanConsuming = -1;
  179.     this.results.pathCacheHit = false;
  180. }
  181.  
  182. PathItem.prototype.SimpleClone = function(){
  183.     var newpi = new PathItem(this.path, this.artId, this.metadb);
  184.     newpi.embed = this.embed;
  185.     return newpi;
  186. }
  187.  
  188.  
  189. //----------------------------------------------------------
  190. function ImageItem(img, srcw, srch){
  191.     this.img = img;
  192.     this.srcW = srcw ? srcw : (img ? img.width : 0);
  193.     this.srcH = srch ? srch : (img ? img.height : 0);
  194. }
  195.  
  196. //----------------------------------------------------------
  197. function QueueItem(cookie, pathitem, whendone){
  198.     this.cookie = cookie;
  199.     this.pathItem = pathitem;
  200.     this.whenDone = whendone;
  201. }
  202.  
  203. //----------------------------------------------------------
  204. function ImageLoader(maxCacheLength, w, h) {
  205.     var Caches = [];
  206.     var imgLoadingQueue = [];
  207.    
  208.     function ImageCacheItem(img, pathitem, srcw, srch){
  209.         this.img = img;
  210.         this.embed = pathitem.embed;
  211.         this.path = pathitem.path;
  212.         this.artId = pathitem.artId;
  213.         this.srcW = srcw;
  214.         this.srcH = srch;
  215.     }
  216.    
  217.     ImageCacheItem.prototype.Compare = function(newpathitem){
  218.         if(this.embed!=newpathitem.embed)
  219.             return false;
  220.         else if(this.embed && newpathitem.embed)
  221.             return this.path==newpathitem.path && this.artId==newpathitem.artId;
  222.         else
  223.             return this.path==newpathitem.path;
  224.     }
  225.    
  226.     ImageCacheItem.prototype.ReadImage = function(dstw, dsth){
  227.         var img;
  228.         if (!dstw || !dsth)
  229.             img = this.img;
  230.         else {
  231.             var size = CalcNewImgSize({width:this.srcW, height:this.srcH}, dstw, dsth);
  232.             if (size.w==this.img.width && size.h==this.img.height)      // Nothing need to be changed.
  233.                 img = this.img;
  234.             else if (Math.abs(size.w-this.img.width)<Math.max(this.img.width*0.1, 5) && Math.abs(size.h-this.img.height)<Math.max(this.img.height*0.1, 5)){       // Not too much, just take a zoom.
  235.                 //img = this.img.Resize(size.w, size.h);
  236.                 img = ResizeImage(this.img, size.w, size.h);
  237.             } else if (size.w>this.img.width || size.h>this.img.height)       // Too large to zoom, back to reload.
  238.                 return null;
  239.             else {     // Too small, update caches too.
  240.                 img = ResizeImage(this.img, size.w, size.h);
  241.                 this.img.Dispose();
  242.                 this.img = img;
  243.             }
  244.         }
  245.        
  246.         return new ImageItem(img, this.srcW, this.srcH);
  247.     }
  248.    
  249.     function StoreCache(img, pathitem, srcw, srch, check){
  250.         if(check){
  251.             for (var i=0; i<Caches.length; i++){
  252.                 if(Caches[i].Compare(pathitem)){
  253.                     Caches[i].img = img;
  254.                     return;
  255.                 }
  256.             }
  257.         }
  258.        
  259.         Caches.unshift(new ImageCacheItem(img, pathitem, srcw, srch));
  260.        
  261.         if (Caches.length>maxCacheLength)
  262.             Caches.splice(maxCacheLength, 1);
  263.     }
  264.    
  265.     function SearchCache(pathitem, dstw, dsth){
  266.         var ci, imgitem;
  267.         for (var i=0; i<Caches.length; i++){
  268.             ci = Caches[i];
  269.             if(ci.Compare(pathitem)) {
  270.                 Caches.splice(i,1);
  271.                 imgitem = ci.ReadImage(dstw, dsth);
  272.                 if(imgitem) Caches.unshift(ci);
  273.                 return imgitem;
  274.             }
  275.         }
  276.         return null;
  277.     }
  278.    
  279.     function ResizeAndStoreImage(img, pathitem){
  280.         if(!img) return null;
  281.         var srcW = img.width, srcH = img.height;
  282.         var size = CalcNewImgSize(img, w, h);
  283.         if(srcW!=size.w || srcH!=size.h){
  284.             var img2 = ResizeImage(img, size.w, size.h);
  285.             img.Dispose();
  286.             img = img2;
  287.         }
  288.         if(img.width>w || img.height>h){
  289.             var img2 = img.Clone(-size.x, -size.y, w, h);
  290.             img.Dispose();
  291.             img = img2;
  292.         }
  293.         StoreCache(img, pathitem, srcW, srcH);
  294.         return new ImageItem(img, srcW, srcH);
  295.     }
  296.    
  297.     this.Load = function(pathitem, whendone, ignoreCache, sync){
  298.         pathitem.time = Logger.profiler.Time;
  299.         var imgitem = (!maxCacheLength || ignoreCache) ? null : SearchCache(pathitem, w, h);
  300.         if (imgitem) {
  301.             pathitem.loadConsuming = Logger.profiler.Time - pathitem.time;
  302.             pathitem.cacheHit = true;
  303.             whendone && whendone(imgitem);
  304.             return;
  305.         } else
  306.             pathitem.cacheHit = false;
  307.        
  308.         if(sync){
  309.             var img;
  310.             if (pathitem.artId==-1)
  311.                 img = gdi.Image(pathitem.path);
  312.             else if (pathitem.embed)
  313.                 img = utils.GetAlbumArtEmbedded(pathitem.metadb.RawPath, pathitem.artId);
  314.             else
  315.                 img = utils.GetAlbumArtV2(pathitem.metadb, pathitem.artId, false);
  316.             imgitem = new ImageItem(img);
  317.             whendone && whendone(imgitem);
  318.         } else {
  319.             var cookie = null;
  320.             if (pathitem.artId==-1)
  321.                 cookie = gdi.LoadImageAsync(window_id, pathitem.path);
  322.             else
  323.                 utils.GetAlbumArtAsync(window_id, pathitem.metadb, pathitem.artId, false, pathitem.embed);
  324.             imgLoadingQueue.push(new QueueItem(cookie, pathitem, whendone));
  325.         }
  326.     }
  327.    
  328.     this.OnGetAlbumArtDone = function(metadb, art_id, image, image_path){
  329.         if(!imgLoadingQueue.length){
  330.             image && image.Dispose();
  331.             return;
  332.         }
  333.        
  334.         for (var i=0; i<imgLoadingQueue.length; i++){
  335.             var qi = imgLoadingQueue[i];
  336.             if (qi.cookie) continue;
  337.             var pi = qi.pathItem;
  338.             if(art_id==pi.artId && metadb.Compare(pi.metadb)){
  339.                 imgLoadingQueue.splice(i,1);
  340.                 var imgitem = ResizeAndStoreImage(image, pi);
  341.                 pi.loadConsuming = Logger.profiler.Time - pi.time;
  342.                 qi.whenDone && qi.whenDone(imgitem);
  343.                 break;
  344.             }
  345.         }
  346.     }
  347.    
  348.     this.OnLoadImageDone = function(cookie, image) {
  349.         if(!imgLoadingQueue.length || !cookie){
  350.             image && image.Dispose();
  351.             return;
  352.         }
  353.        
  354.         for (var i=0; i<imgLoadingQueue.length; i++){
  355.             var qi = imgLoadingQueue[i];
  356.             var pi = qi.pathItem;
  357.             if (cookie==qi.cookie){
  358.                 imgLoadingQueue.splice(i,1);
  359.                 var imgitem = ResizeAndStoreImage(image, pi);
  360.                 pi.loadConsuming = Logger.profiler.Time - pi.time;
  361.                 qi.whenDone && qi.whenDone(imgitem);
  362.                 break;
  363.             }
  364.         }
  365.     }
  366.    
  367.     this.ClearCache = function(){
  368.         imgLoadingQueue = [];
  369.         Caches = [];
  370.         CollectGarbage();           // Release memory.
  371.     }
  372.    
  373.     this.FlushQueue = function(){
  374.         if(!imgLoadingQueue.length) return;
  375.         for (var i=0; i<imgLoadingQueue.length; i++){
  376.             imgLoadingQueue[i].whenDone = null;
  377.         }
  378.         //imgLoadingQueue = [];
  379.     }
  380.    
  381.     this.Resize = function(w2, h2){
  382.         w = w2;
  383.         h = h2;
  384.     }
  385. }
  386.  
  387.  
  388. //----------------------------------------------------------
  389. function PathChecker(supportedTypes, maxFileSize, singleImageMode, cycleInWildCard, maxCacheCapacity){
  390.     this.AsyncChecking = false;
  391.     this.OnQueueFinished = null;
  392.     var albumArtCheckQueue = [];
  393.     var Caches = [];
  394.    
  395.     function PathCacheItem(pathitem, matchresult){
  396.         this.pathitem = pathitem.SimpleClone();
  397.         this.matchResult = [];
  398.         if(matchresult){
  399.             for(var i=0; i<matchresult.length; i++)
  400.                 this.matchResult[i] = matchresult[i].SimpleClone();
  401.         }
  402.     }
  403.    
  404.     function SearchCache(pathitem){
  405.         if(pathitem.artId==-1){
  406.             for(var i=0; i<Caches.length; i++){
  407.                 if(Caches[i].pathitem.path==pathitem.path){
  408.                     var mr = Caches[i].matchResult;
  409.                     var arr = [];
  410.                     for(var j=0; j<mr.length;j++)
  411.                         arr[j] = mr[j].SimpleClone();
  412.                     return arr;
  413.                 }
  414.             }
  415.         } else {
  416.             for(var i=0; i<Caches.length; i++){
  417.                 if(Caches[i].pathitem.artId==pathitem.artId && Caches[i].pathitem.metadb.Compare(pathitem.metadb))
  418.                     return Caches[i].pathitem.SimpleClone();
  419.             }
  420.         }
  421.         return null;
  422.     }
  423.    
  424.     function StoreCache(pathitem, arg){
  425.         if(pathitem.artId==-1){
  426.             if(pathitem.path.indexOf("*")==-1 && pathitem.path.indexOf("?")==-1)
  427.                 return;
  428.             else
  429.                 Caches.unshift(new PathCacheItem(pathitem, arg));
  430.         } else {
  431.             if(arg) pathitem.path = arg;
  432.             Caches.unshift(new PathCacheItem(pathitem));
  433.         }
  434.        
  435.         if (Caches.length>maxCacheCapacity)
  436.             Caches.splice(maxCacheCapacity, 1);
  437.     }
  438.    
  439.     this.Glob = function(pathitem, ignoreCache){
  440.         var resultArr = (!maxCacheCapacity || ignoreCache) ? null : SearchCache(pathitem);
  441.        
  442.         if (resultArr)
  443.             var cachehit = true;
  444.         else {
  445.             var cachehit = false;
  446.             paths = utils.Glob(pathitem.path).toArray();
  447.             resultArr = [];
  448.             var p, ext;
  449.             for (var k=0; k<paths.length; k++) {
  450.                 p = paths[k];
  451.                 ext = p.slice(p.lastIndexOf('.')+1).toLowerCase();
  452.                 for (var l=0; l<supportedTypes.length; l++) {
  453.                     if(ext==supportedTypes[l]){
  454.                         if (maxFileSize>0){
  455.                             var size = utils.FileTest(p,"s");
  456.                             if (size>maxFileSize)       // When size over 4G, "typeof(size)" will be unknown.
  457.                                 break;
  458.                         }
  459.                         resultArr.push(new PathItem(p, -1, null, false, pathitem.index));
  460.                         break;
  461.                     }
  462.                 }
  463.                 if((!cycleInWildCard || singleImageMode) && resultArr.length)
  464.                     break;
  465.             }
  466.             StoreCache(pathitem, resultArr);
  467.         }
  468.        
  469.         pathitem.results = resultArr;
  470.         pathitem.results.pathCacheHit = cachehit;
  471.         return pathitem;
  472.     }
  473.    
  474.     this.CheckAlbumArt = function(pathitem, whendone, ignoreCache){
  475.         var pi = (!maxCacheCapacity || ignoreCache) ? null : SearchCache(pathitem);
  476.         if (pi) {
  477.             pathitem.results.pathCacheHit = true;
  478.             if(pi.path){
  479.                 pathitem.path = pi.path;
  480.                 pathitem.embed = pi.embed;
  481.                 pathitem.results = [pathitem];
  482.             }
  483.             whendone && whendone(pathitem);
  484.             return;
  485.         } else {
  486.             pathitem.results.pathCacheHit = false;
  487.            
  488.             this.AsyncChecking = true;
  489.             utils.GetAlbumArtAsync(window_id, pathitem.metadb, pathitem.artId, false, pathitem.embed, true);
  490.             albumArtCheckQueue.push(new QueueItem(null, pathitem, whendone));
  491.         }
  492.     }
  493.    
  494.     this.OnCheckAlbumArtDone = function(metadb, art_id, image, image_path){
  495.         if(!albumArtCheckQueue.length){
  496.             image && image.Dispose();
  497.             return;
  498.         }
  499.        
  500.         var hit, qi, pi;
  501.         for (var i=0; i<albumArtCheckQueue.length; i++) {
  502.             qi = albumArtCheckQueue[i];
  503.             pi = qi.pathItem;
  504.             if(pi.artId==art_id && metadb.Compare(pi.metadb)){
  505.                 hit = true;
  506.                 break
  507.             }
  508.         }
  509.         if(!hit) return;
  510.        
  511.         if (image_path){
  512.             if(qi.whenDone) pi.results = [pi];
  513.             pi.path = image_path;
  514.             if(image_path==metadb.Path)
  515.                 pi.embed = true;
  516.            
  517.         }
  518.         StoreCache(pi);
  519.        
  520.         albumArtCheckQueue.splice(i,1);
  521.         if(!albumArtCheckQueue.length){
  522.             this.AsyncChecking = false;
  523.             this.OnQueueFinished && this.OnQueueFinished();
  524.             this.OnQueueFinished = null;
  525.         }
  526.        
  527.         qi.whenDone && qi.whenDone(pi);
  528.     }
  529.    
  530.     this.FlushQueue = function(){
  531.         //albumArtCheckQueue = [];
  532.         for(var i=0; i<albumArtCheckQueue.length; i++)
  533.             albumArtCheckQueue[i].whenDone = null;
  534.     }
  535.    
  536.     this.ClearCache = function(){
  537.         albumArtCheckQueue = [];
  538.         Caches = [];
  539.     }
  540. }
  541.  
  542.  
  543. //----------------------------------------------------------
  544. function PathProcessor(prop){
  545.     var pathGroups;
  546.     var currentPathGroup;
  547.     var whenDone;
  548.     var ignoreCache;
  549.     var pathArray = [];
  550.     var reg1 = /^\<(.*)\>$/;        // Use to get field from "<field>".
  551.     var _this = this;
  552.     this.pathChecker = new PathChecker(prop.SupportedTypes, prop.MaxFileSize, prop.SingleImageMode, prop.CycleInWildCard, prop.PathsCacheCapacity);
  553.    
  554.     function ParsePathSting(psgroup, metadb){
  555.         var pathGroups = [], pathArr, path, arr, artid = null;
  556.        
  557.         for (var i=0; i<psgroup.length; i++) {
  558.             pathArr = psgroup[i].split('||');
  559.             for (var j=0; j<pathArr.length; j++) {
  560.                 path = pathArr[j];
  561.                 if(!path) continue;
  562.                 arr = path.match(reg1);
  563.                 if (arr) {
  564.                     artid = AlbumArtId[arr[1]];
  565.                     if (artid==null) {      // Invalid artid, delete this item.
  566.                         pathArr.splice(j, 1);
  567.                         j--;
  568.                         continue;
  569.                     } else
  570.                         pathArr[j] = new PathItem(null, artid, metadb, j);
  571.                 } else
  572.                     pathArr[j] = new PathItem(path, -1, null, j);
  573.             }
  574.             pathGroups[i] = pathArr;
  575.         }
  576.        
  577.         return pathGroups;
  578.     }
  579.    
  580.     this.Parse = function(psgroup, metadb, whendone, ignorecache){
  581.         this.pathChecker.FlushQueue();
  582.         pathGroups = ParsePathSting(psgroup, metadb);
  583.         pathGroups.index = 0;
  584.         Logger.pathGroups = pathGroups;
  585.         whenDone = whendone;
  586.         ignoreCache = ignorecache;
  587.         CheckOneGroup(pathGroups.index);
  588.     }
  589.    
  590.     function CheckOneGroup(){
  591.         for (; pathGroups.index<pathGroups.length; pathGroups.index++) {
  592.             currentPathGroup = pathGroups[pathGroups.index];
  593.             currentPathGroup.checkFinished = 0;
  594.             currentPathGroup.results = [];
  595.            
  596.             var wait = false;
  597.             for (var i=0; i<currentPathGroup.length; i++) {
  598.                 var pi = currentPathGroup[i];
  599.                 pi.time = Logger.profiler.Time;
  600.                
  601.                 if(prop.SingleImageMode && currentPathGroup.results.length)
  602.                     currentPathGroup.checkFinished++;
  603.                 else{
  604.                     if (pi.artId==-1){       // Process common paths.
  605.                         _this.pathChecker.Glob(pi, ignoreCache);
  606.                         CheckFinished(pi);
  607.                     } else {        // Process "<...>" paths.
  608.                         wait = true;
  609.                         _this.pathChecker.CheckAlbumArt(pi, CheckFinished, ignoreCache);
  610.                     }
  611.                 }
  612.             }
  613.            
  614.             if (wait || currentPathGroup.results.length) break;
  615.         }
  616.        
  617.         if (currentPathGroup.checkFinished==currentPathGroup.length){
  618.             CheckOneGroupDone();
  619.         }
  620.     }
  621.    
  622.     function CheckFinished(pathitem){
  623.         pathitem.results.scanConsuming = Logger.profiler.Time - pathitem.time;
  624.         if(pathitem.results.length)
  625.             currentPathGroup.results.push(pathitem);
  626.         currentPathGroup.checkFinished++;
  627.        
  628.         if(pathitem.artId!=-1 && currentPathGroup.checkFinished==currentPathGroup.length){
  629.             if(currentPathGroup.results.length)
  630.                 currentPathGroup.results.sort(sortByIndex);
  631.             CheckOneGroupDone();
  632.         }
  633.     }
  634.    
  635.     var sortByIndex = function(a, b){
  636.         return a.index-b.index;
  637.     };
  638.    
  639.     function CheckOneGroupDone(){
  640.         var checkResults, lastIndex = 0;
  641.         pathArray = [];     // This one is the last result.
  642.         checkResults = pathGroups[0].results;
  643.         if(pathGroups.index!=0){
  644.             lastIndex = checkResults.length;
  645.             checkResults = checkResults.concat(pathGroups[1].results);
  646.         }
  647.        
  648.         if(checkResults.length){
  649.             if(prop.SingleImageMode)
  650.                 pathArray = [checkResults[0].results[0]];     // Cut to only one left.
  651.             else {
  652.                 for (var i=0; i<checkResults.length; i++){       // Remove duplicate paths.
  653.                     var cr1 = checkResults[i].results;
  654.                     for (var j=0; j<cr1.length; j++){
  655.                         var pi1 = cr1[j];
  656.                         if(pi1.duplicate)
  657.                             continue;
  658.                         if(!pi1.lowerPath)
  659.                             pi1.lowerPath = pi1.path.toLowerCase();
  660.                         for (var k=Math.max(i+1, lastIndex); k<checkResults.length; k++){        // lastIndex: skip the results from last group, they're already checked.
  661.                             var cr2 = checkResults[k].results;
  662.                             for (var l=0; l<cr2.length; l++){
  663.                                 var pi2 = cr2[l];
  664.                                 if(pi2.duplicate)
  665.                                     continue;
  666.                                 if(!pi1.embed || !pi2.embed || pi1.artId==pi2.artId){
  667.                                     if(!pi2.lowerPath)
  668.                                         pi2.lowerPath = pi2.path.toLowerCase();
  669.                                     if(pi1.lowerPath==pi2.lowerPath){
  670.                                         pi2.duplicate = true;
  671.                                         //cr2.splice(l,1);
  672.                                         //l--;
  673.                                     }
  674.                                 }
  675.                             }
  676.                         }
  677.                         pi1.lowerPath = undefined;
  678.                         pathArray.push(pi1);
  679.                     }
  680.                 }
  681.             }
  682.            
  683.             if(prop.AtMostLoadImagesNumber && pathArray.length>prop.AtMostLoadImagesNumber)
  684.                 pathArray.length = prop.AtMostLoadImagesNumber;
  685.         }
  686.        
  687.         if(
  688.             pathGroups.index>=pathGroups.length-1
  689.              || (
  690.                 pathArray.length
  691.                  && (
  692.                     prop.SingleImageMode
  693.                      || prop.TreatAdditionalPathAsBackup
  694.                      || (prop.AtMostLoadImagesNumber && pathArray.length>=prop.AtMostLoadImagesNumber)
  695.                 )
  696.             )
  697.         ){
  698.             if(!_this.pathChecker.AsyncChecking)
  699.                 ParseDone();
  700.             else
  701.                 _this.pathChecker.OnQueueFinished = ParseDone;
  702.         } else {
  703.             pathGroups.index++;
  704.             CheckOneGroup();
  705.         }
  706.     }
  707.    
  708.     function ParseDone(){
  709.         Logger.consuming = Logger.profiler.Time;
  710.         whenDone && whenDone(pathArray);
  711.         whenDone = null;
  712.     }
  713.    
  714.     this.OnCheckAlbumArtDone = function(metadb, art_id, image, image_path){
  715.         this.pathChecker.OnCheckAlbumArtDone(metadb, art_id, image, image_path);
  716.     }
  717. }
  718.  
  719.  
  720. // ImagesArray Class ===========================================
  721. function ImagesArray(prop){
  722.     var w, h;
  723.     this.Properties = prop;
  724.     this.pathArray;
  725.     this.currentImageItem = null;
  726.     this.length = 0;
  727.     this.pathFormatGroup = this.Properties.PathFormatGroup;
  728.     this.pathStringGroup = [];
  729.     var parse_done, load_done, load_index, load_ignoreCache;
  730.     var _this = this;
  731.    
  732.     var imgLoader = new ImageLoader(this.Properties.ImagesCacheCapacity);
  733.     var pathProcessor = new PathProcessor(this.Properties);
  734.    
  735.     function EvalTF(tfArr, metadb){
  736.         var arr = [], str;
  737.         for(var i=0; i<tfArr.length; i++){
  738.             str = tfArr[i];
  739.             str = fb.TitleFormat(str).EvalWithMetadb(metadb);
  740.             arr.push(str);
  741.         }
  742.         return arr;
  743.     }
  744.    
  745.     this.Update = function(metadb, whendone, ignoreCache){
  746.         this.pathArray = [];
  747.         this.length = 0;
  748.         parse_done = whendone;
  749.        
  750.         if(metadb){
  751.             this.pathStringGroup = EvalTF(this.pathFormatGroup, metadb);
  752.             Logger.pathFormatGroup = this.pathFormatGroup;
  753.             Logger.pathStringGroup = this.pathStringGroup;
  754.             Logger.Reset();
  755.             pathProcessor.Parse(this.pathStringGroup, metadb, ParseFinished, ignoreCache);
  756.         } else {
  757.             Logger.pathFormatGroup = [];
  758.             whendone && whendone();
  759.         }
  760.     }
  761.    
  762.     function ParseFinished(arr){
  763.         _this.pathArray = arr;
  764.         _this.length = _this.pathArray.length;
  765.         parse_done && parse_done();
  766.     }
  767.    
  768.     this.GetImage = function(index, whendone, ignoreCache){
  769.         if (!this.pathArray.length){
  770.             whendone && whendone();
  771.             return;
  772.         }
  773.         load_index = index;
  774.         load_done = whendone;
  775.         load_ignoreCache = ignoreCache;
  776.        
  777.         imgLoader.FlushQueue();
  778.         imgLoader.Load(this.pathArray[index], LoadFinished, ignoreCache);
  779.     }
  780.    
  781.     function LoadFinished(imgitem){
  782.         _this.currentImageItem = imgitem;
  783.         load_done && load_done(imgitem ? imgitem.img : null);
  784.         if(_this.Properties.ImagesCacheCapacity && _this.pathArray.length>1){
  785.             if(load_index==_this.pathArray.length-1)
  786.                 load_index = -1;
  787.             imgLoader.Load(_this.pathArray[load_index+1], null, load_ignoreCache);       // Preload next image.
  788.         }
  789.     }
  790.    
  791.     this.ClearCache = function(){
  792.         imgLoader.ClearCache();
  793.         pathProcessor.pathChecker.ClearCache();
  794.     }
  795.    
  796.     this.OnGetAlbumArtDone = function(metadb, art_id, image, image_path){
  797.         if(pathProcessor.pathChecker.AsyncChecking)
  798.             pathProcessor.OnCheckAlbumArtDone(metadb, art_id, image, image_path);
  799.         else
  800.             imgLoader.OnGetAlbumArtDone(metadb, art_id, image, image_path);
  801.     }
  802.    
  803.     this.OnLoadImageDone = function(cookie, image) {
  804.         imgLoader.OnLoadImageDone(cookie, image)
  805.     }
  806.    
  807.     this.Resize = function(w2, h2){
  808.         w = w2;
  809.         h = h2;
  810.         imgLoader.Resize(w, h);
  811.     }
  812. }
  813.  
  814.  
  815. // Display Class =============================================
  816. function Display(x, y, w, h, prop){
  817.     this.x = x; this.y = y; this.w = w; this.h = h;
  818.     this.Properties = prop;
  819.     this.ImageRange = {x: 0, y: 0, w: 0, h: 0};
  820.     var cover_default, cover_frame, cover_case;
  821.     var case_margin = 0;
  822.     var case_overlay = gdi.Image(workPath+"\\case_overlay.png");
  823.     var covers = [];
  824.     var timer;
  825.     var _this = this;
  826.    
  827.     //-----------------------------------------------
  828.     var animation = new function(prop){
  829.         this.enable = prop.Animation.Enable;
  830.         this.duration = prop.Animation.Duration;
  831.         this.refreshInterval = prop.Animation.RefreshInterval;
  832.         this.totalFrames = Math.floor(this.duration/this.refreshInterval);
  833.         this.rollingDistance = 0;
  834.         this.rollingOffsets = [];
  835.         this.opacitys = [];
  836.         this.switchDelay = Math.floor(this.totalFrames*2/5);
  837.        
  838.         // Calculate animation opacitys.
  839.         for (var i=0; i<this.totalFrames; i++)
  840.             this.opacitys[i] = Math.ceil(255*Math.pow((i+1)/this.totalFrames,2));
  841.        
  842.         this.CalcData = function(h){
  843.             // Calculate animation y offsets.
  844.             this.rollingDistance = Math.round(h/3);
  845.             this.rollingOffsets = [];
  846.             for (var i=0; i<this.totalFrames; i++)
  847.                 this.rollingOffsets[i] = Math.ceil(Math.sqrt((i+1)/this.totalFrames)*this.rollingDistance);
  848.         }
  849.         this.CalcData(0);
  850.     } (this.Properties);
  851.    
  852.     function ActiveTimer(){
  853.         if (!timer) timer = window.SetInterval(_this.OnTimer, animation.refreshInterval);
  854.     }
  855.    
  856.     function KillTimer(){
  857.         timer && window.ClearInterval(timer);
  858.         timer=null;
  859.     }
  860.    
  861.     //-----------------------------------------------
  862.     function CoverItem(type, img){
  863.         this.img;
  864.         this.opacity = 0;
  865.         this.faddingStep = 0;
  866.         this.faddingOn = false;
  867.         this.isDefault = false;
  868.         this.size;
  869.         this.SetImage(type, img);
  870.     }
  871.     var prttp = CoverItem.prototype;
  872.    
  873.     prttp.w = 0;
  874.     prttp.h = 0;
  875.     prttp.faddingStep_abs = Math.ceil(255 * animation.refreshInterval / animation.duration);
  876.    
  877.     prttp.CalcSize = function(dstw, dsth){
  878.         if(!dstw || !dsth || !this.img) return;
  879.         if(this.isDefault)
  880.             this.size = this.defaultImgSize;
  881.         else
  882.             this.size = CalcNewImgSize(this.img, dstw, dsth);
  883.     }
  884.    
  885.     prttp.SetImage = function(type, img){
  886.         if(type==undefined && img==undefined) return;
  887.         this.img && this.img!=cover_default && this.img.Dispose();
  888.        
  889.         if (type==0) {
  890.             this.img = null;
  891.             this.isDefault = false;
  892.             this.size = {};
  893.         } else if (img) {
  894.             this.img = img;
  895.             this.isDefault = false;
  896.             this.CalcSize(this.w, this.h);
  897.             if(this.w && this.h && (this.size.w!=img.width || this.size.h!=img.height)){
  898.                 this.img = ResizeImage(img, this.size.w, this.size.h);
  899.                 //img.Dispose();
  900.             }
  901.             this.img = this.img.CreateRawBitmap();
  902.         } else {
  903.             this.img = cover_default;
  904.             this.isDefault = true;
  905.             this.CalcSize(this.w, this.h);
  906.         }
  907.     }
  908.    
  909.     prttp.Draw = function(g, x, y, opacity){
  910.         if(!this.img) return;
  911.         opacity = 255*(opacity/255)*(this.opacity/255);
  912.         g.GdiAlphaBlend(this.img, x+this.size.x, y+this.size.y, this.size.w, this.size.h, 0, 0, this.img.width, this.img.height, opacity);
  913.     }
  914.    
  915.     prttp.Show = function(show){
  916.         this.faddingStep = show ? this.faddingStep_abs : -this.faddingStep_abs;
  917.         this.faddingOn = true;
  918.         ActiveTimer();
  919.     }
  920.    
  921.     prttp.OnTimer = function(){
  922.         if(!this.faddingOn) return;
  923.         this.opacity += this.faddingStep;
  924.         if(this.opacity<=0 || this.opacity>=255){
  925.             this.opacity = Math.max(Math.min(this.opacity, 255), 0);
  926.             this.faddingOn = true;
  927.             return false;
  928.         } else
  929.             return true;
  930.     }
  931.    
  932.     //-----------------------------------------------
  933.     function FadingCovers(type, img){
  934.         this.nowIsDefault = false;
  935.         this.size;
  936.         this.covers = [];
  937.         this.opacity = 0;
  938.         this.rolling = {
  939.             frame: 0,
  940.             offset: 0,
  941.             direction: 0,       // 1: in, -1: out, 0: stay.
  942.             delay: 0
  943.         };
  944.         this.SetImage(type, img);
  945.         this.rolling.frame = 0;
  946.     }
  947.     prttp = FadingCovers.prototype;
  948.    
  949.     prttp.w = 0;
  950.     prttp.h = 0;
  951.     prttp.animation = animation;
  952.        
  953.     prttp.SetImage = function(type, img){
  954.         if(type==undefined) return;
  955.         if(type){
  956.             if(this.covers.length)
  957.                 this.covers[this.covers.length-1].SetImage(type, img);
  958.             else{
  959.                 this.covers.push(new CoverItem(type, img));
  960.                 this.covers[0].opacity = 255;
  961.             }
  962.         } else
  963.             this.covers = [];
  964.     }
  965.    
  966.     prttp.CalcSize = function(){
  967.         for(var i=0; i<this.covers.length; i++)
  968.             this.covers[i].CalcSize(this.w, this.h);
  969.     }
  970.        
  971.     prttp.Draw = function(g, x, y){
  972.         var tempy = y+this.rolling.offset;
  973.        
  974.         if(cover_frame)
  975.             g.GdiAlphaBlend(cover_frame, x-3, tempy-2, cover_frame.width, cover_frame.height, 0, 0, cover_frame.width, cover_frame.height, this.opacity);
  976.        
  977.         for(var i=0; i<this.covers.length; i++)
  978.             this.covers[i].Draw(g, x, tempy, this.opacity);
  979.     }
  980.    
  981.     prttp.ChangeImage = function(type, img){
  982.         if(this.covers.length)
  983.             this.covers[this.covers.length-1].Show(false);
  984.         var newcover = new CoverItem(type, img);
  985.         this.covers.push(newcover);
  986.         newcover.Show(true);
  987.     }
  988.    
  989.     prttp.Roll = function(direction, delay){
  990.         this.rolling.direction = direction;
  991.         this.rolling.delay = delay;
  992.         ActiveTimer();
  993.     }
  994.    
  995.     prttp.OnTimer = function(){
  996.         var flag;
  997.         for(var i=0; i<this.covers.length; i++){
  998.             flag = this.covers[i].OnTimer() || flag;
  999.             if(this.covers[i].opacity==0){
  1000.                 this.covers.splice(i,1);
  1001.                 i--
  1002.             }
  1003.         }
  1004.        
  1005.         if(this.rolling.direction!=0){
  1006.             if(this.rolling.delay){
  1007.                 this.rolling.delay--;
  1008.                 flag = true;
  1009.             } else {
  1010.                 var k = Math.min(Math.max(0, this.rolling.frame), this.animation.totalFrames-1);
  1011.                 this.rolling.offset = this.animation.rollingOffsets[k] - this.animation.rollingDistance;
  1012.                 this.opacity = this.animation.opacitys[k];
  1013.                 this.rolling.frame += this.rolling.direction;
  1014.                
  1015.                 if(this.rolling.frame<0 || this.rolling.frame>=this.animation.totalFrames){
  1016.                     this.rolling.direction = 0;
  1017.                     flag = false || flag;
  1018.                 } else
  1019.                     flag = true;
  1020.             }
  1021.         }
  1022.        
  1023.         return flag;
  1024.     }
  1025.    
  1026.     //-----------------------------------------------
  1027.     this.SetImage = function(type, img){
  1028.         if(type==undefined) return;
  1029.         if(type){
  1030.             if(covers.length)
  1031.                 covers[covers.length-1].SetImage(type, img);
  1032.             else{
  1033.                 covers.push(new FadingCovers(type, img));
  1034.                 covers[0].opacity = 255;
  1035.                 covers[0].rolling.frame = animation.totalFrames;
  1036.             }
  1037.         } else
  1038.             covers = [];
  1039.     }
  1040.    
  1041.     this.ChangeImage = function(type, img, aniNo){        // type: 0: to empty, 1: to image or default image.
  1042.         if(!covers.length && aniNo==1) aniNo = 2;
  1043.        
  1044.         if(aniNo==0 || !animation.enable)
  1045.             this.SetImage(type, img);
  1046.         else if (aniNo==1)
  1047.             covers[covers.length-1].ChangeImage(type, img);
  1048.         else if (aniNo==2) {
  1049.             var newcover = new FadingCovers(type, img);
  1050.             if(covers.length){
  1051.                 covers[covers.length-1].Roll(-1);
  1052.                 newcover.Roll(1, animation.switchDelay);
  1053.             } else
  1054.                 newcover.Roll(1);
  1055.             covers.push(newcover);
  1056.         }
  1057.     }
  1058.    
  1059.     this.Draw = function(g){
  1060.         for(var i=0; i<covers.length; i++)
  1061.             covers[i].Draw(g, this.ImageRange.x, this.ImageRange.y);
  1062.     //    g.DrawRect(this.ImageRange.x, this.ImageRange.y, this.ImageRange.w-1, this.ImageRange.h-1, 1, RGBA(255,0,0,255));       // Debug rect: cover range.
  1063.        
  1064.         if(this.Properties.ShowCoverCase)
  1065.             g.GdiAlphaBlend(cover_case, this.ImageRange.x+(this.ImageRange.w-cover_case.width)/2, this.ImageRange.y+(this.ImageRange.h-cover_case.height)/2,
  1066.             cover_case.width, cover_case.height,0, 0, cover_case.width, cover_case.height);
  1067.        
  1068.         if(this.menuButton)
  1069.             this.menuButton.Draw(g);
  1070.     }
  1071.    
  1072.     this.Refresh = function(type, img){
  1073.         this.SetImage(type, img);
  1074.         window.Repaint();
  1075.     }
  1076.    
  1077.     this.Resize = function(w, h){
  1078.         this.w = w;
  1079.         this.h = h;
  1080.        
  1081.         var old_w = this.ImageRange.w;
  1082.         var old_h = this.ImageRange.h;
  1083.         if(this.Properties.ShowCoverCase)
  1084.             this.ImageRange.w = Math.floor(Math.max(Math.min(Math.min(this.w*17/19, this.w-12), Math.min(this.h*17/19, this.h-12)-2), 0));
  1085.         else
  1086.             this.ImageRange.w = Math.max(Math.min(this.w, this.h)-6, 0);
  1087.         this.ImageRange.h = this.ImageRange.w;
  1088.         this.ImageRange.x = this.x + Math.round((this.w - this.ImageRange.w)/2);
  1089.         this.ImageRange.y = this.y + Math.round((this.h - this.ImageRange.h)/2);
  1090.        
  1091.         if(this.ImageRange.w!=old_w || this.ImageRange.h!=old_h){
  1092.             GenerateImages(this.ImageRange.w, this.ImageRange.h, this.Properties.ShowCoverCase);
  1093.             CoverItem.prototype.w = this.ImageRange.w;
  1094.             CoverItem.prototype.h = this.ImageRange.h;
  1095.             CoverItem.prototype.defaultImg = cover_default;
  1096.             CoverItem.prototype.defaultImgSize = {x:0, y:0, w: this.ImageRange.w, h: this.ImageRange.h};
  1097.             FadingCovers.prototype.w = this.ImageRange.w;
  1098.             FadingCovers.prototype.h = this.ImageRange.h;
  1099.            
  1100.             for(var i=0; i<covers.length; i++)
  1101.                 covers[i].CalcSize();
  1102.         }
  1103.        
  1104.         animation.CalcData(this.h);
  1105.        
  1106.         if(this.menuButton){
  1107.             this.menuButton.x = this.x + Math.round(this.w/2) - this.menuButton.w/2;
  1108.             this.menuButton.y = this.ImageRange.y + this.ImageRange.h + (this.Properties.ShowCoverCase ? case_margin : 2) - this.menuButton.h;
  1109.         }
  1110.     }
  1111.    
  1112.     function GenerateImages(w, h, generateCase){
  1113.         // Draw default cover ----------------
  1114.         cover_default && cover_default.Dispose();
  1115.         cover_default = gdi.CreateImage(w, h);
  1116.         var g = cover_default.GetGraphics();
  1117.         // Grad background.
  1118.         g.FillGradRect(0, 0, cover_default.width, cover_default.height, 60, RGBA(255,255,255,255), RGBA(233,233,233,255));
  1119.         g.DrawRect(0, 0, cover_default.width-1, cover_default.height-1, 1, RGBA(204,204,204,255));
  1120.         g.SetSmoothingMode(4);
  1121.         // Big circle.
  1122.         var s = Math.min(w, h);
  1123.         var d = s - Math.floor(s/4)*2;
  1124.         var margin_left = Math.round((w-d)/2);
  1125.         var margin_top = Math.round((h-d)/2);
  1126.         g.FillEllipse(margin_left, margin_top, d-1, d-1, RGBA(255,255,255,255));
  1127.         g.DrawEllipse(margin_left, margin_top, d-1, d-1, 1, RGBA(235,235,235,255));
  1128.         // Small circle.
  1129.         d = s - Math.floor(s*53/128)*2;
  1130.         margin_left = Math.round((w-d)/2);
  1131.         margin_top = Math.round((h-d)/2);
  1132.         g.FillEllipse(margin_left, margin_top, d-1, d-1, RGBA(241,241,241,255));
  1133.         g.DrawEllipse(margin_left, margin_top, d-1, d-1, 1, RGBA(229,229,229,255));
  1134.         cover_default.ReleaseGraphics(g);
  1135.         cover_default = CreateRawBitmap2(cover_default);
  1136.        
  1137.         // Draw cover frame -----------------
  1138.         cover_frame && cover_frame.Dispose();
  1139.         cover_frame = gdi.CreateImage(w+6, h+6);
  1140.         g = cover_frame.GetGraphics();
  1141.         g.SetSmoothingMode(4);
  1142.         g.DrawRoundRect(0, 0, w+5, h+5, 2, 2, 1, RGBA(0,0,0,6));
  1143.         g.DrawRoundRect(1, 1, w+3, h+3, 1, 1, 1, RGBA(0,0,0,17));
  1144.         g.DrawLine(2, h+3, w+3, h+3, 1, RGBA(0,0,0,34));
  1145.         g.SetSmoothingMode(0);
  1146.         g.FillSolidRect(2, 1, w+2, h+2, RGBA(255,255,255,255));
  1147.         cover_frame.ReleaseGraphics(g);
  1148.         cover_frame = CreateRawBitmap2(cover_frame);
  1149.        
  1150.         if(!generateCase) return;
  1151.        
  1152.         // Draw cover case -------------------
  1153.         cover_case && cover_case.Dispose();
  1154.         margin_left = Math.max(Math.round(w/17), 6);
  1155.         margin_top = Math.max(Math.round(h/17), 6) + 1;
  1156.         cover_case = gdi.CreateImage(w+margin_left*2, h+margin_top*2);
  1157.         var ellipse_margin_top = Math.round(cover_case.height*7/50);
  1158.         var ellipse_margin_left = Math.round(cover_case.width*41.3/100);
  1159.         var d = cover_case.width - 2*ellipse_margin_left;
  1160.         var ellipse = gdi.CreateImage(d, d/2);
  1161.         g = ellipse.GetGraphics();
  1162.         g.SetSmoothingMode(2);
  1163.         g.DrawEllipse(2, -Math.round(d/2)+2, d-5, d-5, 1, RGBA(0,0,0,12));
  1164.         g.DrawEllipse(1, -Math.round(d/2)+1, d-3, d-3, 1, RGBA(45,45,45,118));
  1165.         g.DrawEllipse(0, -Math.round(d/2), d-1, d-1, 1, RGBA(255,255,255,90));
  1166.         //g.DrawRect(0, 0, ellipse.width-1, ellipse.height-1, 1, RGBA(255,0,0,255));
  1167.         ellipse.ReleaseGraphics(g);
  1168.        
  1169.         var body = gdi.CreateImage(cover_case.width, cover_case.height-ellipse_margin_top);
  1170.         g = body.GetGraphics();
  1171.         g.SetSmoothingMode(4);
  1172.         g.DrawRoundRect(0, -1, body.width-1, body.height, 3, 3, 1, RGBA(0,0,0,6));
  1173.         g.DrawRoundRect(1, 0, body.width-3, body.height-2, 2, 2, 1, RGBA(0,0,0,12));
  1174.         g.DrawRoundRect(2, 1, body.width-5, body.height-4, 1, 1, 1, RGBA(0,0,0,20));
  1175.         g.DrawLine(3, body.height-4, body.width-4, body.height-4, 1, RGBA(0,0,0,30));
  1176.         g.DrawRoundRect(2, 1, body.width-5, body.height-5, 1, 1, 1, RGBA(51,51,51,102));
  1177.         g.DrawRect(3, 2, body.width-7, body.height-7, 1, RGBA(255,255,255,64));
  1178.         //g.FillGradRect(0, 0, body.width, body.height, 50, RGBA(255,255,255,64), RGBA(255,255,255,32));
  1179.         body.ReleaseGraphics(g);
  1180.         g = cover_case.GetGraphics();
  1181.         g.DrawImage(ellipse, ellipse_margin_left, ellipse_margin_top+2, ellipse.width, ellipse.height, 0, 0, ellipse.width, ellipse.height);
  1182.         g.DrawImage(body, 0, ellipse_margin_top, ellipse_margin_left+2, 2, 0, 0, ellipse_margin_left+2, 2);
  1183.         g.DrawImage(body, 0, ellipse_margin_top+2, ellipse_margin_left, 1, 0, 2, ellipse_margin_left, 1);
  1184.         g.DrawImage(body, ellipse_margin_left+2, ellipse_margin_top, 1, 2, body.width-2, 0, 1, 2);
  1185.         g.DrawImage(body, ellipse_margin_left+d-2, ellipse_margin_top, ellipse_margin_left+2, 2, ellipse_margin_left+d-2, 0, ellipse_margin_left+2, 2);
  1186.         g.DrawImage(body, ellipse_margin_left+d, ellipse_margin_top+2, ellipse_margin_left, 1, ellipse_margin_left+d, 2, ellipse_margin_left, 1);
  1187.         g.DrawImage(body, ellipse_margin_left+d-3, ellipse_margin_top, 1, 2, 1, 0, 1, 2);
  1188.         g.DrawImage(body, 0, ellipse_margin_top+3, body.width, body.height-3, 0, 3, body.width, body.height-3);
  1189.         g.DrawImage(case_overlay, 3, ellipse_margin_top+2, cover_case.width-6, cover_case.height-ellipse_margin_top-6, 1, 0, case_overlay.width-2, case_overlay.height-1);
  1190.         cover_case.ReleaseGraphics(g);
  1191.         cover_case = CreateRawBitmap2(cover_case);
  1192.        
  1193.         ellipse.Dispose();
  1194.         body.Dispose();
  1195.        
  1196.         case_margin = margin_left-3;
  1197.     }
  1198.    
  1199.     //---------------------------------------------
  1200.     this.isXYIn = function (x, y) {
  1201.         return x >= this.ImageRange.x - case_margin
  1202.         && y >= this.ImageRange.y-  case_margin
  1203.         && x <= this.ImageRange.x + this.ImageRange.w + case_margin
  1204.         && y <= this.ImageRange.y + this.ImageRange.h + case_margin;
  1205.     }
  1206.    
  1207.     if(this.Properties.MenuButton.Show){
  1208.         this.menuButton = new function(x, y, w, h, prop){
  1209.             this.x = x, this.y = y, this.w = w, this.h = h;
  1210.             this.caption = "";
  1211.             this.menu = null;
  1212.             this.state = 0;     // 0: hide, 1: normal, 2: hover, 3: down.
  1213.             var state_old = -1;
  1214.             var menuShowing = false;
  1215.             var faddingStep = 0;
  1216.             var faddingStep2 = 0;
  1217.             var opacity = 255;
  1218.             var opacity2 = 0;
  1219.             var fmt = prop.MacTypeSupport ? 1|4|100 : StrFmt(1, 2, 100, 0);
  1220.             var imgCache = {img: null, text: ""};
  1221.             var img_btn_src = gdi.Image(workPath+"\\btn.png");
  1222.             var img_btn = gdi.CreateImage(this.w, this.h*3);
  1223.             var g = img_btn.GetGraphics();
  1224.             g.DrawImage(img_btn_src, 0, 0, 4, img_btn.height, 0, 0, 4, img_btn_src.height);
  1225.             g.DrawImage(img_btn_src, 4, 0, img_btn.width-8, img_btn.height, 4, 0, img_btn_src.width-8, img_btn_src.height);
  1226.             g.DrawImage(img_btn_src, img_btn.width-4, 0, 4, img_btn.height, img_btn_src.width-4, 0, 4, img_btn_src.height);
  1227.             img_btn.ReleaseGraphics(g);
  1228.             img_btn = CreateRawBitmap2(img_btn);
  1229.             img_btn_src.Dispose();
  1230.             img_btn_src = undefined;
  1231.            
  1232.             this.isXYIn = function (x, y) {
  1233.                 return x >= this.x && y >= this.y && x <= this.x + this.w && y <= this.y + this.h;
  1234.             }
  1235.            
  1236.             function StrFmt(alignH, alignV, trim, flag){
  1237.                 return (alignH<<28)|(alignV<<24)|(trim<<20)|( StringTrimmingEllipsisWord = 4 << 20)|flag;
  1238.             }
  1239.            
  1240.             this.Draw = function(g){
  1241.                 if(opacity2<=0) return;
  1242.                
  1243.                 g.GdiAlphaBlend(img_btn, this.x, this.y, this.w, this.h, 0, this.state*this.h, img_btn.width, this.h, opacity*opacity2/255);
  1244.                 if(state_old!=-1)
  1245.                     g.GdiAlphaBlend(img_btn, this.x, this.y, this.w, this.h, 0, state_old*this.h, img_btn.width, this.h, (255-opacity)*opacity2/255);
  1246.                
  1247.                 if (!imgCache.img || imgCache.text!=this.caption) {
  1248.                     imgCache.text = this.caption;
  1249.                     imgCache.img && imgCache.img.Dispose();
  1250.                     imgCache.img = gdi.CreateImage(this.w, this.h);
  1251.                     var g2 = imgCache.img.GetGraphics();
  1252.                     g2.SetTextRenderingHint(4);
  1253.                     if(prop.MacTypeSupport)
  1254.                         g2.GdiDrawText(this.caption, prop.Font, RGBA(0,0,0,255), 0, 1, imgCache.img.width, imgCache.img.height, fmt);
  1255.                     else
  1256.                         g2.DrawString(this.caption, prop.Font, RGBA(0,0,0,255), 0, -1, imgCache.img.width, imgCache.img.height, fmt);     // This is the blured text background.
  1257.                     imgCache.img.BoxBlur(2, 2);
  1258.                     if(prop.MacTypeSupport)
  1259.                         g2.GdiDrawText(this.caption, prop.Font, prop.CaptionColor, 0, 1, imgCache.img.width, imgCache.img.height, fmt);
  1260.                     else {
  1261.                         g2.DrawImage(imgCache.img, 0, 0, imgCache.img.width, imgCache.img.height, 0, 0, imgCache.img.width, imgCache.img.height);
  1262.                         g2.DrawString(this.caption, prop.Font, prop.CaptionColor, 0, -1, imgCache.img.width, imgCache.img.height, fmt);     // This is foreground text.
  1263.                     }
  1264.                     imgCache.img.ReleaseGraphics(g2);
  1265.                     imgCache.img = CreateRawBitmap2(imgCache.img);
  1266.                 }
  1267.                 g.GdiAlphaBlend(imgCache.img, this.x, this.y, this.w, this.h, 0, 0, imgCache.img.width, imgCache.img.height, opacity2);
  1268.             }
  1269.            
  1270.             this.OnClick = function(x, y){
  1271.                 menuShowing = true;
  1272.                 this.menu.Pop(this.x, this.y + this.h);
  1273.                 menuShowing = false;
  1274.             }
  1275.            
  1276.             this.ChangeState = function(s){
  1277.                 if(s==this.state || (s==0 && menuShowing)) return;
  1278.                 state_old = this.state;
  1279.                 this.state = s;
  1280.                 opacity = 0;
  1281.                 if(s==0)
  1282.                     faddingStep = 43;
  1283.                 else if (s==1)
  1284.                     faddingStep = 85;
  1285.                 else
  1286.                     faddingStep = 128;
  1287.                 ActiveTimer();
  1288.             }
  1289.            
  1290.             this.ChangeState2 = function(s2){
  1291.                 if(s2){
  1292.                     if(opacity2==255){
  1293.                         faddingStep2 = 0;
  1294.                         return;
  1295.                     }
  1296.                     faddingStep2 = 85;
  1297.                 } else {
  1298.                     if(opacity2==0 || menuShowing){
  1299.                         faddingStep2 = 0;
  1300.                         return;
  1301.                     }
  1302.                     faddingStep2 = -43;
  1303.                 }
  1304.                 ActiveTimer();
  1305.             }
  1306.            
  1307.             this.OnTimer = function(){
  1308.                 var flag;
  1309.                 if(faddingStep){
  1310.                     opacity += faddingStep;
  1311.                     if(opacity>=255){
  1312.                         opacity = 255;
  1313.                         state_old = -1;
  1314.                         faddingStep = 0;
  1315.                     } else
  1316.                         flag = true;
  1317.                 }
  1318.                
  1319.                 if(faddingStep2){
  1320.                     opacity2 += faddingStep2;
  1321.                     if(opacity2<=0 || opacity2>=255){
  1322.                         opacity2 = Math.max(Math.min(opacity2, 255), 0);
  1323.                         faddingStep2 = 0;
  1324.                     } else
  1325.                         flag = true;
  1326.                 }
  1327.                 return flag;
  1328.             }
  1329.            
  1330.             this.ClearCache = function(){
  1331.                 imgCache.img && imgCache.img.Dispose();
  1332.                 imgCache.img = null;
  1333.                 imgCache.text = "";
  1334.             }
  1335.            
  1336.         } (0, 0, 82, 22, this.Properties.MenuButton);
  1337.        
  1338.         var btnStatus = -1;     // -1: hide.
  1339.         var bugx, bugy;     // Prevent a windows menu behaviour bug.
  1340.        
  1341.         this.OnMouseMove = function(x, y){
  1342.             if (x==bugx && y==bugy) {
  1343.                 bugx = null, bugy = null;
  1344.                 return;
  1345.             }
  1346.             bugx = null, bugy = null;
  1347.            
  1348.             if(btnStatus==2)
  1349.                 this.menuButton.ChangeState(this.menuButton.isXYIn(x,y) ? 2 : 1);
  1350.             else if (this.isXYIn(x, y)) {
  1351.                 if(btnStatus==-1)
  1352.                     this.menuButton.ChangeState2(1);
  1353.                 if(this.menuButton.isXYIn(x,y)){
  1354.                     btnStatus = 1;
  1355.                     this.menuButton.ChangeState(1);
  1356.                 } else {
  1357.                     btnStatus = 0;
  1358.                     this.menuButton.ChangeState(0);
  1359.                 }
  1360.             } else if (btnStatus!=-1)
  1361.                 this.OnMouseLeave();
  1362.         }
  1363.        
  1364.         this.OnMouseDown = function(x, y){
  1365.             if(btnStatus==1){
  1366.                 btnStatus = 2;
  1367.                 this.menuButton.ChangeState(2);
  1368.             }
  1369.         }
  1370.        
  1371.         this.OnMouseUp = function(x, y){
  1372.             if(this.menuButton.state==2){
  1373.                 bugx = x, bugy = y;
  1374.                 this.menuButton.OnClick(x, y);
  1375.                 this.menuButton.ChangeState2(0);
  1376.             }
  1377.             this.OnMouseMove(x, y);
  1378.         }
  1379.        
  1380.         this.OnMouseLeave = function(x, y){
  1381.             btnStatus = -1;
  1382.             this.menuButton.ChangeState(0);
  1383.             this.menuButton.ChangeState2(0);
  1384.         }
  1385.     }
  1386.    
  1387.     this.OnTimer = function(){
  1388.         //fb.trace(covers.length)
  1389.         var flag;
  1390.         for(var i=0; i<covers.length; i++){
  1391.             flag = covers[i].OnTimer() || flag;
  1392.             if(covers[i].rolling.frame<0){
  1393.                 covers.splice(i,1);
  1394.                 i--;
  1395.             }
  1396.         }
  1397.        
  1398.         if(_this.menuButton)
  1399.             flag = _this.menuButton.OnTimer() || flag;
  1400.        
  1401.         window.Repaint();
  1402.         if(!flag) KillTimer();
  1403.     }
  1404. }
  1405.  
  1406.  
  1407. // Controller Class ==========================================
  1408. function Controller(imgArray, imgDisplay, prop){
  1409.     this.Properties = prop;
  1410.     var groupString = null;
  1411.     var isFollowingCursor;
  1412.     var currentMetadb = null;
  1413.     var currentIndex = -1;
  1414.     var currentPathItem = null;
  1415.     var currentImage;
  1416.     var shellObj;
  1417.     var _this = this;
  1418.    
  1419.     if(!this.Properties.SingleImageMode){
  1420.         this.imgSwitchMenu = new function(){
  1421.             var baseMenu, subMenu;
  1422.             var funcs = {};
  1423.             this.matchResult;
  1424.            
  1425.             this.Update = function(){
  1426.                 var arr = imgArray.pathArray;
  1427.                 this.matchResult = [[],[],[],[],[]];
  1428.                 this.matchResult[-1] = [];
  1429.                 if(arr){
  1430.                     for(var i=0; i<arr.length; i++)
  1431.                         this.matchResult[arr[i].artId].push(i);
  1432.                 }
  1433.             }
  1434.            
  1435.             this.Build = function(){
  1436.                 this.Update();
  1437.                
  1438.                 var id = 1;
  1439.                 baseMenu = window.CreatePopupMenu();
  1440.                
  1441.                 funcs[id] = [_this.SwitchCyclePause, null];
  1442.                 baseMenu.AppendMenuItem(_this.cycle.paused ? MF_STRING : MF_CHECKED, id++, GetText("Cycle on"));
  1443.                 baseMenu.AppendMenuSeparator();
  1444.                 funcs[id] = [_this.SwitchCover, this.matchResult[0][0]];
  1445.                 baseMenu.AppendMenuItem(this.matchResult[0].length ? MF_STRING : MF_DISABLED, id++, GetText("Front"));
  1446.                 funcs[id] = [_this.SwitchCover, this.matchResult[1][0]];
  1447.                 baseMenu.AppendMenuItem(this.matchResult[1].length ? MF_STRING : MF_DISABLED, id++, GetText("Back"));
  1448.                 funcs[id] = [_this.SwitchCover, this.matchResult[2][0]];
  1449.                 baseMenu.AppendMenuItem(this.matchResult[2].length ? MF_STRING : MF_DISABLED, id++, GetText("Disc"));
  1450.                 id++;
  1451.                 funcs[id] = [_this.SwitchCover, this.matchResult[4][0]];
  1452.                 baseMenu.AppendMenuItem(this.matchResult[4].length ? MF_STRING : MF_DISABLED, id--, GetText("Artist"));
  1453.                 funcs[id] = [_this.SwitchCover, this.matchResult[3][0]];
  1454.                 baseMenu.AppendMenuItem(this.matchResult[3].length ? MF_STRING : MF_DISABLED, id++, GetText("Icon"));
  1455.                 id++;
  1456.  
  1457.                 var check;
  1458.                 if(this.matchResult[-1].length){
  1459.                     if(this.matchResult[-1].length>1){
  1460.                         subMenu = window.CreatePopupMenu();
  1461.                         funcs[id] = [_this.SwitchOthers, -2];
  1462.                         subMenu.AppendMenuItem(MF_STRING, id++, GetText("First image"));
  1463.                         funcs[id] = [_this.SwitchOthers, -1];
  1464.                         subMenu.AppendMenuItem(MF_STRING, id++, GetText("Previous image"));
  1465.                         funcs[id] = [_this.SwitchOthers, 1];
  1466.                         subMenu.AppendMenuItem(MF_STRING, id++, GetText("Next image"));
  1467.                         funcs[id] = [_this.SwitchOthers, 2];
  1468.                         subMenu.AppendMenuItem(MF_STRING, id++, GetText("Last image"));
  1469.                     }
  1470.                    
  1471.                     if(currentPathItem){
  1472.                         var caption = GetText("Others") + " (" + Math.max(currentIndex-(imgArray.length-this.matchResult[-1].length)+1, 0) + "/" + this.matchResult[-1].length + ")";
  1473.                         var flag = currentPathItem.artId==-1 ? MF_CHECKED : MF_STRING;
  1474.                         if(subMenu)
  1475.                             subMenu.AppendTo(baseMenu, flag | MF_POPUP, caption);
  1476.                         else {
  1477.                             funcs[id] = [_this.SwitchCover, this.matchResult[-1][0]];
  1478.                             baseMenu.AppendMenuItem(flag, id++, caption);
  1479.                         }
  1480.                     }
  1481.                 } else if(imgArray.Properties.AdditionalPathFormat)
  1482.                     baseMenu.AppendMenuItem(MF_DISABLED, id++, GetText("Others") + " (0)");
  1483.                
  1484.                 if(currentPathItem && currentPathItem.artId!=-1)
  1485.                     baseMenu.CheckMenuItem(currentPathItem.artId+2, true);
  1486.             }
  1487.            
  1488.             this.Pop = function(x, y){
  1489.                 if(!baseMenu) this.Build();
  1490.                 var ret = baseMenu.TrackPopupMenu(x, y);
  1491.                 if(ret)
  1492.                     funcs[ret] && funcs[ret][0].call(_this, funcs[ret][1]);
  1493.                 this.Dispose();
  1494.             }
  1495.            
  1496.             this.Dispose = function(){
  1497.                 baseMenu && baseMenu.Dispose();
  1498.                 baseMenu = null;
  1499.                 subMenu = null;
  1500.                 funcs = {};
  1501.             }
  1502.         }();
  1503.     }
  1504.    
  1505.     this.funcMenu = new function(){
  1506.         var baseMenu = null, subMenus = [];
  1507.         var funcs = {};
  1508.        
  1509.         this.Build = function(){
  1510.             var id = 1;
  1511.             baseMenu = window.CreatePopupMenu();
  1512.            
  1513.             funcs[id] = [_this.AboutCurrentImage, null];
  1514.             baseMenu.AppendMenuItem(currentPathItem ? MF_STRING : MF_DISABLED, id++, GetText("About current picture"));
  1515.             funcs[id] = [_this.ViewWithExternalViewer, null];
  1516.             baseMenu.AppendMenuItem(currentPathItem ? MF_STRING : MF_DISABLED, id++, GetText("View with external viewer"));
  1517.             funcs[id] = [_this.OpenContainingFolder, null];
  1518.             baseMenu.AppendMenuItem(currentPathItem ? MF_STRING : MF_DISABLED, id++, GetText("Open containing folder"));
  1519.             funcs[id] = [_this.Refresh, true];
  1520.             baseMenu.AppendMenuItem(MF_STRING, id++, GetText("Refresh"));
  1521.             //funcs[id] = [_this.ShowMatchLog, true];
  1522.             //baseMenu.AppendMenuItem(currentMetadb ? MF_STRING : MF_DISABLED, id++, GetText("Show match log"));
  1523.            
  1524.             baseMenu.AppendMenuSeparator();
  1525.             if(currentMetadb){
  1526.                 var subMenu_MAP = window.CreatePopupMenu();
  1527.                 subMenus.push(subMenu_MAP);
  1528.                 subMenu_MAP.AppendTo(baseMenu, MF_POPUP, GetText("Manage attached pictures"));
  1529.                
  1530.                 funcs[id] = [_this.ManageAttachedImages, [0, currentMetadb]];
  1531.                 subMenu_MAP.AppendMenuItem(MF_STRING, id++, GetText("Edit attached pictures"));
  1532.                 funcs[id] = [_this.ManageAttachedImages, [1, currentMetadb]];
  1533.                 subMenu_MAP.AppendMenuItem(MF_STRING, id++, GetText("Batch attach pictures"));
  1534.                 funcs[id] = [_this.ManageAttachedImages, [2, currentMetadb]];
  1535.                 subMenu_MAP.AppendMenuItem(MF_STRING, id++, GetText("Remove all pictures"));
  1536.             } else
  1537.                 baseMenu.AppendMenuItem(MF_DISABLED, id++, GetText("Manage attached pictures"));
  1538.            
  1539.             if(currentMetadb && _this.Properties.SearchScriptPresets.length){
  1540.                 var subMenu_SPFI = window.CreatePopupMenu();
  1541.                 subMenus.push(subMenu_SPFI);
  1542.                 subMenu_SPFI.AppendTo(baseMenu, MF_POPUP, GetText("Search pictures from internet"));
  1543.                 var caption;
  1544.                 for(var i=0; i<_this.Properties.SearchScriptPresets.length; i++){
  1545.                     funcs[id] = [_this.SearchFromWeb, [i, currentMetadb]];
  1546.                     caption = fb.TitleFormat(_this.Properties.SearchScriptPresets[i].name).EvalWithMetadb(currentMetadb);
  1547.                     if(caption.length>42)
  1548.                         caption = caption.slice(0, 40) + "...";
  1549.                     subMenu_SPFI.AppendMenuItem(MF_STRING, id++, caption);
  1550.                 }
  1551.             } else
  1552.                 baseMenu.AppendMenuItem(MF_DISABLED, id++, GetText("Search pictures from internet"));
  1553.            
  1554.             baseMenu.AppendMenuSeparator();
  1555.             var subMenu_IS = window.CreatePopupMenu();
  1556.             subMenus.push(subMenu_IS);
  1557.             subMenu_IS.AppendTo(baseMenu, MF_POPUP, GetText("Image stretching"));
  1558.            
  1559.             funcs[id] = [_this.SetStretchProperties, 0];
  1560.             subMenu_IS.AppendMenuItem(imgArray.Properties.Stretch ? MF_CHECKED : MF_STRING, id++, GetText("Stretch image"));
  1561.             funcs[id] = [_this.SetStretchProperties, 1];
  1562.             subMenu_IS.AppendMenuItem(imgArray.Properties.KeepAspectRatio ? MF_CHECKED : MF_STRING, id++, GetText("Keep aspect ratio"));
  1563.             subMenu_IS.AppendMenuSeparator();
  1564.             funcs[id] = [_this.SetStretchProperties, 2];
  1565.             subMenu_IS.AppendMenuItem(imgArray.Properties.Fill ? MF_CHECKED : MF_STRING, id++, GetText("Fill panel"));
  1566.            
  1567.             var subMenu_FC = window.CreatePopupMenu();
  1568.             subMenus.push(subMenu_FC);
  1569.             subMenu_FC.AppendTo(baseMenu, MF_POPUP, GetText("Follow cursor"));
  1570.            
  1571.             funcs[id+1] = [_this.SetFollowCursorProperties, 1];
  1572.             subMenu_FC.AppendMenuItem(MF_STRING, id+1, GetText("Only when not playing"));
  1573.             funcs[id+2] = [_this.SetFollowCursorProperties, 2];
  1574.             subMenu_FC.AppendMenuItem(MF_STRING, id+2, GetText("Always"));
  1575.             funcs[id] = [_this.SetFollowCursorProperties, 0];
  1576.             subMenu_FC.AppendMenuItem(MF_STRING, id, GetText("Never"));
  1577.             subMenu_FC.CheckMenuRadioItem(id, id+2, _this.Properties.FollowCursor+id);
  1578.             id += 3;
  1579.            
  1580.             funcs[id] = [_this.ClearCache, null];
  1581.             baseMenu.AppendMenuItem(MF_STRING, id++, GetText("Clear cache"));
  1582.             baseMenu.AppendMenuSeparator();
  1583.             funcs[id] = [_this.ShowProperties, null];
  1584.             baseMenu.AppendMenuItem(MF_STRING, id++, GetText("WSH Cover properties")+"...");
  1585. /*            funcs[id] = [_this.ShowHelp, null];
  1586.             baseMenu.AppendMenuItem(MF_STRING, id++, GetText("Help")+"...");*/
  1587.         }
  1588.        
  1589.         this.Pop = function(x, y){
  1590.             if (!baseMenu) this.Build();
  1591.             var ret = baseMenu.TrackPopupMenu(x, y);
  1592.             if(ret)
  1593.                 funcs[ret] && funcs[ret][0].call(_this, funcs[ret][1]);
  1594.             this.Dispose();
  1595.         }
  1596.        
  1597.         this.Dispose = function(){
  1598.             baseMenu && baseMenu.Dispose();
  1599.             baseMenu = null;
  1600.             subMenus = [];
  1601.             funcs = {};
  1602.         }
  1603.     }();
  1604.    
  1605.     this.cycle = new function(prop) {
  1606.         var timer;
  1607.         var locked = prop.SingleImageMode;
  1608.         this.paused = window.GetProperty("Cycle.Paused", false);
  1609.        
  1610.         this.Active = function(){
  1611.             if(!locked && !this.paused && !timer)
  1612.                 timer = window.SetInterval(_this.OnTimer, prop.Period);
  1613.         }
  1614.        
  1615.         this.Stop = function(){
  1616.             timer && window.ClearInterval(timer);
  1617.             timer=null;
  1618.         }
  1619.        
  1620.         this.PauseOrResume = function(){
  1621.             if(this.paused){
  1622.                 this.paused = false;
  1623.                 window.SetProperty("Cycle.Paused", false);
  1624.                 this.Active();
  1625.             } else {
  1626.                 this.paused = true;
  1627.                 window.SetProperty("Cycle.Paused", true);
  1628.                 this.Stop();
  1629.             }
  1630.         }
  1631.        
  1632.         this.Lock = function(lock){
  1633.             locked = prop.SingleImageMode && lock;
  1634.             if(lock)
  1635.                 this.Stop();
  1636.         }
  1637.        
  1638.         this.Reset = function(){
  1639.             this.Stop();
  1640.             this.Active();
  1641.         }
  1642.     }(this.Properties.Cycle);
  1643.    
  1644.     this.OnTimer = function(){
  1645.         _this.Next();
  1646.     }
  1647.    
  1648.     // Menu functions --------------------------------------------
  1649.     this.SwitchCyclePause = function(){
  1650.         this.cycle.PauseOrResume();
  1651.     }
  1652.    
  1653.     this.SwitchCover = function(index){
  1654.         if (imgArray.length<=1 || index<0 || index>=imgArray.length || index==currentIndex) return;
  1655.         currentIndex = index;
  1656.         currentPathItem = imgArray.pathArray[currentIndex];
  1657.         this.cycle.Stop();
  1658.         imgArray.GetImage(currentIndex, NavFinished);
  1659.     }
  1660.    
  1661.     this.SwitchOthers = function(arg){
  1662.         if(!this.imgSwitchMenu) return;
  1663.         var others = this.imgSwitchMenu.matchResult[-1];
  1664.         if(!others.length) return;
  1665.         var index;
  1666.         switch(arg){
  1667.             case -2:
  1668.                 index = others[0];
  1669.                 break;
  1670.             case -1:
  1671.                 index = currentIndex<=others[0] ? others[others.length-1] : currentIndex-1;
  1672.                 break;
  1673.             case 1:
  1674.                 index = currentIndex>=others[others.length-1] ? others[0] : currentIndex+1;
  1675.                 if(index<others[0]) index = others[0];
  1676.                 break;
  1677.             case 2:
  1678.                 index = others[others.length-1];
  1679.                 break;
  1680.         }
  1681.         this.SwitchCover(index);
  1682.     }
  1683.    
  1684.     this.Next = function(){
  1685.         this.SwitchCover(currentIndex>=imgArray.length-1 ? 0 : currentIndex+1);
  1686.     }
  1687.    
  1688.     this.Prev = function(){
  1689.         this.SwitchCover(currentIndex<=0 ? imgArray.length-1 : currentIndex-1);
  1690.     }
  1691.            
  1692.     function NavFinished(img){
  1693.         currentImage = img;
  1694.         imgDisplay.ChangeImage(1, img, 1);
  1695.         SetMenuButtonCaption();
  1696.         _this.cycle.Reset();
  1697.     }
  1698.    
  1699.     function SetMenuButtonCaption(){
  1700.         if(!imgDisplay.menuButton) return;
  1701.         var caption;
  1702.         if(currentPathItem)
  1703.             caption = currentPathItem.artId==-1 ? GetText("Others") : GetText(AlbumArtId.GetName(currentPathItem.artId).capitalize());
  1704.         else
  1705.             caption = "";
  1706.         imgDisplay.menuButton.caption = GetText(caption);
  1707.     }
  1708.    
  1709.     this.AboutCurrentImage = function(){
  1710.         if(!currentPathItem) return;
  1711.         var str = [];
  1712.        
  1713.         str.push("-- " + GetText("Current picture infomation") + " --------------------\n");
  1714.        
  1715.         str.push("\n" + GetText("Match type") + ":\t");
  1716.         str.push(currentPathItem.artId==-1 ? GetText("Others") : GetText(AlbumArtId.GetName(currentPathItem.artId).capitalize()));
  1717.        
  1718.         str.push("\n" + GetText("File path ") + ":\t");
  1719.         if(currentPathItem.embed)
  1720.             str.push("(" + GetText("Embed") + ") ");
  1721.         str.push(currentPathItem.path);
  1722.        
  1723.         str.push("\n" + GetText("Resolution") + ":\t");
  1724.         str.push(imgArray.currentImageItem ? (imgArray.currentImageItem.srcW + "?" + imgArray.currentImageItem.srcH) : "Invalid");
  1725.        
  1726.         PopMessage(0, str.join(""), 0);
  1727.     }
  1728.    
  1729.     this.ManageAttachedImages = function(arr){
  1730.         if(!currentMetadb) return;
  1731.         switch(arr[0]){
  1732.             case 0:
  1733.                 fb.RunContextCommandWithMetadb(GetText("Edit attached pictures"), arr[1])
  1734.                 //|| fb.RunContextCommandWithMetadb("Edit attached pictures", arr[1]);
  1735.                 break;
  1736.             case 1:
  1737.                 fb.RunContextCommandWithMetadb(GetText("Batch attach pictures"), arr[1])
  1738.                 //|| fb.RunContextCommandWithMetadb("Batch attach pictures", arr[1]);
  1739.                 break;
  1740.             case 2:
  1741.                 fb.RunContextCommandWithMetadb(GetText("Remove all pictures"), arr[1])
  1742.                 //|| fb.RunContextCommandWithMetadb("Remove all pictures", arr[1]);
  1743.                 break;
  1744.         }
  1745.     }
  1746.    
  1747.     this.SearchFromWeb = function(arr){
  1748.         var url, si = this.Properties.SearchScriptPresets[arr[0]];
  1749.         if(si.GetUrl)
  1750.             url = si.GetUrl(arr[1]);
  1751.         else{
  1752.             var keyword = fb.TitleFormat(si.keyword).EvalWithMetadb(arr[1]);
  1753.             keyword = encodeURIComponent(keyword);
  1754.             url = si.url.replace(/%s/ig, keyword);
  1755.         }
  1756.         ShellExecute('"' + url + '"', "", "", "open", 1);
  1757.     }
  1758.    
  1759.     this.SetStretchProperties = function(switchWhichOne){
  1760.         var str = imgArray.Properties.Stretch;
  1761.         var kar = imgArray.Properties.KeepAspectRatio;
  1762.         var fill = imgArray.Properties.Fill;
  1763.         if(switchWhichOne==0){
  1764.             str = !str;
  1765.             imgArray.Properties.Stretch = str;
  1766.             window.SetProperty("Image.Stretch.Stretch", str);
  1767.         } else if (switchWhichOne==1) {
  1768.             kar = !kar;
  1769.             imgArray.Properties.KeepAspectRatio = kar;
  1770.             window.SetProperty("Image.Stretch.KeepAspectRatio", kar);
  1771.         } else if (switchWhichOne==2) {
  1772.             fill = !fill;
  1773.             imgArray.Properties.Fill = fill;
  1774.             window.SetProperty("Image.Stretch.Fill", fill);
  1775.         }
  1776.                
  1777.         if(!fill || switchWhichOne==2){
  1778.             imgArray.ClearCache();
  1779.             if(imgArray.length)
  1780.                 imgArray.GetImage(currentIndex, GetImageFinished);
  1781.         }
  1782.        
  1783.         function GetImageFinished(img){
  1784.             currentImage = img;
  1785.             imgDisplay.ChangeImage(1, img, 1);
  1786.         }
  1787.     }
  1788.    
  1789.     this.SetFollowCursorProperties = function(fc){
  1790.         this.Properties.FollowCursor = fc;
  1791.         window.SetProperty("Panel.FollowCursor", fc);
  1792.        
  1793.         isFollowingCursor = fc==2 || (fc==1 && !fb.IsPlaying);
  1794.         this.cycle.Lock(isFollowingCursor);
  1795.        
  1796.         if(isFollowingCursor)
  1797.             this.OnSelectionChanged(fb.GetSelection());
  1798.         else
  1799.             this.OnPlaybackNewTrack(fb.GetNowPlaying());
  1800.     }
  1801.    
  1802.     this.ViewWithExternalViewer = function(){
  1803.         if (!currentPathItem) return;
  1804.         if (currentPathItem.embed) {
  1805.             PopMessage(1, GetText("This is an embed image, it can't be opened in external viewer."), 48);
  1806.             return;
  1807.         }
  1808.         ShellExecute('"' + currentPathItem.path + '"', "", "", "open", 1);
  1809.     }
  1810.    
  1811.     this.OpenContainingFolder = function(){
  1812.         if (!currentPathItem) return;
  1813.         ShellExecute("explorer", '/select,\"' + currentPathItem.path + '"', "", "open", 1);
  1814.     }
  1815.    
  1816.     function ShellExecute(arg1, arg2, arg3, arg4, arg5){
  1817.         if(!shellObj){
  1818.             try{
  1819.                 shellObj= new ActiveXObject("Shell.Application");
  1820.             } catch(e) {
  1821.                 PopMessage(1, "Can not create ActiveX object (Shell.Application), command can't be execute. Please check your system authorities.", 16);
  1822.                 return;
  1823.             }
  1824.         }
  1825.         shellObj.ShellExecute(arg1, arg2, arg3, arg4, arg5);
  1826.     }
  1827.    
  1828.     this.ShowMatchLog = function(toPopup){
  1829.         Logger.Print(toPopup);
  1830.     }
  1831.    
  1832.     this.ClearCache = function(){
  1833.         imgArray.ClearCache();
  1834.     }
  1835.    
  1836.     this.ShowProperties = function(){
  1837.         window.ShowProperties();
  1838.     }
  1839.    
  1840.     /*this.ShowHelp = function(){
  1841.         var HelpFile = workPath + "\\WSH_Cover_Help.txt";
  1842.         if(utils.FileTest(HelpFile,"e"))
  1843.             PopMessage(0, utils.ReadTextFile(HelpFile), 2);
  1844.         else
  1845.             PopMessage(1, GetText("Can't find file") + ": \'" + HelpFile + "\'", 16);
  1846.     }*/
  1847.    
  1848.     this.OnRightClick = function(x, y){
  1849.         if(CoverDisplay.isXYIn(x, y) && this.funcMenu)
  1850.             this.funcMenu.Pop(x, y);
  1851.     }
  1852.    
  1853.     this.OnDoubleClick = function(x, y){
  1854.         if(CoverDisplay.isXYIn(x, y) && currentPathItem){
  1855.             if (currentPathItem.embed)
  1856.                 this.ManageAttachedImages([0, currentMetadb]);       // "Edit attached pictures"
  1857.             else
  1858.                 this.ViewWithExternalViewer();
  1859.         }
  1860.     }
  1861.    
  1862.     //-----------------------------------------------------------------
  1863.     this.OnSelectionChanged = function(metadb){
  1864.         if(metadb){
  1865.             if(isFollowingCursor && fb.GetSelectionType()<3)
  1866.                 OnNewTrack(metadb);
  1867.         } else
  1868.             OnStop();
  1869.     }
  1870.    
  1871.     this.OnPlaybackNewTrack = function(metadb){
  1872.         if(metadb){
  1873.             if(this.Properties.FollowCursor!=2){
  1874.                 isFollowingCursor = false;
  1875.                 this.cycle.Lock(false);
  1876.                 OnNewTrack(metadb);
  1877.             }
  1878.         } else
  1879.             OnStop();
  1880.     }
  1881.    
  1882.     this.OnPlaybackStop = function(reason){
  1883.         this.cycle.Stop();
  1884.         if(reason==2) {
  1885.             CollectGarbage();           // Release memory.
  1886.         } else if(this.Properties.FollowCursor!=0){
  1887.             isFollowingCursor = true;
  1888.             this.cycle.Lock(true);
  1889.             this.OnSelectionChanged(fb.GetSelection());
  1890.         } else
  1891.             OnStop();
  1892.     }
  1893.    
  1894.     //------------------------------------------
  1895.     var isNewgroup;
  1896.     function OnNewTrack(metadb){
  1897.         if (metadb && currentMetadb && currentMetadb.Compare(metadb))
  1898.             _this.SwitchCover(0);
  1899.         else {
  1900.             isNewgroup = false;
  1901.             currentIndex = -1;
  1902.             currentMetadb = metadb;
  1903.             imgArray.Update(currentMetadb, OnNewTrack_UpdateFinished);
  1904.         }
  1905.     }
  1906.    
  1907.     function OnNewTrack_UpdateFinished(){
  1908.         isNewgroup = groupString != (groupString = fb.TitleFormat(_this.Properties.GroupFormat).EvalWithMetadb(currentMetadb));
  1909.         if (imgArray.length){
  1910.             currentIndex = 0;
  1911.             if(currentPathItem && imgArray.pathArray[0].path==currentPathItem.path && (!currentPathItem.embed || imgArray.pathArray[0].artId==currentPathItem.artId)){
  1912.                 currentPathItem = imgArray.pathArray[0];
  1913.                 if(isNewgroup)
  1914.                     imgDisplay.ChangeImage(1, currentImage, 2);
  1915.                 if(imgArray.length>1) _this.cycle.Active();
  1916.             } else {
  1917.                 currentPathItem = imgArray.pathArray[0];
  1918.                 imgArray.GetImage(currentIndex, OnNewTrack_GetImageFinished);
  1919.             }
  1920.         } else if (currentPathItem!=null) {
  1921.             currentPathItem = null;
  1922.             currentImage = null;
  1923.             imgDisplay.ChangeImage(1, currentImage, isNewgroup?2:1);
  1924.         } else {
  1925.             //isNewgroup && imgDisplay.ChangeImage(1, currentImage, 2);
  1926.         }
  1927.         SetMenuButtonCaption();
  1928.     }
  1929.    
  1930.     function OnNewTrack_GetImageFinished(img){
  1931.         currentImage = img;
  1932.         imgDisplay.ChangeImage(1, currentImage, isNewgroup?2:1);
  1933.         if(imgArray.length>1) _this.cycle.Active();
  1934.     }
  1935.    
  1936.     //------------------------------------------
  1937.     function OnStop(){
  1938.         imgArray.Update();
  1939.         if(currentPathItem)
  1940.             imgDisplay.ChangeImage(1, null, 2);
  1941.         currentMetadb = null;
  1942.         groupString = null;
  1943.         currentPathItem = null;
  1944.         currentImage = null;
  1945.         SetMenuButtonCaption();
  1946.         _this.cycle.Lock(true);
  1947.         window.Repaint();
  1948.         CollectGarbage();           // Release memory.
  1949.     }
  1950.    
  1951.     //------------------------------------------
  1952.     var refresh_ignoreCache;
  1953.     this.Refresh = function(ignoreCache, metadb){
  1954.         refresh_ignoreCache = ignoreCache;
  1955.         if(!metadb)
  1956.             metadb = isFollowingCursor ? fb.GetSelection() : fb.GetNowPlaying();
  1957.         if(metadb){
  1958.             currentMetadb = metadb;
  1959.             groupString = fb.TitleFormat(_this.Properties.GroupFormat).EvalWithMetadb(metadb);
  1960.             imgArray.Update(metadb, Refresh_UpdateFinished, ignoreCache);
  1961.         } else {
  1962.             groupString = "";
  1963.             Refresh_UpdateFinished();
  1964.         }
  1965.     }
  1966.    
  1967.     function Refresh_UpdateFinished(){
  1968.         if (imgArray.length){
  1969.             if(currentIndex==-1 || currentIndex>=imgArray.length)
  1970.                 currentIndex = 0;
  1971.             currentPathItem = imgArray.pathArray[currentIndex];
  1972.             imgArray.GetImage(currentIndex ,Refresh_GetImageFinished, refresh_ignoreCache);
  1973.         } else
  1974.             Refresh_GetImageFinished(null);
  1975.         SetMenuButtonCaption();
  1976.     }
  1977.    
  1978.     function Refresh_GetImageFinished(img){
  1979.         currentImage = img;
  1980.         if (currentMetadb) {
  1981.             imgDisplay.ChangeImage(1, currentImage, 1);
  1982.             window.Repaint();
  1983.         }
  1984.         if(imgArray.length>1) _this.cycle.Active();
  1985.     }
  1986.    
  1987.     //------------------------------------------
  1988.     this.Resize = function(w, h){
  1989.         if(w==0 || h==0) return;
  1990.         imgDisplay.Resize(w, h);
  1991.         imgArray.Resize(imgDisplay.ImageRange.w, imgDisplay.ImageRange.h);
  1992.        
  1993.         if (imgArray.length)
  1994.             imgArray.GetImage(currentIndex ,Resize_GetImageFinished);
  1995.         else
  1996.             Resize_GetImageFinished(null);
  1997.     }
  1998.    
  1999.     function Resize_GetImageFinished(img){
  2000.         currentImage = img;
  2001.         imgDisplay.Refresh(1, currentImage);
  2002.         window.Repaint();
  2003.     }
  2004.    
  2005.     //------------------------------------------
  2006.     this.Init = function(){
  2007.         if(imgDisplay.menuButton && this.imgSwitchMenu)
  2008.             imgDisplay.menuButton.menu = this.imgSwitchMenu;
  2009.        
  2010.         isFollowingCursor = this.Properties.FollowCursor==2 || (this.Properties.FollowCursor==1 && !fb.IsPlaying);
  2011.         this.cycle.Lock(isFollowingCursor);
  2012.        
  2013.         metadb = isFollowingCursor ? fb.GetSelection() : fb.GetNowPlaying();
  2014.         if(metadb){
  2015.             currentMetadb = metadb;
  2016.             groupString = fb.TitleFormat(this.Properties.GroupFormat).EvalWithMetadb(metadb);
  2017.             imgArray.Update(metadb, Init_UpdateFinished);
  2018.         } else {
  2019.             groupString = "";
  2020.             Init_UpdateFinished();
  2021.         }
  2022.     }
  2023.    
  2024.     function Init_UpdateFinished(){
  2025.         //this.ShowMatchLog();
  2026.         if (imgArray.length){
  2027.             currentIndex = currentIndex==-1 ? 0 : currentIndex;
  2028.             currentPathItem = imgArray.pathArray[currentIndex];
  2029.             imgArray.GetImage(currentIndex ,Init_GetImageFinished);
  2030.         } else
  2031.             Init_GetImageFinished(null);
  2032.         SetMenuButtonCaption();
  2033.     }
  2034.    
  2035.     function Init_GetImageFinished(img){
  2036.         currentImage = img;
  2037.         imgDisplay.Refresh(1, currentImage);
  2038.         if(imgArray.length>1) _this.cycle.Active();
  2039.     }
  2040.    
  2041.     this.Init();
  2042. }
  2043.  
  2044.  
  2045. // ===================================================
  2046. var margin = {left:0, top:4, right:0, bottom:0};
  2047.  
  2048. var themePath = fb.ProfilePath + "themes\\strigUI";
  2049. //------------------------------------------------
  2050. var VBE;
  2051. try{
  2052.     VBE= new ActiveXObject("ScriptControl");
  2053.     VBE.Language = "VBScript";
  2054. } catch(e) {
  2055.     PopMessage(0, "Can not create ActiveX object (ScriptControl), some functions are not available.\nPlease check your system authorities.", 1);
  2056. }
  2057. function PopMessage(method, text, type){
  2058.     if(method==1){
  2059.         if(VBE){
  2060.             var s = VBE.eval("MsgBox(\"" + text + "\", " + type + ", \"WSH Cover Panel\")");
  2061.             return s;
  2062.         } else
  2063.             type = type==32 ? 2 : (type==16 ? 1 :0);
  2064.     }
  2065.     fb.ShowPopupMessage(text, "WSH Cover Panel", type);
  2066. }
  2067. //------------------------------------------------
  2068. var Properties = new function(){
  2069.     //------------------------------------------------------
  2070.     this.Panel = {
  2071.         WorkDirectory: fb.TitleFormat(window.GetProperty("Panel.WorkDirectory", "")).Eval(true),
  2072.         Lang: window.GetProperty("Panel.Language", "auto").toLowerCase()            // Language
  2073.     }
  2074.    
  2075.     this.Panel.BackgroundColor = RGBA(255,255,255, 255);
  2076.    
  2077.     if(this.Panel.WorkDirectory)
  2078.         this.PanelWorkDirectory = fb.ProfilePath + this.PanelWorkDirectory;
  2079.     else
  2080.         this.Panel.WorkDirectory = themePath + "\\WSH Cover";
  2081.    
  2082.     if (!utils.FileTest(this.Panel.WorkDirectory,"e"))
  2083.         PopMessage(1, "Invalid work directory.", 16);
  2084.    
  2085.     //------------------------------------------------------
  2086.     this.Controller = {
  2087.         GroupFormat: window.GetProperty("Image.GroupFormat", "%album artist%|%album%"),
  2088.         // 0: Never, 1: When not playing, 2: Always.
  2089.         FollowCursor: window.GetProperty("Panel.FollowCursor", 1),
  2090.         Cycle: {
  2091.             SingleImageMode: window.GetProperty("Cycle.SingleImageMode", false),
  2092.             Period: window.GetProperty("Cycle.Period", 10000)
  2093.         }
  2094.     }
  2095.    
  2096.     if (typeof(this.Controller.FollowCursor)!="number")
  2097.         this.Controller.FollowCursor = 1;
  2098.     else if (this.Controller.FollowCursor<0)
  2099.         this.Controller.FollowCursor = 0;
  2100.     else if (this.Controller.FollowCursor>2)
  2101.         this.Controller.FollowCursor = 2;
  2102.     window.SetProperty("Panel.FollowCursor", this.Controller.FollowCursor);
  2103.    
  2104.     if (typeof(this.Controller.Cycle.Period)!="number")
  2105.         this.Controller.Cycle.Period = 15000;
  2106.     else if (this.Controller.Cycle.Period<100)
  2107.         this.Controller.Cycle.Period = 100;
  2108.     window.SetProperty("Cycle.Period", this.Controller.Cycle.Period);
  2109.    
  2110.     //------------------------------------------------------
  2111.     this.GetFont = function(){
  2112.         var font = window.GetFontDUI(0);
  2113.         var fs = font ? font.Style : 0;
  2114.         var fn = fb.TitleFormat(window.GetProperty("MenuButton.Font", "")).Eval(true);        
  2115.         //font.Dispose();
  2116.         return gdi.Font(fn, 14, fs);
  2117.     }
  2118.    
  2119.     this.Display = {
  2120.         ShowCoverCase: window.GetProperty("Panel.ShowCoverCase", true),
  2121.         Animation: {
  2122.             Enable: window.GetProperty("Cycle.Animation.Enable", true),
  2123.             RefreshInterval: window.GetProperty("Cycle.Animation.RefreshInterval", 50),
  2124.             Duration: window.GetProperty("Cycle.Animation.Duration", 500)
  2125.         },
  2126.         MenuButton: {
  2127.             Show: !this.Controller.Cycle.SingleImageMode && window.GetProperty("MenuButton.Show", true),
  2128.             Font: this.GetFont(),
  2129.             MacTypeSupport: window.GetProperty("MenuButton.Font.MacTypeSupport", false),
  2130.             CaptionColor : RGBA(255,255,255,255)
  2131.         }
  2132.     }
  2133.    
  2134.     if (typeof(this.Display.Animation.RefreshInterval)!="number")
  2135.         this.Display.Animation.RefreshInterval = 50;
  2136.     else if (this.Display.Animation.RefreshInterval<10)
  2137.         this.Display.Animation.RefreshInterval = 10;
  2138.     window.SetProperty("Cycle.Animation.RefreshInterval", this.Display.Animation.RefreshInterval);
  2139.    
  2140.     if (typeof(this.Display.Animation.Duration)!="number")
  2141.         this.Display.Animation.Duration = 300;
  2142.     else if (this.Display.Animation.Duration<10)
  2143.         this.Display.Animation.Duration = 10;
  2144.     window.SetProperty("Cycle.Animation.Duration", this.Display.Animation.Duration);
  2145.    
  2146.     //------------------------------------------------------
  2147.     var defaultAdditionalPathFormat = '$directory_path(%path%)\\*.jpg';
  2148.     this.Image = {
  2149.         BuildinPathFormat: '<front>||<back>||<disc>||<artist>||<icon>',
  2150.         AdditionalPathFormat: window.GetProperty('Image.AdditionalPath.Format', defaultAdditionalPathFormat),
  2151.         TreatAdditionalPathAsBackup: window.GetProperty('Image.AdditionalPath.TreatAsBackup', false),
  2152.         SupportedTypes: ['jpg', 'png', 'gif', 'bmp', 'jpeg'],
  2153.         SingleImageMode: this.Controller.Cycle.SingleImageMode,
  2154.         CycleInWildCard: window.GetProperty("Cycle.CycleInWildCard", true),
  2155.         MaxFileSize: window.GetProperty("Image.AdditionalPath.MaxFileSize", 2097152),
  2156.         AtMostLoadImagesNumber: window.GetProperty("Image.AtMostLoadImagesNumber", 10),
  2157.         KeepAspectRatio: window.GetProperty("Image.Stretch.KeepAspectRatio", true),
  2158.         Stretch: window.GetProperty("Image.Stretch.Stretch", false),
  2159.         Fill: window.GetProperty("Image.Stretch.Fill", false),
  2160.         InterpolationMode: window.GetProperty("Image.Stretch.InterpolationMode", 2),
  2161.         ImagesCacheCapacity: window.GetProperty("Image.CacheCapacity.Images", 10),
  2162.         PathsCacheCapacity: window.GetProperty("Image.CacheCapacity.Paths", 30)
  2163.     }
  2164.    
  2165.     //this.Image.BuildinPathFormat = this.Image.BuildinPathFormat.replace(/%foobar_path%/ig, fb.ProfilePath.slice(0,-1));
  2166.     this.Image.AdditionalPathFormat = this.Image.AdditionalPathFormat.replace(/%foobar_path%/ig, fb.ProfilePath.slice(0,-1));      // Process %foobar_path% field.
  2167.     this.Image.PathFormatGroup = [this.Image.BuildinPathFormat, this.Image.AdditionalPathFormat];
  2168.    
  2169.     if (typeof(this.Image.MaxFileSize)!="number")
  2170.         this.Image.MaxFileSize = 2097152;
  2171.     else if (this.Image.MaxFileSize<0)
  2172.         this.Image.MaxFileSize = 0;
  2173.     window.SetProperty("Image.AdditionalPath.MaxFileSize", this.Image.MaxFileSize);
  2174.        
  2175.     if (typeof(this.Image.AtMostLoadImagesNumber)!="number")
  2176.         this.Image.AtMostLoadImagesNumber = 10;
  2177.     else if (this.Image.AtMostLoadImagesNumber<0)
  2178.         this.Image.AtMostLoadImagesNumber = 0;
  2179.     window.SetProperty("Image.AtMostLoadImagesNumber", this.Image.AtMostLoadImagesNumber);
  2180.    
  2181.     if (typeof(this.Image.ImagesCacheCapacity)!="number")
  2182.         this.Image.ImagesCacheCapacity = 10;
  2183.     else if (this.Image.ImagesCacheCapacity<0)
  2184.         this.Image.ImagesCacheCapacity = 0;
  2185.     window.SetProperty("Image.CacheCapacity.Images", this.Image.ImagesCacheCapacity);
  2186.    
  2187.     if (typeof(this.Image.PathsCacheCapacity)!="number")
  2188.         this.Image.PathsCacheCapacity = 10;
  2189.     else if (this.Image.PathsCacheCapacity<0)
  2190.         this.Image.PathsCacheCapacity = 0;
  2191.     window.SetProperty("Image.CacheCapacity.Paths", this.Image.PathsCacheCapacity);
  2192.    
  2193. }();
  2194.  
  2195.  
  2196. //===================================================
  2197. var ww, wh;
  2198. var imgPath, workPath = Properties.Panel.WorkDirectory;
  2199. var imgPath = themePath + "\\images";
  2200.  
  2201. //---------------------------------------
  2202. function SearchItem(name, url, keyword, func){
  2203.     this.name = name;
  2204.     this.url = url;
  2205.     this.keyword = keyword;
  2206.     this.GetUrl = func;
  2207. }
  2208.  
  2209. var SearchItems = [];
  2210. var searchScripts = utils.ReadTextFile(workPath+"\\online_search_scripts.js");
  2211. if(searchScripts)
  2212.     eval(searchScripts);
  2213.  
  2214. for(var i=0; i<SearchItems.length; i++){
  2215.     if(!SearchItems[i] instanceof SearchItem){
  2216.         SearchItems.splice(i, 1);
  2217.         i--;
  2218.     }
  2219. }
  2220. Properties.Controller.SearchScriptPresets = SearchItems;
  2221.  
  2222. //---------------------------------------
  2223. var LangPack = {};
  2224. if (Properties.Panel.Lang=="auto") {
  2225.     var uf = fb.TitleFormat("$meta()").Eval(true);
  2226.     var langfiles = utils.Glob(workPath+"\\*.lang").toArray();
  2227.     for (var i in langfiles) {
  2228.         eval(utils.ReadTextFile(langfiles[i]));
  2229.         if (uf=="["+GetText("UNKNOWN FUNCTION")+"]") {
  2230.             Properties.Panel.Lang = utils.FileTest(langfiles[i],"split").toArray()[1];
  2231.             break;
  2232.         } else
  2233.             LangPack = {};
  2234.     }
  2235.     if (Properties.Panel.Lang=="auto") {
  2236.         Properties.Panel.Lang = "en";
  2237.         if (uf!="[UNKNOWN FUNCTION]")
  2238.             fb.trace("WSH Cover: Can't find matched language file, use english instead.");
  2239.     }
  2240. } else {
  2241.     var langtxt = utils.ReadTextFile(workPath+"\\"+Properties.Panel.Lang+".lang");
  2242.     if (langtxt) {
  2243.         eval(langtxt);
  2244.         if (fb.TitleFormat("$meta()").Eval(true)!="["+GetText("UNKNOWN FUNCTION")+"]") {
  2245.             Properties.Panel.Lang = "en";
  2246.             LangPack = {};
  2247.             fb.trace("WSH Cover: Specified language file dosen't match, use english instead.");
  2248.         }
  2249.     } else {
  2250.         Properties.Panel.Lang = "en";
  2251.         fb.trace("WSH Cover: Can't load language file, use english instead.");
  2252.     }
  2253. }
  2254.  
  2255. function GetText(name){
  2256.     //fb.trace(name)      // Uncomment this, you can know how many sentencies you need to translate.
  2257.     var str = LangPack[name];
  2258.     if(!str) str = name;
  2259.     return str;
  2260. }
  2261.  
  2262. //====================================
  2263. var Covers = new ImagesArray(Properties.Image);
  2264. var CoverDisplay = new Display(margin.left, margin.top, 0, 0, Properties.Display);
  2265. var MainController = new Controller(Covers, CoverDisplay, Properties.Controller);
  2266.  
  2267. // START ==============================================
  2268.  
  2269. function on_paint(gr) {
  2270.     if(!ww || !wh) return;
  2271.      //if(Properties.Panel.BackgroundColor)
  2272.         //gr.FillSolidRect(0, 0, ww, wh, Properties.Panel.BackgroundColor);
  2273.         gr.FillSolidRect(0, 0, ww, wh, RGBA(240,240,240,255));
  2274.        
  2275.         //gr.FillSolidRect(0, 1, ww, 1, RGBA(255,255,255,255));
  2276.         //gr.FillSolidRect(0, 0, ww, 1, RGBA(150,150,150,255));
  2277.     CoverDisplay.Draw(gr);
  2278. }
  2279.  
  2280. function on_size() {
  2281.     if (!window.Width || !window.Height) return;
  2282.     ww = window.Width;
  2283.     wh = window.Height;
  2284.     MainController.Resize(Math.max(ww, 91)-margin.left-margin.right, Math.max(wh, 94)-margin.top-margin.bottom);
  2285. }
  2286.  
  2287. function on_selection_changed(metadb){
  2288.     MainController.OnSelectionChanged(metadb);
  2289. }
  2290.  
  2291. function on_playback_new_track(metadb){
  2292.     MainController.OnPlaybackNewTrack(metadb);
  2293. }
  2294.  
  2295. function on_playback_stop(reason) {
  2296.     MainController.OnPlaybackStop(reason);
  2297. }
  2298.  
  2299. var cursorX, cursorY;
  2300. function on_mouse_move(x, y) {
  2301.     cursorX = x, cursorY = y;
  2302.     CoverDisplay.OnMouseMove && CoverDisplay.OnMouseMove(x, y);
  2303.    
  2304. }
  2305.  
  2306. function on_mouse_lbtn_down(x, y) {
  2307.     CoverDisplay.OnMouseDown && CoverDisplay.OnMouseDown(x, y);
  2308. }
  2309.  
  2310. function on_mouse_lbtn_up(x, y) {
  2311.     CoverDisplay.OnMouseUp && CoverDisplay.OnMouseUp(x, y);
  2312. }
  2313.  
  2314. function on_mouse_leave() {
  2315.     CoverDisplay.OnMouseLeave && CoverDisplay.OnMouseLeave();
  2316. }
  2317.  
  2318. function on_mouse_lbtn_dblclk(x, y, mask) {
  2319.     MainController.OnDoubleClick(x, y, mask);
  2320. }
  2321.  
  2322. function on_mouse_wheel(delta) {
  2323.     if(!CoverDisplay.isXYIn(cursorX, cursorY)) return;
  2324.     if (delta>0)
  2325.         MainController.Prev();
  2326.     else
  2327.         MainController.Next();
  2328. }
  2329.  
  2330. function on_metadb_changed(metadb, fromhook) {
  2331.     MainController.Refresh(true, metadb);
  2332. }
  2333.  
  2334. function on_get_album_art_done(metadb, art_id, image, image_path) {
  2335.     Covers.OnGetAlbumArtDone(metadb, art_id, image, image_path);
  2336. }
  2337.  
  2338. function on_load_image_done(cookie, image) {
  2339.     Covers.OnLoadImageDone(cookie, image)
  2340. }
  2341.  
  2342. var rbtnDown;
  2343. function on_mouse_rbtn_down(x, y, vkey){
  2344.     rbtnDown = vkey==6;
  2345. }
  2346.  
  2347. function on_mouse_rbtn_up(x, y, vkey){
  2348.     if (rbtnDown) {
  2349.         rbtnDown=false;
  2350.         return vkey!=4;
  2351.     } else {
  2352.         MainController.OnRightClick(x, y, vkey);
  2353.         return true;            // Disable default right click menu.
  2354.     }
  2355. }
  2356.  
  2357. function on_font_changed() {
  2358.     var oldfont = Properties.Display.MenuButton.Font;
  2359.     var newfont = Properties.GetFont();
  2360.     if(oldfont && oldfont.Name==newfont.Name && oldfont.Style==newfont.Style)
  2361.         return;
  2362.     oldfont && oldfont.Dispose();
  2363.     Properties.Display.MenuButton.Font = newfont;
  2364.     CoverDisplay.menuButton.ClearCache();
  2365.     //window.Repaint();
  2366. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement