WaBis

walter.bislins.ch

Datei: Zoom: ic.js

Inhalt der Datei: javascript/zoom/src/ic.js
// Copyright Januar 2005, Walter Bislin, walter.bislins.ch
//
// 12.10.2014
//  minor changes in code, no semantic changes
// 29.04.2014:
//  upd: global constants moved to object -> IC.CState
//  err: MaxNLoading, new default = 2 (old = 1) for better performance on preload on fast connections
//  new: GetStatus(), ResetStatus()
//  upd: catch exceptions in CallLoadedFunc()
//  upd: callbacks are called even on error and abort events (check IC.Image(id).CacheState)

// class CImgCache --------------------------------------------------------------------------------------

function CImgCache() {
  // public
  this.CheckLoadInterval  = 100; // ms
  this.MaxNLoading = 2;
  this.LoadDelay = 0;  // for debugging
  this.EnableStatusDisplay = true;
  // public read only
  this.NImages = 0;
  this.NLoading = 0;
  this.NUnloaded = 0;
  this.NError = 0;
  this.NAbort = 0;
  this.NLoaded = 0;
  this.Images = [];
  // constants
  this.CState = {
    LoadPending: 0,
    Loading:     1,
    Loaded:      2,
    Error:       3,
    Abort:       4
  };
  // private
  this.ErrorMsg = '';
  this.OnAllLoaded = new xCallbackChain();
  this.OnImgLoaded = new xCallbackChain();
  this.OnLoadCalling = false;
  this.LoadNextCalling = false;
  this.PrioList = [];
  this.Timer = null;
  var me = this;
  this.OnCheckLoaded = function() { me.CheckLoaded(); };
}

CImgCache.prototype.AddOnAllLoaded = function(aFunc) { this.OnAllLoaded.Add(aFunc); };
CImgCache.prototype.AddOnImgLoaded = function(aFunc) { this.OnImgLoaded.Add(aFunc); };

CImgCache.prototype.IsValid = function(aImageID) {
  return ((aImageID >= 0) && (aImageID < this.NImages));
};

CImgCache.prototype.PreloadImages = function( aUrls, aRoot )
// aUrls: array of string; aRoot: string
{
  aRoot = xDefStr( aRoot, '' );
  var len = aUrls.length;
  for (var i = 0; i < len; i++) {
    this.PreloadImage( aRoot+aUrls[i] );
  }
};

CImgCache.prototype.PreloadImage = function( aUrl, aOnLoadFunc, bPriority )
// load an image with higher priority if bPriority = true
// returns an ImageID.
{
  bPriority = xDefBool( bPriority, false );
  var id = this.FindImage(aUrl);
  if (id >= 0) {
    var img = this.Images[id];
    aOnLoadFunc = xDefFuncOrNull( aOnLoadFunc, img.OnLoadFunc );
    if (img.CacheState == this.CState.Error || img.CacheState == this.CState.Abort) {
      this.ReloadImage( id, aOnLoadFunc );
    } else {
      img.OnLoadFunc = aOnLoadFunc;
    }
  } else {
    aOnLoadFunc = xDefFuncOrNull( aOnLoadFunc, null );
    id = this.AddImage( aUrl, aOnLoadFunc );
  }
  if (bPriority) {
    if (this.Images[id].CacheState == this.CState.LoadPending && !this.InPrioList(id)) {
      this.PrioList[this.PrioList.length] = id;
    }
  }
  this.LoadNext();
  return id;
};

CImgCache.prototype.LoadImage = function( aUrl, aOnLoadFunc )
// load an image with higher priority
// returns an ImageID.
{
  return this.PreloadImage( aUrl, aOnLoadFunc, true );
};

CImgCache.prototype.ReloadImage = function( aImgID, aOnLoadFunc )
{
  var img = this.Images[aImgID];
  img.OnLoadFunc = xDefFuncOrNull( aOnLoadFunc, null );
  if (img.CacheState != this.CState.LoadPending) {
    img.CacheState = this.CState.LoadPending;
    this.NUnloaded++;
    this.DisplayStatus();
  }
};

CImgCache.prototype.FindImage = function( aUrl ) {
  var images = this.Images;
  var len = this.NImages;
  for (var i = 0; i < len; i++) {
    if (images[i].CacheUrl == aUrl) return i;
  }
  return -1;
};

CImgCache.prototype.Image = function(aImageID) {
  // require IsValid(aImageID)
  return this.Images[aImageID];
};

CImgCache.prototype.ImageByUrl = function(aUrl) {
  var imgID = this.FindImage(aUrl);
  return (imgID >= 0) ? this.Image(imgID) : null;
};

CImgCache.prototype.GetNUnloaded = function() {
  this.CheckLoaded();
  return this.NUnloaded;
};

CImgCache.prototype.IsLoaded = function(aImageID) {
  return (this.IsValid(aImageID) && (this.Images[aImageID].CacheState == this.CState.Loaded));
};

CImgCache.prototype.IsLoadedByUrl = function(aUrl) {
  // Sucht das Bild aUrl im Cache und testet, ob es ganz geladen ist.
  return this.IsLoaded(this.FindImage(aUrl));
};

CImgCache.prototype.ImageState = function(aImageID) {
  // require IsValid(aImageID)
  return (this.Images[aImageID].CacheState);
};

CImgCache.prototype.ImageStateByUrl = function(aUrl) {
  var imgID = this.FindImage(aUrl);
  return (imgID >= 0) ? this.ImageState(imgID) : -1;
};

CImgCache.prototype.ImageUrl = function(aImageID) {
  // require IsValid(aImageID)
  return this.Image(aImageID).CacheUrl;
};

CImgCache.prototype.GetStatus = function()
{
  var s = '';
  if (this.NUnloaded > 0 || this.NError > 0 || this.NAbort > 0) {
    s += 'Bilder zu laden: noch ' + this.NUnloaded + ' von ' + this.NImages + '. ';
    if (this.NError > 0 || this.NAbort > 0) {
      s += '(Geladen: ' + this.NLoaded + '; ';
      s += 'Fehler: ' + this.NError + '; ';
      s += 'Abbruch: ' + this.NAbort + ')';
    }
    // s += this.ErrorMsg;
  }
  return s;
};

CImgCache.prototype.ResetStatus = function() {
  this.ErrorMsg = '';
  this.NError = 0;
  this.NAbort = 0;
};

// private:

CImgCache.prototype.DisplayStatus = function()
{
  if (this.EnableStatusDisplay) {
    window.status = this.GetStatus();
  }
};

CImgCache.prototype.AddImage = function( aUrl, aOnLoadFunc )
{
  var id = this.NImages;
  var img = new Image();
  img.CacheUrl = aUrl;
  img.CacheState = this.CState.LoadPending;
  img.OnLoadFunc = aOnLoadFunc;
  img.WasLoaded = false;
  img.WasError = false;
  img.WasAbort = false;
  img.onload = function() { this.WasLoaded = true; };
  img.onerror = function() { this.WasError = true; };
  img.onabort = function() { this.WasAbrort = true; };
  this.Images[id] = img;
  this.NUnloaded++;
  this.NImages++;
  this.DisplayStatus();
  return id;
};

CImgCache.prototype.InPrioList = function( aImageID ) {
  var pl = this.PrioList;
  var len = pl.length;
  for (var i = 0; i < len; i++) {
    if (pl[i] == aImageID) return true;
  }
  return false;
};

CImgCache.prototype.LoadNext = function()
{
  if (this.NUnloaded == 0 || this.LoadNextCalling) return;
  this.LoadNextCalling = true;
  while ((this.NUnloaded > 0) && (this.PrioList.length > 0) && (this.NLoading < this.MaxNLoading)) {
    var id = this.PrioList.shift();
    this.StartLoading(id);
  }
  var found = true;
  while ((this.NUnloaded > 0) && found && (this.NLoading < this.MaxNLoading)) {
    var id = this.FindLoadPending();
    if (id == -1) {
      found = false;
    } else {
      this.StartLoading(id);
    }
  }
  this.LoadNextCalling = false;
};

CImgCache.prototype.FindLoadPending = function(){
  var images = this.Images;
  var len = images.length;
  for (var id = 0; id < len; id++) {
    if (images[id].CacheState == this.CState.LoadPending) return id;
  }
  return -1;
};

CImgCache.prototype.StartLoading = function( aImageID ) {
  // require IsValid(aImageID)
  if (this.Timer) {
    clearTimeout( this.Timer );
    this.Timer = null;
  }
  var img = this.Images[aImageID];
  if (img.CacheState == this.CState.LoadPending || img.CacheState == this.CState.Abort) {
    this.NLoading++;
    this.DisplayStatus();
    img.CacheState = this.CState.Loading;
    if (this.LoadDelay > 0) {
      setTimeout( function(){ img.src = img.CacheUrl; }, this.LoadDelay );
    } else {
      img.src = img.CacheUrl;
    }
  }
  // only start timer if not loading completed in statement obove!!!
  if (this.NLoading > 0 && this.Timer == null) {
    this.Timer = setTimeout( this.OnCheckLoaded, this.CheckLoadInterval );
  }
};

CImgCache.prototype.CheckLoaded = function()
{
  if (this.Timer) {
    clearTimeout( this.Timer );
    this.Timer = null;
  }
  for (var id = 0; id < this.NImages; id++) {
    var img = this.Images[id];
    if (img.CacheState == this.CState.Loading) {
      if (img.complete || img.WasLoaded) {
        this.OnLoad( id ); // may call LoadNext and set the timer!!!
      }
      if (img.WasError) {
        this.OnError( id );
      }
      if (img.WasAbort) {
        this.OnAbort( id );
      }
    }
  }
  if (this.NLoading > 0 && this.Timer == null) {
    this.Timer = setTimeout( this.OnCheckLoaded, this.CheckLoadInterval );
  }
};

CImgCache.prototype.OnImage = function( aImageID )
{
  this.NLoading--;
  this.NUnloaded--;
  this.DisplayStatus();
  this.CallLoadedFunc( aImageID );
  this.OnImgLoaded.Call( aImageID );
  if (this.NUnloaded == 0) {
    this.OnAllLoaded.Call();
  } else {
    this.LoadNext();
  }
};

CImgCache.prototype.OnLoad = function( aImageID )
{
  if (this.Images[aImageID].CacheState != this.CState.Loading) return;
  this.NLoaded++;
  this.DisplayStatus();
  this.Images[aImageID].CacheState = this.CState.Loaded;
  this.OnImage( aImageID );
};

CImgCache.prototype.OnError = function( aImageID )
{
  if (this.Images[aImageID].CacheState == this.CState.Loaded) {
    // some browsers (safari) are calling OnLoad and OnError! -> reset State to Loading
    this.Images[aImageID].CacheState = this.CState.Loading;
    this.NLoaded--;
    this.NLoading++;
    this.NUnloaded++;
  }

  if (this.Images[aImageID].CacheState != this.CState.Loading) return;
  this.NError++;
  this.ErrorMsg += ' Error loading ' + this.Images[aImageID].src;
  this.DisplayStatus();
  this.Images[aImageID].CacheState = this.CState.Error;
  this.OnImage( aImageID );
};

CImgCache.prototype.OnAbort = function( aImageID )
{
  if (this.Images[aImageID].CacheState == this.CState.Loaded) {
    // some browsers (safari) are calling OnLoad and OnAbort! -> reset State to Loading
    this.Images[aImageID].CacheState = this.CState.Loading;
    this.NLoaded--;
    this.NLoading++;
    this.NUnloaded++;
  }

  if (this.Images[aImageID].CacheState != this.CState.Loading) return;
  this.NAbort++;
  this.DisplayStatus();
  this.Images[aImageID].CacheState = this.CState.Abort;
  this.OnImage( aImageID );
};

CImgCache.prototype.CallLoadedFunc = function( aImageID ) {
  // prevents recursion!
  var img = this.Images[aImageID];
  if (this.OnLoadCalling || !img.OnLoadFunc) return;
  this.OnLoadCalling = true;
  try {
    img.OnLoadFunc( aImageID );
  } catch(e) { }
  this.OnLoadCalling = false;
};

var IC = new CImgCache();


More Page Infos / Sitemap
Created Dienstag, 17. Juni 2014
Scroll to Top of Page
Changed Montag, 12. Oktober 2015