Source: main.js

/** telll SDK
* Copyright 2015, 2016 by Monsenhor <filipo@kobkob.org>
* license GPL Affero 3.0
* telll requires jQuery version 2.2.0
*/

VERSION = "0.17.01";

// Load mustache
Mustache = require('mustache');

// Apply some jQuery plugins
(function($){
/*
 * jQuery FxQueues 2.1
 * Copyright 2009, 2010 Luciano German Panaro <contact@decodeuri.com>
 * Released under the MIT and GPL licenses.
 */
  var fxQueue = function(queueName) {
    return {
      name: queueName,

      isFxQueue: true,

      paused: false,

      playing: null,

      shouldStart: function() {
          return (this.playing == null || !this.paused);
      },

      pause: function() {
          if (!this.playing) { return false; }
          ((this.playing.isScope)? this.playing: this.playing.elem).stop();
          this.paused = true;
          return true;
      },

      stop: function() {
          if (!this.playing) { return false; }
          ((this.playing.isScope)? this.playing: this.playing.elem).stop();
          this.playing = null;
          this.paused = false;
          this.length = 0;
          return true;
      },

      start: function() {
          // if a dequeued fn was paused
          if (this.playing && this.paused) {
              this.playing();
              this.paused = false;
              return true;
          // or if the queue has not started
          } else if (this.length && !this.playing) {
              this.playing = this[0];
              $(document).dequeue(this.name);
              return true;
          }
          return false;
      },

      getScope: function( scopeName ) {
          if (this.playing && this.playing.isScope && this.playing.called == scopeName) {
              return this.playing;
          }

          for (var i = 0; i < this.length; i++) {
              if (this[i].isScope && this[i].called == scopeName) {
                  return this[i];
              }
          }
          return false;
      },

      dequeue: function( caller ) {
          // Do nothing if queue is not playing anything
          if (!this.playing) {
            return false;
          }

          if (this.playing.isScope) {

              var queueItems = this.playing.items;
              // Find the actual element in scope's items
              for ( var i=0; i < queueItems.length; i++) {
                  if ( caller == queueItems[i].elem[0] && !queueItems[i].finished ) {
                      queueItems[i].finished = true;
                      this.playing.finishedItems++;
                  }
              }

              // Do not dequeue if scope is not finished
              if (this.playing.finishedItems < queueItems.length) {
                  return false;
              }

          // Dequeue just once for every selection
          } else if (this.playing.elem && this.playing.elem[0] != caller) {
              return false;
          }

          var queue = this;

          setTimeout(function() {
              queue.playing = queue[0];
              $(document).dequeue(queue.name);
          }, this.playing.postDelay);

          return true;
      }

    };

  };

  var fxScope = function ( scopeName ) {
      var newScope = function() {
          for (var i=0; i < newScope.items.length; i++) {
              newScope.items[i]();
          }
      };
      newScope.called = scopeName;
      newScope.isScope = true;
      newScope.finishedItems = 0;
      newScope.stop = function() {
          for (var i=0; i < newScope.items.length; i++) {
              newScope.items[i].elem.stop();
          }
      };
      newScope.items = [];
      return newScope;
  };

  // We need to overload the default animate method
  var _animate = $.fn.animate;

  $.fn.animate = function( props, speed, easing, callback ) {
      if (!this.length) {
          return this;
      }

      var options = (typeof speed == "object")? speed: $.speed(speed, easing, callback);

      // Load in the default options
      var opts = $.extend({
          queue: "fx",
          position: "end",
          limit: -1,
          preDelay: 0,
          postDelay: 0,
          complete: null
      }, options );

      // Let normal animations just pass through
      if ( !opts.queue || opts.queue == "fx" ) {
          return _animate.apply( this, arguments );
      }

      // Get the name of the queue
      var queueName = opts.queue;

      // Get the effect queue (A global queue is centered on 'document')
      var queue = $(document).queue( opts.queue );

      // Queue initialization
      if ( queue.length == 0 && !queue.isFxQueue ) {
          $(document).queue( queueName, [] ); //initialize queue
          queue = $(document).queue( queueName ); //get the new queue
          $.extend(queue, fxQueue(queueName)); //extend with fxQueue
      }

      // We're overriding the default queueing behavior
      opts.queue = false;

      // The animation to queue
      var fn = function() {
          opts.complete = function() {
              queue.dequeue(this);
              if ( $.isFunction(fn.users_complete) ) {
                  return fn.users_complete.apply(this, arguments);
              }
          };
          setTimeout(function() {
              fn.elem.animate( props, opts );
          }, fn.preDelay);
      };
      fn.elem = this;
      fn.preDelay = opts.preDelay || 0;
      fn.postDelay = opts.postDelay || 0;
      fn.users_complete = speed.complete || callback; //Do not use the one generated by $.speed

      // If scope exists, just add the animation and return
      var scope = queue.getScope( opts.scope );
      if ( scope ) {
          scope.items.push( fn );
          // Start the animation if the scope is already being played
          if (queue.playing == scope) {
              fn();
          }
          return this;
      }

      // Restrict the animation to a specifically sized queue
      if ( opts.limit < 0 || queue.length < opts.limit) {

          var add = null; //What we are going to add into the queue
          if ( opts.scope ) {
              add = fxScope( opts.scope );
              add.items.push(fn);
          } else {
              add = fn;
          }

          if ( opts.position == "end" ) {
            queue.push( add );
          } else if ( opts.position == "front" ) {
            queue.splice( 1, 0, add );
          }

          if ( queue.shouldStart() ) {
            queue.start();
          }

          return this;
      }
  };

  // A simple global fx queue getter
  $.extend({
      fxqueue: function(queueName) {
          return $(document).queue( queueName );
      }
  });

  $.fxqueue.version = "2.1";


  // Create event to listen when jquery changes a class
  // We need that to trigger the projekktor toolbar and reposition the warn tool
  var originalAddClassMethod = $.fn.addClass;
  $.fn.addClass = function() {
    var result = originalAddClassMethod.apply(this, arguments);
    // trigger a custom event
    $(this).trigger('cssClassChanged');
    return result;
  };

  // Create the doubletap method
  // based on http://appcropolis.com/blog/howto/implementing-doubletap-on-iphones-and-ipads/
  $.fn.doubletap = function(onDoubleTapCallback, onTapCallback, delay) {

	var agent = navigator.userAgent.toLowerCase();
	// detect ios
	var isiOS = false;
	var isIpad = false;
	var isIphone = false;
	if (agent.indexOf('iphone') >= 0) {
	    isiOS = true;
	    isIphone = true;
	}
	if (agent.indexOf('ipad') >= 0) {
	    isiOS = true;
	    isIpad = true;
	}
	// detect android
	var isAndroid = false;
	if (agent.indexOf('android') >= 0) {
	    isAndroid = true;
	}

    var eventName, action;
    delay = delay === null ? 500 : delay;
    eventName = isiOS === true || isAndroid === true ? 'touchend' : 'click';

    $(this).on(eventName, function(event) {
      var now = new Date().getTime();
      /** the first time this will make delta a negative number */
      var lastTouch = $(this).data('lastTouch') || now + 1;
      var delta = now - lastTouch;
      clearTimeout(action);
      if (delta < 500 && delta > 0) {
        if (onDoubleTapCallback !== null && typeof onDoubleTapCallback == 'function') {
          event.preventDefault();
          onDoubleTapCallback(event);
        }
      } else {
        $(this).data('lastTouch', now);
        action = setTimeout(function(evt) {
          if (onTapCallback !== null && typeof onTapCallback == 'function') {
            event.preventDefault();
            onTapCallback(event);
          }
          clearTimeout(action); // clear the timeout
        }, delay, [event]);
      }
      $(this).data('lastTouch', now);
    });
    return this;
  };
})(jQuery);
///////////////////////////////////////////////////



// create the object telllSDK
/**
 * The main Telll module object
 * @module telllSDK
 */
telllSDK = {};
telllSDK = require('./Telll.js');
/**
 * The main TWS module object
 * @module telllSDK.TWS
 */
telllSDK.TWS = {};
telllSDK.TWS = require('./Tws.js').TWS;
telllSDK.CWS = require("./CommandWS.js");
telllSDK.View = {};
telllSDK.TWS.User = require('./User.js').User;
telllSDK.TWS.Device = require('./Device.js').Device;
telllSDK.TWS.Movie = require('./Movie.js').Movie;
telllSDK.TWS.Trackmotion = require('./Trackmotion.js').Trackmotion;
telllSDK.TWS.Photolink = require('./Photolink.js').Photolink;
telllSDK.View.Login = require('./Login.js').Login;
telllSDK.View.TagPlayer = require('./TagPlayer.js').TagPlayer;
telllSDK.View.PhotolinksList = require('./PhotolinksList.js').PhotolinksList;
telllSDK.View.MoviesList = require('./MoviesList.js').MoviesList;
telllSDK.View.MoviePage = require('./MoviePage.js').MoviePage;
telllSDK.View.Clickbox = require('./Clickbox.js').Clickbox;
telllSDK.View.TelllBtn = require('./TelllBtn.js').TelllBtn;
telllSDK.View.Dashboard = require('./Dashboard.js').Dashboard;
telllSDK.View.TelllPlayer = require('./TelllPlayer.js').TelllPlayer;
telllSDK.View.MockPlayer = require('./MockPlayer.js').MockPlayer;
telllSDK.View.TagEditor = require('./TagEditor.js').TagEditor;
telllSDK.View.YoutubePlayer = require('./YoutubePlayer.js').YoutubePlayer;

/**
 * Provides requestAnimationFrame/cancelRequestAnimation in a cross browser way.
 * from paul irish + jerome etienne
 * - http://paulirish.com/2011/requestanimationframe-for-smart-animating/
 * - http://notes.jetienne.com/2011/05/18/cancelRequestAnimFrame-for-paul-irish-requestAnimFrame.html
 */

if ( !window.requestAnimationFrame ) {
	window.requestAnimationFrame = ( function() {
		return window.webkitRequestAnimationFrame ||
		window.mozRequestAnimationFrame ||
		window.oRequestAnimationFrame ||
		window.msRequestAnimationFrame ||
		function( callback, element ) {
			return window.setTimeout( callback, 1000 / 60 );
		};
	} )();
}
if ( !window.cancelRequestAnimationFrame ) {
	window.cancelRequestAnimationFrame = ( function() {
		return window.webkitCancelRequestAnimationFrame ||
		window.mozCancelRequestAnimationFrame ||
		window.oCancelRequestAnimationFrame ||
		window.msCancelRequestAnimationFrame ||
		clearTimeout
	} )();
}

// load default css
var tmpl = require('./templates/default.mtjs');
if (tmpl.css)
$('<style id="default-css">'+tmpl.css+'</style>').appendTo('head');

///////////////////////////
/**
 * Util widgets and plugins
 * @ module telllSDK.util
 */

/**
 * Telll Dialog
 * @function telllDialog
 * @param msg {String} The message
 * @param delay {integer} The delay
 * @global
 */
telllDialog = function(msg, delay){
    $("<div class='telll-dialog'>"+msg+"</div>").appendTo("body").fadeIn(function(){
        setTimeout(function(){
             $(".telll-dialog").fadeOut(function(){$(".telll-dialog").detach();})
        }, delay);
    });
};

/** 
 * Telll Modal Popup
 * @function telllPopup
 * @param element {JQuery} The Jquery object to embbed in popup
 * @param title {string} The popup title, defaults to "telll"
 * @param cb {function} callback called when popup is loaded
 * @return {String} the unique class name
 * @global
 */
telllPopup = function(element, title, cb){
    var idPopup = "";
    var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
    for( var i=0; i < 30; i++ )
        idPopup += possible.charAt(Math.floor(Math.random() * possible.length));
 
    if (!title) title = "telll";
    //$('<div id="'+idPopup+'-overlay" class="overlay"></div>').appendTo('body');
    $('<div id='+idPopup+' class="telll-popup popup"><div class="popup-titlebar widget-titlebar"><div class="logo-86x86"></div><span class="title">'+title+'</span><button class="close">Close</button></div></div>').append(element).appendTo('body').fadeIn("slow",function(){
	    if (cb) cb(idPopup);
    });
    $('.telll-popup').css('z-index','99999');
    $('html').addClass('overlay');
    $('#'+idPopup+' div.popup-titlebar .close').on('click', function(){
        //$('#'+idPopup+'-overlay').detach();
        $('#'+idPopup).detach();
    });
    return idPopup;
}

/** 
 * TelllTimer
 * @function telllTimer
 * @param fn {JQuery} The Jquery object to embbed in popup
 * @param countdown {string} The popup title, defaults to "telll"
 * @return {Integer} The telllTimer object
 * @global
 */
telllTimer = function (fn, countdown) {
    var ident, complete = false;
    function _time_diff(date1, date2) {
        return date2 ? date2 - date1 : new Date().getTime() - date1;
    }
    function cancel() { clearTimeout(ident); }
    function pause() {
        clearTimeout(ident);
        total_time_run = _time_diff(start_time);
        complete = total_time_run >= countdown;
    }
    function resume() {
        ident = complete ? -1 : setTimeout(fn, countdown - total_time_run);
    }
    var start_time = new Date().getTime();
    ident = setTimeout(fn, countdown);
    return { cancel: cancel, pause: pause, resume: resume };
}

/**
* Animate the screen with the 3 columns sliding down and up
* the animation takes 1600 miliseconds
* @param $element {jQuery} element to fade on 
* @param cb1 {function} callback called when loaded 
* @param cb2 {function} callback called when screen is hidden 
* @param cb3 {function} callback called when effect desapeared
*/
telllWipeScreen = function($element,cb1,cb2,cb3){
    if ($element) {
    $element.hide();
    setTimeout(function(){$element.fadeIn()},1000);
    }
    var screenSize = {
        width:$(window).width(),
        height:$(window).height(),
    };
    var $wp = $('<div id="#wipe-screen" class="top-line rgb-bg"><div class="cframe"><div class="red-bg col"></div></div><div class="cframe"><div class="green-bg col"></div></div><div class="cframe"><div class="blue-bg col"></div></div></div>');
    if (!$("div#wipe-screen.topline").length) {
	$wp.appendTo("body");
    }
    $wp.css({"height":"100%","z-index":99999999999999});
    var col = $wp.find("div.col");
    var frame = $wp.find("div.cframe");
    col.css({"width":"100%", "height":"100%","display":"none"});
    frame.css({"width":screenSize.width/3, "height":screenSize.height,"display":"block","float":"left"});
    var promisseRed   = $(col[0]).slideDown(400);
    var promisseGreen = $(col[1]).slideDown(600);
    var promisseBlue  = $(col[2]).slideDown(800);
    if (cb1) cb1(); 
    $.when (promisseRed, promisseGreen, promisseBlue).then(function() {
         if (cb2) cb2(); 
         var promisseRedUp   = $(col[0]).slideUp(400);
         var promisseGreenUp = $(col[1]).slideUp(600);
         var promisseBlueUp  = $(col[2]).slideUp(800);
         $.when (promisseRedUp, promisseGreenUp, promisseBlueUp).then(function() {
               $wp.detach();
               if (cb3) cb3(); 
         });
    });
};