
log = typeof log !== 'undefined' ? log : function(){}; // disable debug log unless predefined

viipg = (function(viipg,window,document) {
  // page runtime environment
  var $; // set jQuery when initializing 
  var $video_div;
  var $slide_div;
  var bundle;
  var syncs;

  function waitList(cbPrior,name) { // a list of items to be 'pop'ed before callback is executed
    var blockers = {};
    var callbacks = $.isArray(cbPrior) ? cbPrior : [];
    var api = function(cb) {
      //log('cb', name, typeof cb, $.isEmptyObject(blockers));
      if ($.isEmptyObject(blockers)) viipg.later(cb);
      else callbacks.push(cb);
      return api;
    };
    api.block = function(on) {
      //log('++',name,on);
      blockers[on]=true;
      return api;
    };
    api.unblock = function(on) {
      delete blockers[on];
      //log('--',name,on,$.isEmptyObject(blockers));
      if ($.isEmptyObject(blockers)) {
        try { console.timeStamp('viipg '+name+'['+callbacks.length+']'); } catch (e) {}
        $(callbacks).each(function(){viipg.later(this);});
      }
      return api;
    };
    return api;
  }
  // initialization...
  viipg.run = viipg.run || {};
  // Read a page's GET URL variables and return them as an associative array.
  viipg.args = (function() {
    var args = [], hash, href=window.location.href;
    var h=href.indexOf('#');
    var hashes = (h==-1) ? href : href.slice(0,h);
    hashes = hashes.slice(hashes.indexOf('?') + 1).split('&');
    for(var i = 0; i < hashes.length; i++) {
      hash = hashes[i].split('=',2);
      args.push(hash[0]);
      args[hash[0]] = ( hash.length==2 ) ? hash[1] : '';
    }
    return args;
  })();
  if (viipg.args.log !== undefined && viipg.args.log) {
    log = (function() {
      //return function(){}; // - no debugging
      if (console) {
        try {
          var fn = console.log.bind(console);
          if (typeof(fn)==='function') return fn;
        } catch (e) {}
        return console.log;
      } else { 
        return function(){};
      }
    })();
  }
  
  viipg.later = (function() { // a faster setTimeout with zero waiting
    if (typeof(window.postMessage)=='undefined') {
      return function(fn) { setTimeout(fn,0); };
    } else {
      var timeouts = [];
      var messageName = "0timeoutMsg";

      // Like setTimeout, but only takes a function argument.  There's
      // no time argument (always zero) and no arguments (you have to
      // use a closure).
      function setZeroTimeout(fn) {
        timeouts.push(fn);
        window.postMessage(messageName, "*");
      }
      function handleMessage(evt) {
        if (evt.data == messageName && evt.source == window) {
          if (evt.stopPropagation) evt.stopPropagation();
          else evt.cancelBubble=true;
          
          if (timeouts.length > 0) {
            var fn = timeouts.shift();
            fn();
          }
        }
      }
      if (window.addEventListener) { //DOM method for binding an event
        window.addEventListener("message", handleMessage, true);
      } else if (window.attachEvent) { //IE exclusive method for binding an event
        window.attachEvent("onmessage", handleMessage);
      } //else if (document.getElementById) //support older modern browsers

      // Add the one thing we want added to the window object.
      return setZeroTimeout;
    }
  })();
  
 
  var detectFlash = function() { // returns flash version
    // ie
    try {
      try {
        // avoid fp6 minor version lookup issues
        // see: http://blog.deconcept.com/2006/01/11/getvariable-setvariable-crash-internet-explorer-flash-6/
        var axo = new ActiveXObject('ShockwaveFlash.ShockwaveFlash.6');
        try { axo.AllowScriptAccess = 'always';	} 
        catch(e) { return '6,0,0'; }				
      } catch(e) {}
      return new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version').replace(/\D+/g, ',').match(/^,?(.+),?$/)[1];
    // other browsers
    } catch(e) {
      try {
        if(navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin){
          return (navigator.plugins["Shockwave Flash 2.0"] || navigator.plugins["Shockwave Flash"]).description.replace(/\D+/g, ",").match(/^,?(.+),?$/)[1];
        }
      } catch(e) {}		
    }
    return null;
  };
  var detectMobile = function() {
    function findIn(haystack_str, needle_list) {
      if (!haystack_str) return '';
      for (var i=0; i<needle_list.length; i++) {
        if (haystack_str.indexOf(needle_list[i]) != -1) return needle_list[i];
      }
    }
    var mobile = findIn(navigator.userAgent.toLowerCase(),['android','series60','symbian','windows ce','blackberry'])
                 || findIn(navigator.platform,['iPad','iPhone']);
    if (!mobile) { // not detected
      // If the screen orientation is defined we are in a modern mobile OS
      var mobileOS = typeof window.orientation != 'undefined' ? true : false;
      // If touch events are defined we are in a modern touch screen OS
      var touchOS = ('ontouchstart' in document.documentElement) ? true : false;
      if (mobileOS || touchOS) mobile = 'other';
    }
    return mobile;
  };
  var detectHTML5Video = function() {
    var video = document.createElement('video');
    var canPlay = video.canPlayType;
    var result = !!canPlay; // is the function defined
        
    try { // IE9 Running on Windows Server SKU can cause an exception to be thrown, bug #224
      if ( result ) {
        result      = new Boolean(result);
        //result.ogg  = canPlay('video/ogg; codecs="theora"');
        result.webm = canPlay('video/webm; codecs="vp8, vorbis"');
        // Workaround required for IE9, which doesn't report video support without audio codec specified.
        //   bug 599718 @ msft connect
        var h264 = 'video/mp4; codecs="avc1.42E01E';
        result.h264 = canPlay(h264 + '"') || canPlay(h264 + ', mp4a.40.2"');
      }
    } catch(e) { }
    return result;  
  };

  var loadVideoEngine = function($container,onLoad) {
    var loadHTML5VideoEngine = function() {
      $LAB.script('common/js/viipg_html5video.js').wait(function(){
        var videoEngine = viipg.HTML5VideoEngine($container);
        videoEngine.initialize(onLoad);
      });
    };
    var loadFlowplayerVideoEngine = function() {
      $LAB.script('common/js/viipg_flowplayer.js').script('http://dev.viidea.com/site/media/flowplayer/flowplayer.js').wait(function(){
        var videoEngine = viipg.FlowplayerVideoEngine($container);
        videoEngine.initialize(onLoad);
      });
    };
    var loadViideaFlashEngine = function() {
      $LAB.script('common/js/viipg_customflash.js').script('https://ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js').wait(function(){
        var videoEngine = viipg.ViideaFlashEngine($container);
        videoEngine.initialize(onLoad);
      });
    };
    
    // choose player type
    var html5 = !detectFlash() //|| detectMobile();
    html5 |= (typeof viipg.cfg.preferred_player === 'string') && viipg.cfg.preferred_player.toLowerCase()=='html5' && detectHTML5Video();
    html5 |= viipg.args.html5;
    html5 &= !viipg.args.flash;
    if (html5) {
      loadHTML5VideoEngine();
    } else {
      loadViideaFlashEngine();
      //loadFlowplayerVideoEngine();
    }
  };
  
  var parseSmil = function(smil,bundle) {
    function attrs2dict(attributes) {
      var dict = {};
      for (var i=0; i<attributes.length; i++) {
        if (!attributes[i].specified) continue;
        dict[attributes[i].nodeName] = attributes[i].nodeValue;
      }
      return dict;
    }
    function registerVideos() {
      var videos = [];
      videos.dur = this.getAttribute('dur') || this.getAttribute('duration');
      videos.protos = {};
      videos.types = {};
      videos.exts = {};
      videos.type = this.getAttribute('type'); // normal, preview, trailer, add,...
      $(this).children().each( function() {
        var video = attrs2dict( this.attributes );
        video.node = this.nodeName; // audio, video, image ...
        if (video.node=='image') videos[video.type]=video.src;
        videos.push(video);
        videos.types[video.type]=true;
        if (video.proto) videos.protos[video.proto]=true;
        if (video.ext) videos.exts[video.ext]=true;
      });
      bundle.media = videos;
    }
    function registerSubtitles() {
      var subtitles = bundle.subtitles = bundle.subtitles || [];
      $(this).children().each( function() {
        subtitles.push( attrs2dict( this.attributes ) );
      });
    }
    function registerSlide() {
      bundle.slides = bundle.slides || [];
      var slide = attrs2dict(this.attributes);
      slide.images = [];
      $(this).children().each( function() {
        slide.images.push( attrs2dict(this.attributes) );
      });
      var prev_slide = bundle.slides[bundle.slides.length-1];
      if (prev_slide && prev_slide.end === undefined) prev_slide.end = slide.begin;
      bundle.slides.push(slide);
    }
    
    // ----------------
    bundle = bundle || {};
    $('head>meta',smil).each( function() { bundle[this.getAttribute('name')] = this.getAttribute('content'); });
    $('body>switch[region="video"]',smil).each(registerVideos);
    $('body>switch[region="subtitles"]',smil).each(registerSubtitles);
    $('[region="slides"]>switch',smil).each(registerSlide);
    if (bundle.slides) viipg.cfg.syncs = bundle.slides.length;
    else viipg.cfg.syncs=0;
    return bundle;
  };

  var tryInstallBundle = function() { // install bundle when both videoEngine and the bundle are loaded
    log('tryInstallBundle: videoEngine='+typeof(viipg.videoEngine)+', bundle='+typeof(bundle)+', slideEngine='+typeof(viipg.slideEngine));
    if (!viipg.videoEngine || !bundle) return false;
    if (bundle.slides) {
      if (!viipg.slideEngine) return false;
      viipg.dispatcher = viipg.Dispatcher();
      viipg.slideEngine.setBundle(bundle);
    };
    viipg.videoEngine.setBundle(bundle,viipg.run.autoplay);
    viipg.load.unblock('v.eng');
    if (viipg.run.autoplay) sendViewStats(bundle);
    return true;
  };
  var sendViewStats = function(bundle) { // send view statistics after a short timeout
    setTimeout(function() {
      var media_id = bundle.media[0].id;
      $.ajax({
        url:'/site/ajax/'+'log/video/'+viipg.cfg.video_id+'/'+media_id+'/',
        cache:false,
        async:true
      });
    },20000);
  };
  var onVideoEngineLoaded = function(videoEngine) {
    viipg.videoEngine = videoEngine;
    if (bundle) tryInstallBundle(); 
  };
  var processSmil = function(url,text,xml) {
    //alert(xhr.responseXML);
    try { console.timeStamp('SMIL processing...'); } catch (e) {}
    bundle = parseSmil(xml,{url:url,text:text});
    tryInstallBundle();
  };
  var setSmilXHR = function(url_or_xhr) { // on init, the viipg.xhr is already initialized and sent in base.html
    var xhr;
    function process() {
      if (xhr.readyState == 4) {
        xhr.onreadystatechange = function(){}; // fix a memory leak in IE
        log('xhr complete, jQuery ' + ($ ? 'loaded' : 'not loaded'));
        //try { console.timeStamp('SMIL Loaded') } catch (e) {};
        viipg.ready(function() {processSmil(xhr.url,xhr.responseText,xhr.responseXML);});
      }
    }
    if (typeof(url_or_xhr)==='string') {
      xhr = new XMLHttpRequest(); // note: IE never uses XHR (it supports true preloading), so no more need for ActiveXObject fallback for IE <= 7
      xhr.onreadystatechange = process;
      xhr.open("GET",url_or_xhr,true);
      xhr.send();
    } else if (typeof(url_or_xhr)==='object') {
      xhr = url_or_xhr;
      xhr.onreadystatechange = process;
      process();
    }
    return xhr;
  };
  viipg.loadVideo = function(partno) {
    viipg.cfg.video = partno;
    setSmilXHR('/'+viipg.cfg.slug+'/video/'+partno+'/smil.xml');
    $('.partlist_thumb.current').removeClass('current');
    $('#partlist_'+partno).addClass('current');
    return false;
  };

  viipg.run.autoplay = (function() {
    var autoplay = viipg.args.autoplay;
    if (autoplay !== undefined) {
      autoplay = (autoplay !== '0') && (autoplay.toLowerCase() !== 'false');
    } else {
      autoplay = viipg.cfg.autoplay;
      autoplay = (autoplay===undefined) ? !viipg.cfg.editable : !!autoplay;
    }
    return autoplay;
  })(); // execute inplace
  viipg._init = function() {
    log('script loading complete');
    $ = jQuery;
    viipg.ready = waitList(viipg.ready._todo,'ready').block('.ready');
    viipg.load = waitList(viipg.load._todo,'load').block('.load');

    // delayed initialization
    $(document).ready( function() {
      log('document.ready');
      $video_div = $('#video_player_embed');
      $slide_div = $('#current_slide');
      if (viipg.cfg.video) { // have Video
        viipg.load.block('v.eng');
        loadVideoEngine($video_div, onVideoEngineLoaded);
        if (viipg.cfg.syncs) { // have Slides
          viipg.load.block('slides');
          $LAB.script('common/js/viipg_slides.js').script('common/js/viipg_dispatcher.js').script('http://ajax.microsoft.com/ajax/jquery.templates/beta1/jquery.tmpl.min.js').wait(function(){
            viipg.slideEngine = viipg.SlideEngine();
            if (bundle && viipg.videoEngine) tryInstallBundle();
            viipg.load.unblock('slides');
          });
        }
      }
      viipg.headLAB = viipg.headLAB.wait(function() {viipg.ready.unblock('.ready');});
    });
    
    // process viipg.ready: pass the list to $(document).ready()
    $(window).load( function() {
      viipg.load.unblock('.load');
    });
  };
  
  if (viipg.xhr !== undefined) {
    setSmilXHR(viipg.xhr);
//  } else if (viipg.cfg.video && viipg.cfg.slug) {
//    setSmilXHR('/'+viipg.cfg.slug+'/video/'+viipg.cfg.video+'/smil.xml');
  }
  return viipg;
})(viipg||{},window,document);

