﻿/*
* Puretracks Fast-Brand Client Library
*
* Copyright © 2010, Puretracks, a division of Somerset Entertainment Ltd.
* All rights reserved.
* http://corporate.puretracks.com/
*
* Auhor: Roman Gubarenko
*/



/******************************************************************************************************/
/********************************** MISCELLANIOUS PLATFORM UTILS **************************************/
/******************************************************************************************************/

(function($)
{
  // Simple console output
  // Copyright (C) 2009 Jonathan Azoff <jon@azoffdesign.com>
  // jQuery.log v1.0.0 - A jQuery plugin that unifies native console logging across browsers
  $.log = function()
  {
    if (arguments.length > 0)
    {
      // join for graceful degregation
      var args = (arguments.length > 1) ? Array.prototype.join.call(arguments, " ") : arguments[0];

      // this is the standard; firebug and newer webkit browsers support this
      try
      {
        console.log(args);
        return true;
      }
      catch (e)
      {
        // newer opera browsers support posting erros to their consoles
        try
        {
          opera.postError(args);
          return true;
        }
        catch (e) { }
      }

      return false;
    }
  },

  //
  // Generate random alphanumeric string token. Useful when
  // you need to get some pretty looking unique id.
  //
  // length - length of string in characters
  //
  $.FBGetRandomToken = function(length)
  {
    var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    var result = "";
    for (var i = 0; i < length; i++) result += chars.substr(Math.floor(Math.random() * 36), 1);
    return result;
  },

  //
  // Since JSON.stringify is not supported by all browsers, we need
  // this fallback function for real cross-browser functionality.
  //
  // obj - any JSON object
  //
  $.FBJson2String = (typeof (JSON) != "undefined" && JSON.stringify) ? JSON.stringify : function(obj)
  {
    var t = typeof (obj);
    if (t != "object" || obj === null)
    {
      // simple data type  
      if (t == "string") obj = '"' + obj + '"';
      return String(obj);
    }
    else
    {
      // recurse array or object  
      var n, v, json = [], arr = (obj && obj.constructor == Array);
      for (n in obj)
      {
        v = obj[n]; t = typeof (v);
        if (t == "string") v = '"' + v + '"';
        else if (t == "object" && v !== null) v = JSON.stringify(v);
        json.push((arr ? "" : '"' + n + '":') + String(v));
      }
      return (arr ? "[" : "{") + String(json) + (arr ? "]" : "}");
    }
  }
})(jQuery);





/******************************************************************************************************/
/********************************** ASYNC ACTIONS MECHANISM *******************************************/
/******************************************************************************************************/ 

(function($)
{
  //
  // Encode client data to transfer as action parameter
  //
  // data - array of objects that you wish to encode
  //
  $.FBEncodeClientData = function(data)
  {
    if (data == null) return "|0|";
    else if (typeof (data) == "string") return "|" + data.length + "|" + data;
    else if (data.constructor == Array)
    {
      var result = "";
      for (var i = 0; i < data.length; i++)
      {
        result += $.FBEncodeClientData(data[i]);
      }
      return result;
    }
    else throw "Invalid client data";
  },

  //
  // Rise action resulting in async postback.
  //
  // scopeID - id of the update panel that the action is associated with
  // actionName - name of the action using which the system calls a handler
  // actionArgs - array of stringifiable object to pass to action handler as params
  //
  $.FBAsyncAction = function(scopeID, actionName, actionArgs)
  {
    var target = scopeID;
    var prm = Sys.WebForms.PageRequestManager.getInstance();
    if (!Array.contains(prm._asyncPostBackControlIDs, target)) prm._asyncPostBackControlIDs.push(target);
    if (!Array.contains(prm._asyncPostBackControlClientIDs, target)) prm._asyncPostBackControlClientIDs.push(target);
    __doPostBack(target, "" + actionName + "|" + $.FBEncodeClientData(actionArgs));
  }
})(jQuery);


(function($)
{
  var staticMembers =
  {
    handlers: new Array(),
    isInstalled: false,
    installListener: function()
    {
      var prm = Sys.WebForms.PageRequestManager.getInstance();

      prm.add_pageLoaded(function(sender, args)
      {
        // loop through all updated panels
        $.each(args.get_panelsUpdated(), function(idx, elem)
        {
          // invoke refresh handlers for current panel
          var arrHandlers = staticMembers.handlers[elem.id];
          if (arrHandlers)
          {
            var param = args.get_dataItems()[elem.id];
            for (var i = 0; i < arrHandlers.length; i++) arrHandlers[i](param);
          }
        });
      });
    }
  };


  //
  // Install listener callback for refresh of specific update panel.
  // The tool becomes extremely useful whe we need to execute certain
  // activities when a specific update panel is refreshed.
  //
  // updatePanelID - id of the update panel to listen
  // callback - function pointer to execute on refresh
  //
  $.FBAddAsyncRefreshHandler = function(updatePanelID, callback)
  {
    if (typeof (updatePanelID) != "string" || typeof (callback) != "function")
      throw "FBAddAsyncRefreshHandler: Invalid plugin function params";

    // install update panel refresh listener only once
    if (!staticMembers.isInstalled)
    {
      staticMembers.installListener();
      staticMembers.isInstalled = true;
    }

    // get existing or create new handler array
    var arrHandlers = staticMembers.handlers[updatePanelID];
    if (!arrHandlers) arrHandlers = new Array();
    // save handler for current update panel    
    arrHandlers.push(callback);
    staticMembers.handlers[updatePanelID] = arrHandlers;

    $.log("FBAddAsyncRefreshHandler: Async refresh handler #" + arrHandlers.length + " added for: " + updatePanelID);
  }
})(jQuery);





/******************************************************************************************************/
/********************************** FAST-BRAND PLATFORM PLUGINS ***************************************/
/******************************************************************************************************/

/*
* FBCreateDialogJQ jQuery Plugin v1.1
* 
* Creates jQuery dialog window and returns its jq object. This plugin can be used by
* other plugins requiring creating of dialog windows e.g. Message Box Plugin.
*/

; (function($)
{
  $.FBCreateDialogJQ = function(id, options)
  {
    if (id && $("#" + id).length > 0) throw "FBCreateDialogJQ: element with such ID already exists: ";

    var settings =
    {
      "modal": true,
      "autoOpen": false,
      "draggable": false,
      "resizable": false,
      "width": 300,
      "height": "auto",
      "title": "Dialog"
    };

    if (options) $.extend(true, settings, options);

    var $dialog = $("<div></div>");
    if (id) $dialog.attr("id", id);
    $dialog.appendTo(document.body).dialog(settings);

    $.log("FBCreateDialogJQ: New dialog is created: " + id);

    return $dialog;
  };
})(jQuery);


/*
* FBMessageBoxJQ jQuery Plugin v1.1
*
* Extremely useful to pop up jQuery-style user-friendly message boxes with title, text 
* message, icon and set of buttons. When message box is closed, its container element 
* and all jQuery bindings are removed from DOM permanently i.e. no garbage is left.
*/

; (function($)
{
  $.FBMessageBoxJQ = function(options, buttons)
  {
    var $dialog = null;

    var consts =
    {
      "modal": true,
      "autoOpen": true,
      "draggable": false,
      "resizable": false,
      "buttons": {},
      "close": function(e, ui)
      {
        // when message box is closed, all it's dialog
        // container element should be removed from DOM
        if ($dialog) $dialog.dialog('destroy').remove();
      }
    };

    var settings =
    {
      "width": 300,
      "height": "auto",
      "title": "Message Box"
    };

    // add constants to settings
    $.extend(settings, consts);
    // override some predefined settings
    if (options.title) settings.title = options.title;
    if (options.width) settings.width = options.width;
    if (options.height) settings.height = options.height;
    if (options.dialogClass) settings.dialogClass = options.dialogClass;
    // add buttons with callbacks
    for (var btn in buttons)
    {
      // it's important to process current callback inside dedicated scope
      (function(callback)
      {
        // assign dialog button callback so that it calls the 
        // original callback if needed and then closes the dialog
        settings.buttons[btn] = function()
        {
          if (typeof (callback) === "function")
          {
            var res = callback.apply(this);
            if (res) $(this).dialog("close");
          }
          else
          {
            $(this).dialog("close");
          }
        };
      })(buttons[btn]);
    }

    // display dialog with message
    $dialog = $.FBCreateDialogJQ(null, settings);
    if (options.text) $dialog.html(options.text);
  };
})(jQuery);


/*
* FBItemRotator jQuery Plugin v1.1
*
* Useful complement to jQuery UI widgets allowing to rotate generic list
* of elements by showing specific subset of them at a time and switching
* to another subset when required.
*/

; (function($)
{
  var methods =
  {
    init: function(options)
    {
      // if not initialized yet
      if (!$(this).data("FBItemRotator.ContextData"))
      {
        var $this = $(this);

        var newCtx =
        {
          "$items": $this.find(options.item),
          "$prev": $this.find(options.prev),
          "$next": $this.find(options.next),
          "pageSize": options.pageSize,
          "startItemIdx": options.startItemIdx
        };

        // do some basic parameters verification
        if (newCtx.$items.length == 0) throw "No rotator item were found";
        else if (newCtx.pageSize <= 0) throw "Invalid rotator page size";
        else if (newCtx.startItemIdx < 0 || newCtx.startItemIdx > newCtx.$items.length - 1) throw "Invalid rotator start item index";

        // bind prev / next buttons if needed
        if (newCtx.$prev.length > 0)
        {
          newCtx.$prev.click(function()
          {
            if (!$(this).hasClass("ui-state-disabled")) $this.FBItemRotator("prev");
            return false;
          });
        }
        else newCtx.$prev = null;

        if (newCtx.$next.length > 0)
        {
          newCtx.$next.click(function()
          {
            if (!$(this).hasClass("ui-state-disabled")) $this.FBItemRotator("next");
            return false;
          });
        }
        else newCtx.$next = null;

        $(this).data("FBItemRotator.ContextData", newCtx);
      }
    },
    update: function(bInitialLoad)
    {
      var ctx = $(this).data("FBItemRotator.ContextData");
      if (!ctx) throw "Widget is not initialized";

      var itemsCount = ctx.$items.length;
      // adjust display item starting index to make sure it's correct
      ctx.startItemIdx = Math.max(ctx.startItemIdx, 0);
      ctx.startItemIdx = Math.min(ctx.startItemIdx, itemsCount - 1);
      ctx.startItemIdx = Math.floor(ctx.startItemIdx / ctx.pageSize) * ctx.pageSize;

      // show list items for current page
      ctx.$items.css("display", "none");
      for (var i = ctx.startItemIdx; i < ctx.startItemIdx + ctx.pageSize && i < itemsCount; i++)
      {
        if (bInitialLoad) ctx.$items.eq(i).css("display", "block");
        else ctx.$items.eq(i).fadeIn();
      }

      // enable / disable pager buttons
      if (ctx.$prev)
      {
        if (ctx.startItemIdx == 0) ctx.$prev.addClass("ui-state-disabled").removeClass("ui-state-hover");
        else ctx.$prev.removeClass("ui-state-disabled");
      }
      if (ctx.$next)
      {
        if (ctx.startItemIdx + ctx.pageSize > itemsCount - 1) ctx.$next.addClass("ui-state-disabled").removeClass("ui-state-hover");
        else ctx.$next.removeClass("ui-state-disabled");
      }
    },
    prev: function()
    {
      var ctx = $(this).data("FBItemRotator.ContextData");
      if (!ctx) throw "Widget is not initialized";
      ctx.startItemIdx = ctx.startItemIdx - ctx.pageSize;
      methods.update.apply(this, [false]);
    },
    next: function()
    {
      var ctx = $(this).data("FBItemRotator.ContextData");
      if (!ctx) throw "Widget is not initialized";
      ctx.startItemIdx = ctx.startItemIdx + ctx.pageSize;
      methods.update.apply(this, [false]);
    }
  };

  $.fn.FBItemRotator = function(options)
  {
    var settings =
    {
      "item": "._plugin-rotator-item",
      "prev": "._plugin-rotator-btn-prev",
      "next": "._plugin-rotator-btn-next",
      "pageSize": 3,
      "startItemIdx": 0
    };

    this.each(function()
    {
      // process method call
      if (typeof (options) === "string")
      {
        switch (options)
        {
          case "prev": methods.prev.apply(this); break;
          case "next": methods.next.apply(this); break;
          default: throw "Call to unknown method";
        }
      }
      // process widget initialization call
      else if (typeof (options) === "object" || !options)
      {
        if (options) $.extend(settings, options);
        methods.init.apply(this, [settings]);
        methods.update.apply(this, [true]);
      }
      else throw "Invalid plugin call";
    });
  };
})(jQuery);


/*
* FBPreviewPlayer jQuery Plugin v4.2
*
* Provides media preview functionality across the platform. 
* Supported media formats: MP3, WMA 
*
* Requires: JWPlayer5.swf Flash object & JWPlayer5.js javascript API for MP3 playback
* Requires: jQuery Media Plugin for WMA playback
*/

; (function($)
{
  var settings =
  {
    flashLocation: "../res/jwplayer/jwplayer.swf",
    previewButton: "._plugin-preview-button",
    playIconClass: "ui-icon-play",
    stopIconClass: "ui-icon-stop",
    playingState: "ui-state-highlight",
    duration: 30
  };

  var members =
  {
    isInitialized: false,
    $container: null,
    playerID: null,
    currPlayer: null,

    resetPlayButtons: function()
    {
      $(settings.previewButton).removeClass(settings.playingState);
      $(settings.previewButton).find(".ui-icon").removeClass(settings.stopIconClass).addClass(settings.playIconClass);
    },
    showPlayButton: function($button)
    {
      $button.addClass(settings.playingState);
      $button.find(".ui-icon").removeClass(settings.playIconClass).addClass(settings.stopIconClass);
    },
    resetContainer: function()
    {
      members.$container.empty();
      members.$container.append("<div id='" + members.playerID + "'></div>");
    }
  };

  var methods =
  {
    stop: function()
    {
      if (members.currPlayer)
      {
        members.currPlayer.StopPlayback();
      }
    }
  };

  var players =
  {
    //
    // JWPlayer Implementation
    //

    JWPlayer:
    {
      // Public interface     
      StartPlayback: function(previewURL)
      {
        // ensure required JavaScript API present
        if (typeof (jwplayer) == "undefined" || !jwplayer)
          throw logPrefix + ": JWPlayer 5 JavaScript API is required";

        jwplayer(members.playerID).setup({
          flashplayer: settings.flashLocation,
          width: 1,
          height: 1,
          file: previewURL,
          duration: 30,
          autostart: true,
          wmode: "window",

          events:
          {
            onComplete: function() { players.JWPlayer.StopPlayback(); }
          }
        });

        // save just inserted player object
        players.JWPlayer._playerObject = jwplayer(members.playerID);
        if (!players.JWPlayer._playerObject) throw "Failed to add player object to HTML page";

        players.JWPlayer._isPlaying = true;

        $.log("FBPreviewPlayer: JWPlayer will play: " + previewURL);
      },
      StopPlayback: function()
      {
        if (!players.JWPlayer._isPlaying) return;

        players.JWPlayer._isPlaying = false;

        // cleanup player object
        players.JWPlayer._playerObject.stop();
        players.JWPlayer._playerObject = null;

        members.resetPlayButtons();

        $.log("FBPreviewPlayer: JWPlayer stopped");
      },

      // Private members
      _isPlaying: false,
      _playerObject: null
    },

    //
    // WMPlayer Implementation
    //

    WMPlayer:
    {
      // Public interface
      StartPlayback: function(previewURL)
      {
        // ensure required JavaScript API present
        if (typeof ($.fn.media) == "undefined" || !$.fn.media)
          throw "FBPreviewPlayer: jQuery Media Plugin is required";

        $("#" + members.playerID).media({
          width: 1,
          height: 1,
          autoplay: true,
          src: previewURL,
          attrs: { name: members.playerID },
          caption: false
        });

        // get just inserted player object
        players.WMPlayer._playerObject = members.$container.find("object,embed").get(0);
        if (!players.WMPlayer._playerObject) throw "FBPreviewPlayer: Failed to add player object to HTML page";
        // start state checker timer
        if (players.WMPlayer._playerObject.playState) players.WMPlayer._stateTimer = setTimeout(players.WMPlayer._stateCallback, 1000);
        else players.WMPlayer._stateTimer = setTimeout(players.WMPlayer._stateCallback, settings.duration * 1000);

        // make player invisible if possible
        if (players.WMPlayer._playerObject.uiMode) players.WMPlayer._playerObject.uiMode = "invisible";

        players.WMPlayer._isPlaying = true;

        $.log("FBPreviewPlayer: WMPlayer will play: " + previewURL);
      },
      StopPlayback: function()
      {
        if (!players.WMPlayer._isPlaying) return;

        players.WMPlayer._isPlaying = false;

        // cleanup timer
        if (players.WMPlayer._stateTimer)
        {
          clearTimeout(players.WMPlayer._stateTimer);
          players.WMPlayer._stateTimer = null;
        }
        // cleanup player object
        if (players.WMPlayer._playerObject.controls) players.WMPlayer._playerObject.controls.stop();
        players.WMPlayer._playerObject = null;

        members.resetPlayButtons();

        $.log("FBPreviewPlayer: WMPlayer stopped");
      },

      // Private members
      _isPlaying: false,
      _playerObject: null,
      _stateTimer: null,
      _stateCallback: function()
      {
        if (players.WMPlayer._playerObject.controls && players.WMPlayer._playerObject.controls.currentPosition >= settings.duration) // if playing longer then 30 seconds, then force it to stop
        {
          players.WMPlayer.StopPlayback();
        }
        else if (players.WMPlayer._playerObject.playState) // if playState is available (might be unavailable for wrong plugin)
        {
          if (players.WMPlayer._playerObject.playState == 1 || players.WMPlayer._playerObject.playState == 8) // set flag if playback stopped or completed
          {
            players.WMPlayer.StopPlayback();
          }
          else // otherwise, set timeout to check again after a while
          {
            players.WMPlayer._stateTimer = setTimeout(players.WMPlayer._stateCallback, 1000);
          }
        }
        else // otherwise, just stop
        {
          players.WMPlayer.StopPlayback();
        }
      }
    }
  };

  $.FBPreviewPlayer = function(options)
  {
    // initialize plugin only once
    if (!members.isInitialized)
    {
      // override settings if needed
      if (options) $.extend(settings, options);

      // add dedicated player container to page BODY tag
      members.$container = $("<div></div>");
      members.$container.appendTo(document.body);

      members.playerID = "FBPreviewPlayer_" + $.FBGetRandomToken(16);

      // extremely important (!!!) to remove previously 
      // bound handlers, because all of them have a scope 
      // corresponding to previous call to the plugin
      $(settings.previewButton).die("click");
      // bind preview buttons
      $(settings.previewButton).live("click", function()
      {
        var $button = $(this);
        // on click, the playback should start or stop
        if (!$button.hasClass(settings.playingState))
        {

          // retrieve preview media url
          var previewURL = $button.attr("preview");
          if (!previewURL || previewURL.length == 0) throw "FBPreviewPlayer: Preview URL cannot be empty";
          // retrieve media type from url
          var reType = RegExp("\\.(mp3|wma)($|\\?)", "ig");
          if (!reType.exec(previewURL)) throw "FBPreviewPlayer: Invalid preview URL: " + previewURL;
          var mediaType = RegExp.$1.toLowerCase();

          // stop what's currently playing
          if (members.currPlayer)
          {
            members.currPlayer.StopPlayback();
            members.currPlayer = null;
          }
          // prepare DOM
          members.resetContainer();

          // choose player
          switch (mediaType)
          {
            case "mp3": members.currPlayer = players.JWPlayer; break;
            case "wma": members.currPlayer = players.WMPlayer; break;
            default: throw "FBPreviewPlayer: Invalid preview URL : " + previewURL;
          }

          // start playback
          members.currPlayer.StartPlayback(previewURL);

          // display stop button
          members.resetPlayButtons();
          members.showPlayButton($button);

          $.log("FBPreviewPlayer: Playback started");
        }
        else
        {
          // stop playback
          members.currPlayer.StopPlayback();
        }
      });

      members.isInitialized = true;

      $.log("FBPreviewPlayer: Plugin initialized successfully.");
    }

    // call method if one specified
    if (typeof (options) == "string" && methods[options])
    {
      methods[options].apply(this, Array.prototype.slice.call(arguments, 1));
    }
  };
})(jQuery);


/*
* FBLoadAsyncContent jQuery Plugin v1.1
*
* Loads content from specified URL in async mode and 
* populates it into the matched element as child html.
*/

; (function($)
{
  $.fn.FBLoadAsyncContent = function(srcURL, options)
  {
    var settings =
    {
      success: null,
      error: null
    };
    if (options) $.extend(settings, options);

    // take only 1st element in matched sequence
    var $this = $(this).eq(0);

    // start async request to load data on the content screen
    ajaxReq = $.ajax({
      url: srcURL,
      cache: false,
      success: function(html)
      {
        $this.html(html);
        if (typeof (settings.success) == "function") settings.success.apply($this, [html]);
      },
      error: function()
      {
        if (typeof (settings.error) == "function") settings.error.apply($this);
      }
    });
  };
})(jQuery);


/*
* FBGlobalAjaxWait jQuery Plugin v1.1
*
* Displays waiting screen when ASP.NET async postback is in progress.
*/

; (function($)
{

  var settings =
  {
    enabled: true,
    showDelay: 200,
    hangDelay: 500
  };

  var members =
  {
    isInitialized: false,
    $dialog: null,
    timerID: null
  };

  $.FBGlobalAjaxWait = function(options)
  {
    if (options) $.extend(settings, options);

    if (!members.isInitialized)
    {
      // get ASP.NET AJAX PageRequestManager instance
      var prm = Sys.WebForms.PageRequestManager.getInstance();

      // when async postback starts, progress needs to be shown
      prm.add_beginRequest(function()
      {
        if (settings.enabled)
        {
          // clear current timer
          if (members.timerID) clearTimeout(members.timerID);
          // add progress dialog with delay using timer
          members.timerID = setTimeout(function()
          {
            members.$dialog = $.FBCreateDialogJQ(null,
            {
              "modal": true,
              "autoOpen": true,
              "draggable": false,
              "resizable": false,
              "closeOnEscape": false,
              "dialogClass": "fbui-ajax-loader-popup",
              "buttons": {}
            });
          }, settings.showDelay);
        }
      });

      // when async postback ends, progress needs to hide
      prm.add_endRequest(function()
      {
        if (settings.enabled)
        {
          // clear timer
          if (members.timerID)
          {
            clearTimeout(members.timerID);
            members.timerID = null;
          }
          // remove progress dialog
          if (members.$dialog)
          {
            // launch hiding timer for current dialog
            setTimeout(Function.createDelegate(members.$dialog, function()
            {
              $(this).fadeOut("fast", function() { $(this).dialog('destroy').remove(); });
            }), settings.hangDelay);
          }
          // clear dialog 
          members.$dialog = null;
        }
      });

      members.isInitialized = true;

      $.log("FBGlobalAjaxWait: Plugin initialized successfully!");
    }

  };

  // by default, add ajax wait widget to every page
  $(document).ready(function()
  {
    $.FBGlobalAjaxWait();
  });

})(jQuery);

