<commit_before>/*
 * Screens.js - v0.9b
 * Copyright (C) 2013 Andreu Santaren Llop
 */

// Init Screens.js library
{
    Array.prototype.last = function()
    {
        return this[this.length - 1];
    };

    Array.prototype.remove = function(index)
    {
        this.splice(index, 1);
    };

    window.BROADCAST_REGISTER_ARRAY = new Array();
    window.SCREEN_STACK = new Array();
}

// Create a class
function __CONTEXT_CLASS__(context, name, obj1, obj2)
{
    if (obj2 !== undefined)            // New class by heritance
    {
        if (typeof(obj1) !== "object")
        {
            throw "Extending from a non object";
        }

        // Clone the object
        var _asc_ = __CLONE__(obj1);

        // Get all element of obj and push it in _asc_
        for (var index in obj2)
        {
            var attr = obj2[index];
            _asc_[index] = attr;
        }

        // Init base properties
        _asc_.CLASS_TYPE.push(_asc_.CLASS_NAME);
        _asc_.CLASS_NAME = name;

        // Create the variable for the class
        context[name] = _asc_;
        return _asc_;
    }
    else                        // New base class
    {
        // Init base methods
        obj1.New = function()
        {
            var newObj = __CLONE__(this);

            delete newObj.New;

            newObj.OBJECT_UNIQUE_KEY = Math.floor(Math.random() * 10000000000000000).toString();

            // Call constructor
            newObj['Init'].apply(newObj, arguments);

            return newObj;
        };

        obj1.Super = function()
        {
            var superClass = this.CLASS_TYPE.last();

            if (superClass === "__CLASS__")
            {
                return null;
            }
            else
            {
                var object = __CLONE__(eval(superClass));
                delete object.New;

                __MERGE__(this, object);

                return object;
            }
        };

        if (!('Init' in obj1))
        {
            obj1.Init = function() {};
        }

        // Init base properties
        obj1.CLASS_TYPE = new Array("__CLASS__");
        obj1.CLASS_NAME = name;

        // Create the variable for the class
        context[name] = obj1;
        return obj1;
    }
}

// Create a class in the global context
function __CLASS__(name, obj1, obj2)
{
    return __CONTEXT_CLASS__(window, name, obj1, obj2);
}

// Base class
__CLASS__('Obj',
{
    Clone: function(obj)
    {
        return __CLONE__(obj);
    },

	Serialize: function(obj)
	{
		return __SERIALIZE__(obj);
	},

	Deserialize: function(obj)
	{
		return __DESERIALIZE__(obj);
	},

	Merge: function(org, dest)
	{
    	__MERGE__(org, dest);
	},

    LoadClass: function(name, baseurl, readyCallBack)
    {
        return __LOAD_CLASS__(name, baseurl, this, readyCallBack);
    },

    UnloadClass: function(classname)
    {
        return __UNLOAD_CLASS__(classname);
    },

	RegisterReceiver: function(delegate, filter)
    {
        return __REGISTER_RECEIVER__(this, delegate, filter);
    },

    UnregisterReceiver: function(receiver)
    {
        __UNREGISTER_RECEIVER__(receiver);
    },

    SendOrderedBroadcast: function(filter, message)
    {
        __SEND_BROADCAST__(filter, message, false);
    },

    SendBroadcast: function(filter, message)
    {
        __SEND_BROADCAST__(filter, message, true);
    }
});

// Base class for screens
__CLASS__('Screen', Obj,
{
    OnLoad: function()
    {},

    OnClose: function()
    {},

    OnReturn: function(retdata)
    {},

    CloseScreen: function(screen)
    {
        screen.OnClose();
    },

    PushScreen: function(selector, screen, loader, args)
    {
        __PUSH_SCREEN__(selector, screen, loader, args);
    },

    PopScreen: function(retdata, args)
    {
        __POP_SCREEN__(retdata, args);
    },

    SetScreen: function(selector, screen)
    {
        return __SET_SCREEN__(selector, screen);
    },

    SetHtml: function(selector, html)
    {
	    __SET_HTML__(this, selector, html);
    },

    Brand: function(selector)
    {
	    __BRAND__(seletor, this);
    },

    Unbrand: function(selector)
    {
	    __UNBRAND__(selector);
    },

    Callback: function(callback)
    {
        return __CALLBACK__(this, callback);
    },

    Ref: function(selector)
    {
        return __REF__(this, selector);
    },

    Click: function(delegate, selector)
    {
        __CLICK__(this, delegate, selector);
    },

    DoubleClick: function(delegate, selector)
    {
        __DOUBLE_CLICK__(this, delegate, selector);
    },

    ListClick: function(delegate, selector)
    {
        __LIST_CLICK__(this, delegate, selector);
    },

    Change: function(delegate, selector)
    {
        __CHANGE__(this, delegate, selector);
    },

    CheckboxClick: function(delegate, selector)
    {
        __CHECKER_CLICK__(this, delegate, selector, 'checkbox');
    },

    RadioClick: function(delegate, selector)
    {
        __CHECKER_CLICK__(this, delegate, selector, 'radio');
    }
});

// Clone an object
function __CLONE__(obj)
{
   return __DESERIALIZE__(__SERIALIZE__(obj));
}

// Convert a class object to string
function __SERIALIZE__(obj)
{
    var fooArray = new Array();
    var attrArray = new Array();

    // Find all functions and convert to string
    for (var attr in obj)
    {
        if (typeof(obj[attr]) === 'function')
        {
            fooArray.push(obj[attr].toString());
            attrArray.push(attr);
        }
    }

    // Obtain a copy of the object (ignoring functions)
    var json = JSON.stringify(obj);
    var copy = JSON.parse(json);

    // Replace the original functions by the strings
    for (var i = 0 ; i < fooArray.length ; i++)
    {
        copy[attrArray[i]] = '(' + fooArray[i] + ')';
    }

    // Add an array for function names
    copy.OBJ_FOO_NAMES = attrArray;

    var serialized = JSON.stringify(copy);

    return serialized;
}

// Convert a string to a class object
function __DESERIALIZE__(obj)
{
    var copy = JSON.parse(obj);

    if (typeof(copy.OBJ_FOO_NAMES) !== 'object')
    {
        throw 'Not a valid serialized object';
    }

    // Take function string and evaluate to obtain the originals
    for (var i = 0 ; i < copy.OBJ_FOO_NAMES.length ; i++)
    {
        var fooStr = copy[copy.OBJ_FOO_NAMES[i]];
        copy[copy.OBJ_FOO_NAMES[i]] = eval('(' + fooStr + ')');
    }

    delete copy.OBJ_FOO_NAMES;

    return copy;
}

// Copy existing propierties of org object to dest object
function __MERGE__(org, dest)
{
    for (var attr in org)
    {
        if (typeof(org[attr]) !== 'function')
        {
            if (attr != 'CLASS_NAME' && attr != 'CLASS_TYPE' && attr != '__proto__')
            {
                if (dest[attr] !== undefined)
                {
                    dest[attr] = org[attr];
                }
            }
        }
    }
}

function __PUSH_SCREEN__(selector, screen, loader, args)
{
    var name = screen.CLASS_NAME;

    // Create an instance of screen class and save it in the stack
    var callname = name + '.New()';
    var screenObj = eval(callname);

    __BRAND__(selector, screenObj);

    loader(selector, args);     // execute loader callback
    screenObj.OnLoad();         // Screen OnLoad methode

    // Add screen to stack
    window.SCREEN_STACK.push({screen: screenObj, selector: selector, loader: loader});
}

function __POP_SCREEN__(retdata, args)
{
    // It is the main screen, no one in the back
    if (window.SCREEN_STACK.length <= 1)
    {
        return;
    }

    // Close the current screen
    var lastScreen = window.SCREEN_STACK.pop();
    lastScreen.screen.OnClose();

    // Get de previus screen object
    var stackObj = window.SCREEN_STACK.last();

    var pageSelector = stackObj.selector;
    var lastPageSelector = lastScreen.selector;

    // Disable all events
    $(lastPageSelector).add("*").off();

    __UNBRAND__(lastScreen.screen);

    stackObj.loader(pageSelector, args);
    stackObj.screen.OnLoad();      // Screen OnLoad methode

    // Call the return function
    if (retdata !== null)
    {
        stackObj.screen.OnReturn(retdata);
    }
}

// Set a Screen class to control a part of an existing page
function __SET_SCREEN__(selector, screen)
{
    var name = screen.CLASS_NAME;

    // Disable all events
    $(selector).find("*").off();

    // Create an instance of screen class and save it in the stack
    var callname = name + '.New()';
    var screenObj = eval(callname);

    __BRAND__(selector, screenObj);

    screenObj.OnLoad();      // Screen OnLoad methode

    return screenObj;
}

// Insert HTML in the DOM and update screen classes
function __SET_HTML__(screen, selector, html)
{
	__REF__(screen, selector).html(html);
    __BRAND__(selector, screen);
}

// Set the Screens brand for a piece of DOM
function __BRAND__(selector, screen)
{
	$(selector).attr('screensclass', screen.CLASS_NAME);
    $(selector).attr('screensid', screen.OBJECT_UNIQUE_KEY);
	$(selector).find('*').attr('screensclass', screen.CLASS_NAME);
    $(selector).find('*').attr('screensid', screen.OBJECT_UNIQUE_KEY);
}

// Remove the Screens brand
function __UNBRAND__(selector)
{
	$(selector).removeAttr('screensclass');
    $(selector).removeAttr('screensid');
	$(selector).find('*').removeAttr('screensclass');
    $(selector).find('*').removeAttr('screensid');
}

// Load a class
function __LOAD_CLASS__(name, baseurl, obj, readyCallBack)
{
    var jsurl = baseurl + "/" + name + ".js";

    if (typeof(window[name]) === 'undefined')
    {
        if (readyCallBack == null)
        {
            jQuery.ajaxSetup({async:false});

            // Load JavaScript class
            $.getScript(jsurl);

            jQuery.ajaxSetup({async:true});

            return true;
        }
        else
        {
            // Load JavaScript class
            $.getScript(jsurl, $.proxy(readyCallBack, obj));

            return true;
        }
    }
    else
    {
        // Class already loaded
        return false;
    }
}

function __UNLOAD_CLASS__(classname)
{
    if (typeof(classname) === 'string')
    {
        return delete window[classname];
    }
    else
    {
        return false;
    }
}

// Callback in the obj context setting the original 'this' as argument
function __CALLBACK__(obj, callback)
{
    return function(args)
    {
        $.proxy(callback, obj)(this, args);
    };
}

// Get an element of a screen by a selector
function __REF__(obj, selector)
{
    return $(
    			"[screensclass='" + obj.CLASS_NAME + "']" +
    			"[screensid='" + obj.OBJECT_UNIQUE_KEY + "']" +
    			selector
    		);
}

// Set click event delegate
function __CLICK__(obj, delegate, selector)
{
    __REF__(obj, selector).click(__CALLBACK__(obj, delegate));
}

// Set double click event delegate
function __DOUBLE_CLICK__(obj, delegate, selector)
{
    __REF__(obj, selector).dblclick(__CALLBACK__(obj, delegate));
}

// Set list click event delegate
function __LIST_CLICK__(obj, delegate, selector)
{
    __REF__(obj, selector).on('click', 'li', __CALLBACK__(obj, delegate));
}

// Element changed delegate
function __CHANGE__(obj, delegate, selector)
{
    __REF__(obj, selector).on('change', __CALLBACK__(obj, delegate));
}

// Set checkbox/radio click event delegate
function __CHECKER_CLICK__(obj, delegate, selector, type)
{
	__REF__(obj, selector).find('input[type=' + type + ']').on('click', __CALLBACK__(obj, delegate));
}

// Register a broadcast receiver
function __REGISTER_RECEIVER__(obj, delegate, filter)
{
    var broadcastObj = {callback: $.proxy(delegate, obj), filter: filter, id: Math.random()};
    window.BROADCAST_REGISTER_ARRAY.push(broadcastObj);

    return broadcastObj.id;
}

// Unregister a broadcast receiver
function __UNREGISTER_RECEIVER__(receiver)
{
    for (var i = 0 ; i < window.BROADCAST_REGISTER_ARRAY.length ; i++)
    {
        if (window.BROADCAST_REGISTER_ARRAY[i].id === receiver)
        {
            window.BROADCAST_REGISTER_ARRAY.remove(i);
            return;
        }
    }
}

// Send broadcast message
function __SEND_BROADCAST__(filter, message, massive)
{
    // Make a copy of the array to avoid problems when others change it
    var arrCopy = window.BROADCAST_REGISTER_ARRAY.slice();

    for (var i = 0 ; i < arrCopy.length ; i++)
    {
        if (arrCopy[i].filter === filter)
        {
            if (massive)
            {
                arrCopy[i].callback(message);
            }
            else
            {
                // If ordered broadcast and callback returns true, halt broadcast
                if (arrCopy[i].callback(message)) return;
            }
        }
    }
}
<commit_msg>FindReceiver object by it's ID.<commit_after>/*
 * Screens.js - v0.9b
 * Copyright (C) 2013 Andreu Santaren Llop
 */

// Init Screens.js library
{
    Array.prototype.last = function()
    {
        return this[this.length - 1];
    };

    Array.prototype.remove = function(index)
    {
        this.splice(index, 1);
    };

    window.BROADCAST_REGISTER_ARRAY = new Array();
    window.SCREEN_STACK = new Array();
}

// Create a class
function __CONTEXT_CLASS__(context, name, obj1, obj2)
{
    if (obj2 !== undefined)            // New class by heritance
    {
        if (typeof(obj1) !== "object")
        {
            throw "Extending from a non object";
        }

        // Clone the object
        var _asc_ = __CLONE__(obj1);

        // Get all element of obj and push it in _asc_
        for (var index in obj2)
        {
            var attr = obj2[index];
            _asc_[index] = attr;
        }

        // Init base properties
        _asc_.CLASS_TYPE.push(_asc_.CLASS_NAME);
        _asc_.CLASS_NAME = name;

        // Create the variable for the class
        context[name] = _asc_;
        return _asc_;
    }
    else                        // New base class
    {
        // Init base methods
        obj1.New = function()
        {
            var newObj = __CLONE__(this);

            delete newObj.New;

            newObj.OBJECT_UNIQUE_KEY = Math.floor(Math.random() * 10000000000000000).toString();

            // Call constructor
            newObj['Init'].apply(newObj, arguments);

            return newObj;
        };

        obj1.Super = function()
        {
            var superClass = this.CLASS_TYPE.last();

            if (superClass === "__CLASS__")
            {
                return null;
            }
            else
            {
                var object = __CLONE__(eval(superClass));
                delete object.New;

                __MERGE__(this, object);

                return object;
            }
        };

        if (!('Init' in obj1))
        {
            obj1.Init = function() {};
        }

        // Init base properties
        obj1.CLASS_TYPE = new Array("__CLASS__");
        obj1.CLASS_NAME = name;

        // Create the variable for the class
        context[name] = obj1;
        return obj1;
    }
}

// Create a class in the global context
function __CLASS__(name, obj1, obj2)
{
    return __CONTEXT_CLASS__(window, name, obj1, obj2);
}

// Base class
__CLASS__('Obj',
{
    Clone: function(obj)
    {
        return __CLONE__(obj);
    },

	Serialize: function(obj)
	{
		return __SERIALIZE__(obj);
	},

	Deserialize: function(obj)
	{
		return __DESERIALIZE__(obj);
	},

	Merge: function(org, dest)
	{
    	__MERGE__(org, dest);
	},

    LoadClass: function(name, baseurl, readyCallBack)
    {
        return __LOAD_CLASS__(name, baseurl, this, readyCallBack);
    },

    UnloadClass: function(classname)
    {
        return __UNLOAD_CLASS__(classname);
    },

	RegisterReceiver: function(delegate, filter)
    {
        return __REGISTER_RECEIVER__(this, delegate, filter);
    },

    UnregisterReceiver: function(receiver)
    {
        __UNREGISTER_RECEIVER__(receiver);
    },

    SendOrderedBroadcast: function(filter, message)
    {
        __SEND_BROADCAST__(filter, message, false);
    },

    SendBroadcast: function(filter, message)
    {
        __SEND_BROADCAST__(filter, message, true);
    },

    FindReceiver: function(receiver)
    {
        return __FIND_RECEIVER__(receiver);
    }
});

// Base class for screens
__CLASS__('Screen', Obj,
{
    OnLoad: function()
    {},

    OnClose: function()
    {},

    OnReturn: function(retdata)
    {},

    CloseScreen: function(screen)
    {
        screen.OnClose();
    },

    PushScreen: function(selector, screen, loader, args)
    {
        __PUSH_SCREEN__(selector, screen, loader, args);
    },

    PopScreen: function(retdata, args)
    {
        __POP_SCREEN__(retdata, args);
    },

    SetScreen: function(selector, screen)
    {
        return __SET_SCREEN__(selector, screen);
    },

    SetHtml: function(selector, html)
    {
	    __SET_HTML__(this, selector, html);
    },

    Brand: function(selector)
    {
	    __BRAND__(seletor, this);
    },

    Unbrand: function(selector)
    {
	    __UNBRAND__(selector);
    },

    Callback: function(callback)
    {
        return __CALLBACK__(this, callback);
    },

    Ref: function(selector)
    {
        return __REF__(this, selector);
    },

    Click: function(delegate, selector)
    {
        __CLICK__(this, delegate, selector);
    },

    DoubleClick: function(delegate, selector)
    {
        __DOUBLE_CLICK__(this, delegate, selector);
    },

    ListClick: function(delegate, selector)
    {
        __LIST_CLICK__(this, delegate, selector);
    },

    Change: function(delegate, selector)
    {
        __CHANGE__(this, delegate, selector);
    },

    CheckboxClick: function(delegate, selector)
    {
        __CHECKER_CLICK__(this, delegate, selector, 'checkbox');
    },

    RadioClick: function(delegate, selector)
    {
        __CHECKER_CLICK__(this, delegate, selector, 'radio');
    }
});

// Clone an object
function __CLONE__(obj)
{
   return __DESERIALIZE__(__SERIALIZE__(obj));
}

// Convert a class object to string
function __SERIALIZE__(obj)
{
    var fooArray = new Array();
    var attrArray = new Array();

    // Find all functions and convert to string
    for (var attr in obj)
    {
        if (typeof(obj[attr]) === 'function')
        {
            fooArray.push(obj[attr].toString());
            attrArray.push(attr);
        }
    }

    // Obtain a copy of the object (ignoring functions)
    var json = JSON.stringify(obj);
    var copy = JSON.parse(json);

    // Replace the original functions by the strings
    for (var i = 0 ; i < fooArray.length ; i++)
    {
        copy[attrArray[i]] = '(' + fooArray[i] + ')';
    }

    // Add an array for function names
    copy.OBJ_FOO_NAMES = attrArray;

    var serialized = JSON.stringify(copy);

    return serialized;
}

// Convert a string to a class object
function __DESERIALIZE__(obj)
{
    var copy = JSON.parse(obj);

    if (typeof(copy.OBJ_FOO_NAMES) !== 'object')
    {
        throw 'Not a valid serialized object';
    }

    // Take function string and evaluate to obtain the originals
    for (var i = 0 ; i < copy.OBJ_FOO_NAMES.length ; i++)
    {
        var fooStr = copy[copy.OBJ_FOO_NAMES[i]];
        copy[copy.OBJ_FOO_NAMES[i]] = eval('(' + fooStr + ')');
    }

    delete copy.OBJ_FOO_NAMES;

    return copy;
}

// Copy existing propierties of org object to dest object
function __MERGE__(org, dest)
{
    for (var attr in org)
    {
        if (typeof(org[attr]) !== 'function')
        {
            if (attr != 'CLASS_NAME' && attr != 'CLASS_TYPE' && attr != '__proto__')
            {
                if (dest[attr] !== undefined)
                {
                    dest[attr] = org[attr];
                }
            }
        }
    }
}

function __PUSH_SCREEN__(selector, screen, loader, args)
{
    var name = screen.CLASS_NAME;

    // Create an instance of screen class and save it in the stack
    var callname = name + '.New()';
    var screenObj = eval(callname);

    __BRAND__(selector, screenObj);

    loader(selector, args);     // execute loader callback
    screenObj.OnLoad();         // Screen OnLoad methode

    // Add screen to stack
    window.SCREEN_STACK.push({screen: screenObj, selector: selector, loader: loader});
}

function __POP_SCREEN__(retdata, args)
{
    // It is the main screen, no one in the back
    if (window.SCREEN_STACK.length <= 1)
    {
        return;
    }

    // Close the current screen
    var lastScreen = window.SCREEN_STACK.pop();
    lastScreen.screen.OnClose();

    // Get de previus screen object
    var stackObj = window.SCREEN_STACK.last();

    var pageSelector = stackObj.selector;
    var lastPageSelector = lastScreen.selector;

    // Disable all events
    $(lastPageSelector).add("*").off();

    __UNBRAND__(lastScreen.screen);

    stackObj.loader(pageSelector, args);
    stackObj.screen.OnLoad();      // Screen OnLoad methode

    // Call the return function
    if (retdata !== null)
    {
        stackObj.screen.OnReturn(retdata);
    }
}

// Set a Screen class to control a part of an existing page
function __SET_SCREEN__(selector, screen)
{
    var name = screen.CLASS_NAME;

    // Disable all events
    $(selector).find("*").off();

    // Create an instance of screen class and save it in the stack
    var callname = name + '.New()';
    var screenObj = eval(callname);

    __BRAND__(selector, screenObj);

    screenObj.OnLoad();      // Screen OnLoad methode

    return screenObj;
}

// Insert HTML in the DOM and update screen classes
function __SET_HTML__(screen, selector, html)
{
	__REF__(screen, selector).html(html);
    __BRAND__(selector, screen);
}

// Set the Screens brand for a piece of DOM
function __BRAND__(selector, screen)
{
	$(selector).attr('screensclass', screen.CLASS_NAME);
    $(selector).attr('screensid', screen.OBJECT_UNIQUE_KEY);
	$(selector).find('*').attr('screensclass', screen.CLASS_NAME);
    $(selector).find('*').attr('screensid', screen.OBJECT_UNIQUE_KEY);
}

// Remove the Screens brand
function __UNBRAND__(selector)
{
	$(selector).removeAttr('screensclass');
    $(selector).removeAttr('screensid');
	$(selector).find('*').removeAttr('screensclass');
    $(selector).find('*').removeAttr('screensid');
}

// Load a class
function __LOAD_CLASS__(name, baseurl, obj, readyCallBack)
{
    var jsurl = baseurl + "/" + name + ".js";

    if (typeof(window[name]) === 'undefined')
    {
        if (readyCallBack == null)
        {
            jQuery.ajaxSetup({async:false});

            // Load JavaScript class
            $.getScript(jsurl);

            jQuery.ajaxSetup({async:true});

            return true;
        }
        else
        {
            // Load JavaScript class
            $.getScript(jsurl, $.proxy(readyCallBack, obj));

            return true;
        }
    }
    else
    {
        // Class already loaded
        return false;
    }
}

function __UNLOAD_CLASS__(classname)
{
    if (typeof(classname) === 'string')
    {
        return delete window[classname];
    }
    else
    {
        return false;
    }
}

// Callback in the obj context setting the original 'this' as argument
function __CALLBACK__(obj, callback)
{
    return function(args)
    {
        $.proxy(callback, obj)(this, args);
    };
}

// Get an element of a screen by a selector
function __REF__(obj, selector)
{
    return $(
    			"[screensclass='" + obj.CLASS_NAME + "']" +
    			"[screensid='" + obj.OBJECT_UNIQUE_KEY + "']" +
    			selector
    		);
}

// Set click event delegate
function __CLICK__(obj, delegate, selector)
{
    __REF__(obj, selector).click(__CALLBACK__(obj, delegate));
}

// Set double click event delegate
function __DOUBLE_CLICK__(obj, delegate, selector)
{
    __REF__(obj, selector).dblclick(__CALLBACK__(obj, delegate));
}

// Set list click event delegate
function __LIST_CLICK__(obj, delegate, selector)
{
    __REF__(obj, selector).on('click', 'li', __CALLBACK__(obj, delegate));
}

// Element changed delegate
function __CHANGE__(obj, delegate, selector)
{
    __REF__(obj, selector).on('change', __CALLBACK__(obj, delegate));
}

// Set checkbox/radio click event delegate
function __CHECKER_CLICK__(obj, delegate, selector, type)
{
	__REF__(obj, selector).find('input[type=' + type + ']').on('click', __CALLBACK__(obj, delegate));
}

// Register a broadcast receiver
function __REGISTER_RECEIVER__(obj, delegate, filter)
{
    var broadcastObj = {callback: $.proxy(delegate, obj), filter: filter, id: Math.random()};
    window.BROADCAST_REGISTER_ARRAY.push(broadcastObj);

    return broadcastObj.id;
}

// Unregister a broadcast receiver
function __UNREGISTER_RECEIVER__(receiver)
{
    for (var i = 0 ; i < window.BROADCAST_REGISTER_ARRAY.length ; i++)
    {
        if (window.BROADCAST_REGISTER_ARRAY[i].id === receiver)
        {
            window.BROADCAST_REGISTER_ARRAY.remove(i);
            return;
        }
    }
}

// Send broadcast message
function __SEND_BROADCAST__(filter, message, massive)
{
    // Make a copy of the array to avoid problems when others change it
    var arrCopy = window.BROADCAST_REGISTER_ARRAY.slice();

    for (var i = 0 ; i < arrCopy.length ; i++)
    {
        if (arrCopy[i].filter === filter)
        {
            if (massive)
            {
                arrCopy[i].callback(message);
            }
            else
            {
                // If ordered broadcast and callback returns true, halt broadcast
                if (arrCopy[i].callback(message)) return;
            }
        }
    }
}

function __FIND_RECEIVER__(receiver)
{
    for (var i = 0 ; i < window.BROADCAST_REGISTER_ARRAY.length ; i++)
    {
        if (window.BROADCAST_REGISTER_ARRAY[i].id === receiver)
        {
            return window.BROADCAST_REGISTER_ARRAY[i];
        }
    }
}
<|endoftext|>


<commit_before>var Section = (function() {
  var Section = function(index, words, contextOptions) {
    if (typeof index !== 'undefined') {
      if (contextOptions.regex) {
        var regex = {},
            include = {};

        if (contextOptions.regex.start && contextOptions.regex.finish) {
          ['start', 'finish'].forEach(function(el) {
            if (contextOptions.regex[el].hasOwnProperty('regex')) {
              regex[el] = contextOptions.regex[el].regex;
              include[el] = contextOptions.regex[el].include;
              include[el] = typeof include[el] === 'undefined' ? true : include[el];
            } else {
              regex[el] = contextOptions.regex[el];
              include[el] = true;
            }
          });
        } else if (contextOptions.regex.hasOwnProperty('regex')) {
          regex.start = regex.finish = contextOptions.regex.regex;
          include.start = contextOptions.regex.include;
          include.start = typeof include.start === 'undefined' ? true : include.start;
          include.finish = include.start;
        } else {
          regex.start = regex.finish = contextOptions.regex;
          include.start = include.finish = true;
        }

        var tmpIndex = words.slice(0, index).reverse().findIndex(function(el) {return el.match(regex.start);});
        if (tmpIndex > -1) {
          this.start = index - tmpIndex + (include.start ? 1 : 0);
        } else {
          this.start = 0;
        }

        tmpIndex = words.slice(index + 1).findIndex(function(el) {return el.match(regex.finish);});
        if (tmpIndex > -1) {
          this.finish = index + 1 + tmpIndex + (include.finish ? 1 : 0);
        } else {
          this.finish = words.length;
        }
      } else {
        this.start = Math.max(index - contextOptions.words, 0);
        this.finish = Math.min(index + contextOptions.words + 1, words.length);
      }

      this.words = words.slice(this.start, this.finish);
      this.indicies = [index - this.start];
    }
  };

  Section.prototype.append = function(other) {
    if (this.finish >= other.start) {
      this.words = this.words.slice(0, this.words.length - (this.finish - other.start));
    } else {
      this.words.push('...');
    }

    for (var i = 0, max = other.indicies; i < max; i++) {
      this.indicies.push(other.indicies[i] + this.words.length);
    }
    this.words = this.words.concat(other.words);
    this.finish = other.finish;
  };

  Section.prototype.join = function(other) {
    var newThis = this.deepCopy();
    newThis.append(other);
    return newThis;
  };

  Section.prototype.length = function() {
    var total = this.words.length - 1; // all the spaces
    for (var i = 0, max = this.words.length; i < max; i++) {
      total += this.words[i].length;
    }
    return total;
  };

  Section.prototype.toHTML = function() {
    var output = (this.start === 0 ? '' : '...');

    for (var i = 0, max = this.words.length; i < max; i++) {
      if (this.indicies.indexOf(i) > -1) {
        output += ' ' + escapeHtml(this.words[i]) + '';
      } else {
        output += ' ' + escapeHtml(this.words[i]);
      }
    }

    return output.trim() + ' ...';
  };

  Section.prototype.deepCopy = function() {
    var newSection = new Section();
    newSection.start = this.start;
    newSection.finish = this.finish;
    newSection.words = this.words.slice(0);
    newSection.indicies = this.indicies.slice(0);
    return newSection;
  };

  return Section;
})();
<commit_msg>Fix: was excluding extra word instead of including one<commit_after>var Section = (function() {
  var Section = function(index, words, contextOptions) {
    if (typeof index !== 'undefined') {
      if (contextOptions.regex) {
        var regex = {},
            include = {};

        if (contextOptions.regex.start && contextOptions.regex.finish) {
          ['start', 'finish'].forEach(function(el) {
            if (contextOptions.regex[el].hasOwnProperty('regex')) {
              regex[el] = contextOptions.regex[el].regex;
              include[el] = contextOptions.regex[el].include;
              include[el] = typeof include[el] === 'undefined' ? true : include[el];
            } else {
              regex[el] = contextOptions.regex[el];
              include[el] = true;
            }
          });
        } else if (contextOptions.regex.hasOwnProperty('regex')) {
          regex.start = regex.finish = contextOptions.regex.regex;
          include.start = contextOptions.regex.include;
          include.start = typeof include.start === 'undefined' ? true : include.start;
          include.finish = include.start;
        } else {
          regex.start = regex.finish = contextOptions.regex;
          include.start = include.finish = true;
        }

        var tmpIndex = words.slice(0, index).reverse().findIndex(function(el) {return el.match(regex.start);});
        if (tmpIndex > -1) {
          this.start = index - tmpIndex - (include.start ? 1 : 0);
        } else {
          this.start = 0;
        }

        tmpIndex = words.slice(index + 1).findIndex(function(el) {return el.match(regex.finish);});
        if (tmpIndex > -1) {
          this.finish = index + 1 + tmpIndex + (include.finish ? 1 : 0);
        } else {
          this.finish = words.length;
        }
      } else {
        this.start = Math.max(index - contextOptions.words, 0);
        this.finish = Math.min(index + contextOptions.words + 1, words.length);
      }

      this.words = words.slice(this.start, this.finish);
      this.indicies = [index - this.start];
    }
  };

  Section.prototype.append = function(other) {
    if (this.finish >= other.start) {
      this.words = this.words.slice(0, this.words.length - (this.finish - other.start));
    } else {
      this.words.push('...');
    }

    for (var i = 0, max = other.indicies; i < max; i++) {
      this.indicies.push(other.indicies[i] + this.words.length);
    }
    this.words = this.words.concat(other.words);
    this.finish = other.finish;
  };

  Section.prototype.join = function(other) {
    var newThis = this.deepCopy();
    newThis.append(other);
    return newThis;
  };

  Section.prototype.length = function() {
    var total = this.words.length - 1; // all the spaces
    for (var i = 0, max = this.words.length; i < max; i++) {
      total += this.words[i].length;
    }
    return total;
  };

  Section.prototype.toHTML = function() {
    var output = (this.start === 0 ? '' : '...');

    for (var i = 0, max = this.words.length; i < max; i++) {
      if (this.indicies.indexOf(i) > -1) {
        output += ' ' + escapeHtml(this.words[i]) + '';
      } else {
        output += ' ' + escapeHtml(this.words[i]);
      }
    }

    return output.trim() + ' ...';
  };

  Section.prototype.deepCopy = function() {
    var newSection = new Section();
    newSection.start = this.start;
    newSection.finish = this.finish;
    newSection.words = this.words.slice(0);
    newSection.indicies = this.indicies.slice(0);
    return newSection;
  };

  return Section;
})();
<|endoftext|>


<commit_before>var storage = require('./storage.js');
var ADC = require('adc-pi-gpio');

var lightSensorChannel = 0,
  tempSensorChannel = 1;
var analogSensors;

function initADC() {
  var adcConfig = {
    tolerance : 2,
		interval : 300,
		channels : [
      lightSensorChannel,
      tempSensorChannel
    ],
		SPICLK: 12,
		SPIMISO: 16,
		SPIMOSI: 18,
		SPICS: 22
  };

  analogSensors = new ADC(adcConfig);
  analogSensors.init();

  // ADC Events
  analogSensors.on('ready', function() {
      console.log('Analog Pins ready, listening to channel...');
  });

  analogSensors.on('close', function() {
  	console.log('ADC terminated');
  	process.exit();
  });
}

function tearDown() {
  analogSensors.close();
}

var sensors = {
  init: initADC,
  readLightSensor: function () {
    analogSensors.read(lightSensorChannel, function(value) {
      console.log('lightValue is: ' +  value);

      return value;
    });
  },
  tearDown: tearDown
};

// Exports
module.exports = sensors;
<commit_msg>updating sensor logic<commit_after>var storage = require('./storage.js');
var ADC = require('adc-pi-gpio');

var lightSensorChannel = 0,
  tempSensorChannel = 1;
var analogSensors;

function initADC() {
  var adcConfig = {
    tolerance : 2,
		interval : 300,
		channels : [
      lightSensorChannel,
      tempSensorChannel
    ],
		SPICLK: 23,
		SPIMISO: 21,
		SPIMOSI: 19,
		SPICS: 24
  };

  analogSensors = new ADC(adcConfig);
  analogSensors.init();

  // ADC Events
  analogSensors.on('ready', function() {
      console.log('Analog Pins ready, listening to channel...');
  });

  analogSensors.on('close', function() {
  	console.log('ADC terminated');
  	process.exit();
  });
}

function tearDown() {
  analogSensors.close();
}

var sensors = {
  init: initADC,
  readLightSensor: function () {
    analogSensors.read(lightSensorChannel, function(value) {
      console.log('lightValue is: ' +  value);

      return value;
    });
  },
  tearDown: tearDown
};

// Exports
module.exports = sensors;
<|endoftext|>


<commit_before>import { NativeModules, DeviceEventEmitter } from 'react-native';
import Rx from 'rxjs/Rx';
const { Gyroscope: GyroNative, Accelerometer: AccNative } = NativeModules;

const handle = {
  Accelerometer: AccNative,
  Gyroscope: GyroNative,
};

const RNSensors = {
  start: function (type, updateInterval) {
    const api = handle[type];
    api.setUpdateInterval(updateInterval);
    // A promise is returned in Android, since it can fail with an exception
    return api.startUpdates() || Promise.resolve();
  },

  stop: function (type) {
    const api = handle[type];
    api.stopUpdates();
  },
};

function createSensorMonitorCreator(sensorType) {
  function Creator(options = {}) {
    const {
      updateInterval = 100, // time in ms
    } = (options || {});

    let observer;

    // Instanciate observable
    const observable = Rx.Observable.create(function (obs) {
      observer = obs;
      // Start the sensor manager
      RNSensors.start(sensorType, updateInterval).then(() => {
        DeviceEventEmitter.addListener(sensorType, function(data) {
          observer.next(data);
        });
      }, (error) => {
        observer.error(error)
      });
    });

    // Stop the sensor manager
    observable.stop = () => {
      RNSensors.stop(sensorType);
      observer.complete();
    };

    return observable;
  }

  return Creator;
}

// TODO: lazily intialize them (maybe via getter)
const Accelerometer = createSensorMonitorCreator('Accelerometer');
const Gyroscope = createSensorMonitorCreator('Gyroscope');
const Magnetometer = createSensorMonitorCreator('Magnetometer');

export default {
  Accelerometer,
  Gyroscope,
};
<commit_msg>Fix import issue<commit_after>import { NativeModules, DeviceEventEmitter } from 'react-native';
import * as Rx from 'rxjs/Rx';
const { Gyroscope: GyroNative, Accelerometer: AccNative } = NativeModules;

const handle = {
  Accelerometer: AccNative,
  Gyroscope: GyroNative,
};

const RNSensors = {
  start: function (type, updateInterval) {
    const api = handle[type];
    api.setUpdateInterval(updateInterval);
    // A promise is returned in Android, since it can fail with an exception
    return api.startUpdates() || Promise.resolve();
  },

  stop: function (type) {
    const api = handle[type];
    api.stopUpdates();
  },
};

function createSensorMonitorCreator(sensorType) {
  function Creator(options = {}) {
    const {
      updateInterval = 100, // time in ms
    } = (options || {});

    let observer;

    // Instanciate observable
    const observable = Rx.Observable.create(function (obs) {
      observer = obs;
      // Start the sensor manager
      RNSensors.start(sensorType, updateInterval).then(() => {
        DeviceEventEmitter.addListener(sensorType, function(data) {
          observer.next(data);
        });
      }, (error) => {
        observer.error(error)
      });
    });

    // Stop the sensor manager
    observable.stop = () => {
      RNSensors.stop(sensorType);
      observer.complete();
    };

    return observable;
  }

  return Creator;
}

// TODO: lazily intialize them (maybe via getter)
const Accelerometer = createSensorMonitorCreator('Accelerometer');
const Gyroscope = createSensorMonitorCreator('Gyroscope');
const Magnetometer = createSensorMonitorCreator('Magnetometer');

export default {
  Accelerometer,
  Gyroscope,
};
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2015-2016 InSeven Limited.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

var update = require('react-addons-update')
var values = require('./lib/values')
var parse_message = require('./lib/parse-message')

var Express = require('express'),
    path = require('path'),
    HTTP = require('http'),
    SocketIO = require('socket.io'),
    fs = require('fs'),
    busboyBodyParser = require('busboy-body-parser'),
    busboy = require('connect-busboy'),
    gm = require('gm'),
    uuid = require('node-uuid'),
    exec = require('child_process').exec,
    util = require('util'),
    gravatar = require('nodejs-gravatar');

var app = Express(),
    server = HTTP.Server(app),
    io = SocketIO(server);

state = {
  title: "Example Meeting",
  items: [],
  selection: undefined,
  users: {},
  offer: undefined,
  answer: undefined,
}

const uploadDir = __dirname + '/static/uploads/';

// Support parsing multipart/form-data.
app.use(busboy())

// Render static pages.
app.use(Express.static(path.join(__dirname, 'static')))

// Accept file uploads.
app.post('/upload', function(req, res) {
    var fstream
    req.pipe(req.busboy)
    req.busboy.on('file', function(fieldname, file, filename) {

        var uploadWithExtension = (extension) => {
            return __dirname + '/static/uploads/' + uuid.v4() + extension;
        };

        var extension = path.extname(filename);
        var uploadPath = uploadWithExtension(extension);

        var fstream = fs.createWriteStream(uploadPath);
        file.pipe(fstream);
        fstream.on('close', function() {

            var completion = (title, filename, cleanup) => {

                state.items.push({
                    uuid: uuid.v4(),
                    title: title,
                    url: "/#/viewer/" + path.basename(filename),
                    cleanup: cleanup
                });
                broadcastState();

            };

            if (extension == '.jpg' || extension == '.jpeg' || extension == '.png') {

                gm(uploadPath).autoOrient().write(uploadPath, function() {
                    completion(path.basename(filename, extension), uploadPath, () => {
                        fs.unlink(uploadPath, function(error) {
                            if (error) {
                                console.log(error);
                            }
                        });
                    });
                    res.sendStatus(200);
                });

            } else if (extension == '.pdf') {

                var thumbnailPath = uploadWithExtension('.jpg');
                var command = util.format(
                    'gs -dBATCH -dNOPAUSE -sDEVICE=jpeg -r200 -sOutputFile=%s %s',
                    thumbnailPath, uploadPath);
                exec(command, (error) => {

                    if (error) {
                        console.log("Encountered an error generating PDF preview.");
                        console.log(error);
                        return;
                    }

                    console.log(util.format("Generated thumbnail '%s'.", thumbnailPath));

                    completion(path.basename(filename, extension), thumbnailPath, () => {
                        fs.unlink(uploadPath, function(error) {
                            if (error) {
                                console.log(error);
                            }
                        });
                        fs.unlink(thumbnailPath, function(error) {
                            if (error) {
                                console.log(error);
                            }
                        });
                    });

                    res.sendStatus(200);

                });

            } else {
                console.log(util.format("Unsupported file with extension '%s'", extension));
                fs.unlink(uploadPath, function(error) {
                    if (error) {
                        console.log(error);
                    }
                });
            }

        })
    })
})

offerSocket = undefined;

io.emitJSON = function(message, parameters) {
  io.emit(message, JSON.stringify(parameters))
}

function broadcastState() {
  io.emitJSON('server-set-state', state)
}

io.on('connection', function(socket) {

    state.users[socket] = {
        uuid: uuid.v4(),
        name: 'Jason Morley',
        email: 'jason.morley@inseven.co.uk',
        avatar: gravatar.imageUrl('jason.morley@inseven.co.uk', { "size": "128" })
    }
    broadcastState();

  socket.on('disconnect', function() {

    delete state.users[socket];
    if (offerSocket == socket) {
      state.offer = undefined;
      state.answer = undefined;
    }
    broadcastState();

  }).on('client-set-user', parse_message(function(user) {

    state.users[socket].name = user.name;
    state.users[socket].email = user.email;
    broadcastState();

  })).on('client-add-item', parse_message(function(item) {

    item.uuid = uuid.v4();
    state.items.push(item);
    broadcastState();

  })).on('client-remove-item', parse_message(function(message) {

    var item = state.items[0];
    if (item.cleanup) {
        item.cleanup();
    }
    state.items.splice(message.index, 1);
    broadcastState();

  })).on('client-set-selection', parse_message(function(message) {

    state.selection = message.index;
    broadcastState();

  })).on('client-call-add-ice-candidate', function(candidate) {

    socket.broadcast.emit('server-call-add-ice-candidate', candidate);

  }).on('client-call-set-offer', parse_message(function(offer) {

    state.offer = offer;
    offerSocket = socket;
    broadcastState();

  })).on('client-call-set-answer', parse_message(function(answer) {

    state.answer = answer;
    broadcastState();

  }));
});

server.listen(3000, function(){
    console.log('listening on *:3000');
});
<commit_msg>Slightly better guarding of the cleanup code.<commit_after>/*
 * Copyright (C) 2015-2016 InSeven Limited.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

var update = require('react-addons-update')
var values = require('./lib/values')
var parse_message = require('./lib/parse-message')

var Express = require('express'),
    path = require('path'),
    HTTP = require('http'),
    SocketIO = require('socket.io'),
    fs = require('fs'),
    busboyBodyParser = require('busboy-body-parser'),
    busboy = require('connect-busboy'),
    gm = require('gm'),
    uuid = require('node-uuid'),
    exec = require('child_process').exec,
    util = require('util'),
    gravatar = require('nodejs-gravatar');

var app = Express(),
    server = HTTP.Server(app),
    io = SocketIO(server);

state = {
  title: "Example Meeting",
  items: [],
  selection: undefined,
  users: {},
  offer: undefined,
  answer: undefined,
}

const uploadDir = __dirname + '/static/uploads/';

// Support parsing multipart/form-data.
app.use(busboy())

// Render static pages.
app.use(Express.static(path.join(__dirname, 'static')))

// Accept file uploads.
app.post('/upload', function(req, res) {
    var fstream
    req.pipe(req.busboy)
    req.busboy.on('file', function(fieldname, file, filename) {

        var uploadWithExtension = (extension) => {
            return __dirname + '/static/uploads/' + uuid.v4() + extension;
        };

        var extension = path.extname(filename);
        var uploadPath = uploadWithExtension(extension);

        var fstream = fs.createWriteStream(uploadPath);
        file.pipe(fstream);
        fstream.on('close', function() {

            var completion = (title, filename, cleanup) => {

                state.items.push({
                    uuid: uuid.v4(),
                    title: title,
                    url: "/#/viewer/" + path.basename(filename),
                    cleanup: cleanup
                });
                broadcastState();

            };

            if (extension == '.jpg' || extension == '.jpeg' || extension == '.png') {

                var imagePath = uploadPath;
                gm(uploadPath).autoOrient().write(uploadPath, function() {
                    completion(path.basename(filename, extension), uploadPath, () => {
                        fs.unlink(imagePath, function(error) {
                            if (error) {
                                console.log(error);
                            }
                        });
                    });
                    res.sendStatus(200);
                });

            } else if (extension == '.pdf') {

                var thumbnailPath = uploadWithExtension('.jpg');
                var command = util.format(
                    'gs -dBATCH -dNOPAUSE -sDEVICE=jpeg -r200 -sOutputFile=%s %s',
                    thumbnailPath, uploadPath);
                exec(command, (error) => {

                    if (error) {
                        console.log("Encountered an error generating PDF preview.");
                        console.log(error);
                        return;
                    }

                    console.log(util.format("Generated thumbnail '%s'.", thumbnailPath));

                    completion(path.basename(filename, extension), thumbnailPath, () => {
                        fs.unlink(uploadPath, function(error) {
                            if (error) {
                                console.log(error);
                            }
                        });
                        fs.unlink(thumbnailPath, function(error) {
                            if (error) {
                                console.log(error);
                            }
                        });
                    });

                    res.sendStatus(200);

                });

            } else {
                console.log(util.format("Unsupported file with extension '%s'", extension));
                fs.unlink(uploadPath, function(error) {
                    if (error) {
                        console.log(error);
                    }
                });
            }

        })
    })
})

offerSocket = undefined;

io.emitJSON = function(message, parameters) {
  io.emit(message, JSON.stringify(parameters))
}

function broadcastState() {
  io.emitJSON('server-set-state', state)
}

io.on('connection', function(socket) {

    state.users[socket] = {
        uuid: uuid.v4(),
        name: 'Jason Morley',
        email: 'jason.morley@inseven.co.uk',
        avatar: gravatar.imageUrl('jason.morley@inseven.co.uk', { "size": "128" })
    }
    broadcastState();

  socket.on('disconnect', function() {

    delete state.users[socket];
    if (offerSocket == socket) {
      state.offer = undefined;
      state.answer = undefined;
    }
    broadcastState();

  }).on('client-set-user', parse_message(function(user) {

    state.users[socket].name = user.name;
    state.users[socket].email = user.email;
    broadcastState();

  })).on('client-add-item', parse_message(function(item) {

    item.uuid = uuid.v4();
    state.items.push(item);
    broadcastState();

  })).on('client-remove-item', parse_message(function(message) {

    var item = state.items[0];
    if (item.cleanup) {
        item.cleanup();
    }
    state.items.splice(message.index, 1);
    broadcastState();

  })).on('client-set-selection', parse_message(function(message) {

    state.selection = message.index;
    broadcastState();

  })).on('client-call-add-ice-candidate', function(candidate) {

    socket.broadcast.emit('server-call-add-ice-candidate', candidate);

  }).on('client-call-set-offer', parse_message(function(offer) {

    state.offer = offer;
    offerSocket = socket;
    broadcastState();

  })).on('client-call-set-answer', parse_message(function(answer) {

    state.answer = answer;
    broadcastState();

  }));
});

server.listen(3000, function(){
    console.log('listening on *:3000');
});
<|endoftext|>


<commit_before>var Promise = require('bluebird');
var google = require('googleapis');

/**
 * Renews authentication session to BigQuery.
 * @param {Function} callback
 * @return {Promise}
 */
exports.renew = function (callback) {
  var resolver = function (resolve, reject) {
    google.auth.refreshAccessToken(function (err) {
      if (err) return reject(err);
      resolve();
    });
  };

  return new Promise(resolver).nodeify(callback);
};
<commit_msg>Reference the auth object under _options<commit_after>var Promise = require('bluebird');
var google = require('googleapis');

/**
 * Renews authentication session to BigQuery.
 * @param {Function} callback
 * @return {Promise}
 */
exports.renew = function (callback) {
  var resolver = function (resolve, reject) {
    google._options.auth.refreshAccessToken(function (err) {
      if (err) return reject(err);
      resolve();
    });
  };

  return new Promise(resolver).nodeify(callback);
};
<|endoftext|>


<commit_before>(function (root) {

  'use strict';

  const SMOOTHY = {};

  SMOOTHY.init = (options = {}) => {
    // Use options or the defaults:
    const callback = options.callback;
    const easing = options.easing || 'linear';
    const offset = options.offset || 0;
    const speed = options.speed || 1000;
    const time = options.time || 500;
    const type = options.type || 'speed';

    // API:
    const settings = {
      callback: callback,
      easing: easing,
      offset: offset,
      speed: speed,
      time: time,
      type: type
    };

    // Init the scroll function:
    SMOOTHY.scroll(settings);
  };

  SMOOTHY.scroll = (settings) => {
    // Private variable cache:
    let distance;
    let duration;
    let elapsedTime;
    let element;
    let elementId;
    let link;
    let next;
    let start;
    let startTime;
    let stop;

    // Start listen for click event:
    document.body.addEventListener('click', delegate, false);

    // Delegate the event handler if the event target match a link on the page:
    function delegate (event) {
      if (!linkInPage(event.target)) {
        return;
      } else {
        event.preventDefault();
        event.stopPropagation();

        // Get element id:
        link = event.target.attributes.href.value.toString();
        elementId = link.substr(link.lastIndexOf('#') + 1);

        // Get values for the next scroll:
        if (element = document.getElementById(elementId)) {
          start = pageOffset();
          stop = top(element, start);
          distance = stop - start - settings.offset;

          if (settings.type === 'speed') {
            duration = Math.abs(distance / settings.speed) * 1000;
          } else if (settings.type === 'time') {
            duration = time;
          }
        }

        // Start render:
        window.requestAnimationFrame(function (currentTime) {
          startTime = currentTime;
          render(currentTime);
        });
      }
    };

    // rAF render helper:
    function render (currentTime) {
      // Store time scroll started, if not started already:
      if (!startTime) {
        startTime = currentTime;
      }

      // Determine time spent scrolling so far:
      elapsedTime = currentTime - startTime;

      // Calculate next scroll position:
      next = easing(elapsedTime, start, distance, duration, settings);

      // Scroll to it:
      window.scrollTo(0, next);

      // Check progress:
      elapsedTime < duration
        ? window.requestAnimationFrame(render)
        : done(start, startTime, distance, settings);
    };

    // Scroll finished helper:
    function done (start, startTime, distance, settings) {
      window.scrollTo(0, start + distance);

      // If it exists, run the callback:
      if (typeof settings.callback === 'function') {
        settings.callback();
      }

      // Reset time for next render:
      startTime = false;
    };
  };

  // Get page offset position:
  function pageOffset () {
    return window.scrollY || window.pageYOffset;
  };

  // Get element offset:
  function top (element, start) {
    return element.getBoundingClientRect().top + start;
  };

  // Internal page link check:
  function linkInPage (link) {
    const pageUrl = location.hash
      ? stripHash(location.href)
      : location.href;

    return link.tagName.toLowerCase() === 'a'
      && link.hash.length > 0
      && stripHash(link.href) === pageUrl;
  }

  // Remove the hash and the trailing content from the url:
  function stripHash (url) {
    return url.slice(0, url.lastIndexOf('#'));
  }

  // A collection of easing patterns:
  function easing (time, start, distance, duration, settings) {
    switch (settings.easing) {
    case 'linear':
      return distance * time / duration + start;
      break;

    case 'easeInOutQuad':
      time = time / (duration / 2);
      if (time < 1) {
        return distance / 2 * time * time + start;
      } else {
        time--;
        return -distance / 2 * (time * (time - 2) - 1) + start;
      }
      break;

    case 'easeInOutCubic':
      time = time / (duration / 2);
      if (time < 1) {
        return distance / 2 * time * time * time + start;
      } else {
        time -= 2;
        return distance / 2 * (time * time * time + 2) + start;
      }
      break;
    }
  };

  // Expose smoothy for the global scope:
  root.smoothy = SMOOTHY;

})(window);
<commit_msg>Bugfix for missing settings reference when using time as type for the scroll<commit_after>(function (root) {

  'use strict';

  const SMOOTHY = {};

  SMOOTHY.init = (options = {}) => {
    // Use options or the defaults:
    const callback = options.callback;
    const easing = options.easing || 'linear';
    const offset = options.offset || 0;
    const speed = options.speed || 1000;
    const time = options.time || 500;
    const type = options.type || 'speed';

    // API:
    const settings = {
      callback: callback,
      easing: easing,
      offset: offset,
      speed: speed,
      time: time,
      type: type
    };

    // Init the scroll function:
    SMOOTHY.scroll(settings);
  };

  SMOOTHY.scroll = (settings) => {
    // Private variable cache:
    let distance;
    let duration;
    let elapsedTime;
    let element;
    let elementId;
    let link;
    let next;
    let start;
    let startTime;
    let stop;

    // Start listen for click event:
    document.body.addEventListener('click', delegate, false);

    // Delegate the event handler if the event target match a link on the page:
    function delegate (event) {
      if (!linkInPage(event.target)) {
        return;
      } else {
        event.preventDefault();
        event.stopPropagation();

        // Get element id:
        link = event.target.attributes.href.value.toString();
        elementId = link.substr(link.lastIndexOf('#') + 1);

        // Get values for the next scroll:
        if (element = document.getElementById(elementId)) {
          start = pageOffset();
          stop = top(element, start);
          distance = stop - start - settings.offset;

          if (settings.type === 'speed') {
            duration = Math.abs(distance / settings.speed) * 1000;
          } else if (settings.type === 'time') {
            duration = settings.time;
          }
        }

        // Start render:
        window.requestAnimationFrame(function (currentTime) {
          startTime = currentTime;
          render(currentTime);
        });
      }
    };

    // rAF render helper:
    function render (currentTime) {
      // Store time scroll started, if not started already:
      if (!startTime) {
        startTime = currentTime;
      }

      // Determine time spent scrolling so far:
      elapsedTime = currentTime - startTime;

      // Calculate next scroll position:
      next = easing(elapsedTime, start, distance, duration, settings);

      // Scroll to it:
      window.scrollTo(0, next);

      // Check progress:
      elapsedTime < duration
        ? window.requestAnimationFrame(render)
        : done(start, startTime, distance, settings);
    };

    // Scroll finished helper:
    function done (start, startTime, distance, settings) {
      window.scrollTo(0, start + distance);

      // If it exists, run the callback:
      if (typeof settings.callback === 'function') {
        settings.callback();
      }

      // Reset time for next render:
      startTime = false;
    };
  };

  // Get page offset position:
  function pageOffset () {
    return window.scrollY || window.pageYOffset;
  };

  // Get element offset:
  function top (element, start) {
    return element.getBoundingClientRect().top + start;
  };

  // Internal page link check:
  function linkInPage (link) {
    const pageUrl = location.hash
      ? stripHash(location.href)
      : location.href;

    return link.tagName.toLowerCase() === 'a'
      && link.hash.length > 0
      && stripHash(link.href) === pageUrl;
  }

  // Remove the hash and the trailing content from the url:
  function stripHash (url) {
    return url.slice(0, url.lastIndexOf('#'));
  }

  // A collection of easing patterns:
  function easing (time, start, distance, duration, settings) {
    switch (settings.easing) {
    case 'linear':
      return distance * time / duration + start;
      break;

    case 'easeInOutQuad':
      time = time / (duration / 2);
      if (time < 1) {
        return distance / 2 * time * time + start;
      } else {
        time--;
        return -distance / 2 * (time * (time - 2) - 1) + start;
      }
      break;

    case 'easeInOutCubic':
      time = time / (duration / 2);
      if (time < 1) {
        return distance / 2 * time * time * time + start;
      } else {
        time -= 2;
        return distance / 2 * (time * time * time + 2) + start;
      }
      break;
    }
  };

  // Expose smoothy for the global scope:
  root.smoothy = SMOOTHY;

})(window);
<|endoftext|>


<commit_before>/* snapper css snap points carousel */
;(function( w, $ ){
	var pluginName = "snapper";
	$.fn[ pluginName ] = function(optionsOrMethod){
		var pluginArgs = arguments;
		var snapForbidden = false;

		// css snap points feature test.
		// even if this test passes, several behaviors will still be polyfilled, such as snapping after resize, and animated advancing of slides with anchor links or next/prev links
		var testProp = "scroll-snap-type";
		var snapSupported = w.CSS && w.CSS.supports && ( w.CSS.supports( testProp, "mandatory") || w.CSS.supports("-webkit-" + testProp, "mandatory")	 || w.CSS.supports("-ms-" + testProp, "mandatory") );

		// get the snapper_item elements whose left offsets fall within the scroll pane. Returns a wrapped array.
		function itemsAtOffset( elem, offset ){
			var $childNodes = $( elem ).find( "." + pluginName + "_item" );
			var containWidth = $( elem ).width();
			var activeItems = [];

			$childNodes.each(function( i ){
				if( this.offsetLeft >= offset - 5 && this.offsetLeft < offset + containWidth - 5 ){
					activeItems.push( this );
				}
			});
			return $( activeItems );
		}

		function outerWidth( $elem ){
			return $elem.width() + parseFloat( $elem.css( "margin-left" ) ) + parseFloat( $elem.css( "margin-right" ) );
		}

		function outerHeight( $elem ){
			return $elem.height() + parseFloat( $elem.css( "margin-bottom" ) ) + parseFloat( $elem.css( "margin-top" ) );
		}

		// snapEvent dispatches the "snapper.snap" event.
		// The snapper_item elements with left offsets that are inside the scroll viewport are listed in an array in the second callback argument's activeSlides property.
		// use like this: $( ".snapper" ).bind( "snapper.snap", function( event, data ){ console.log( data.activeSlides );	} );
		function snapEvent( elem, x, prefix ){
			prefix = prefix ? prefix + "-" : "";
			var activeSlides = itemsAtOffset( elem, x );
			$( elem ).trigger( pluginName + "." + prefix + "snap", { activeSlides: activeSlides } );
		}

		// optional: include toss() in your page to get a smooth scroll, otherwise it'll just jump to the slide
		function goto( elem, x, nothrow, callback ){
			snapEvent( elem, x );

			var after = function(){
				$( elem ).trigger( pluginName + ".after-goto", {
					activeSlides: itemsAtOffset( elem, x )
				});

				// resume snapping on scroll
				snapForbidden = false;
				if( callback ){ callback(); };
				snapEvent( elem, x, "after" );
			};

			// disable snapping on scroll since we're going to scroll right now
			// programatically using toss where available
			snapForbidden = true;

			// backport to old toss for compat
			if( !w.toss && w.overthrow ){
				w.toss = w.overthrow.toss;
			}
			if( typeof w.toss !== "undefined" && !nothrow ){
				w.toss( elem, { left: x, finished: after });
			}
			else {
				elem.scrollLeft = x;
				after();
			}
		}

		var result, innerResult;

		// Loop through snapper elements and enhance/bind events
		result = this.each(function(){
			if( innerResult !== undefined ){
				return;
			}

			var self = this;
			var $self = $( self );
			var addNextPrev = $self.is( "[data-" + pluginName + "-nextprev]" );
			var autoTiming =
				$self.attr( "data-autoplay" ) || $self.attr( "data-snapper-autoplay" );
			var autoInterval;
			var $slider = $( "." + pluginName + "_pane", self );
			var enhancedClass = pluginName + "-enhanced";
			var $itemsContain = $slider.find( "." + pluginName + "_items" );
			var $items = $itemsContain.children();
			$items.addClass( pluginName + "_item" );
			var numItems = $items.length;
			var $nav = $( "." + pluginName + "_nav", self );
			var navSelectedClass = pluginName + "_nav_item-selected";
			var useDeepLinking = $self.attr( "data-snapper-deeplinking" ) !== "false";

			if( typeof optionsOrMethod === "string" ){
				var args = Array.prototype.slice.call(pluginArgs, 1);
				var index;
				var itemWidth = ( $itemsContain.width() / numItems );

				switch(optionsOrMethod) {
				case "goto":
					index = args[0] % numItems;

					// width / items * index to make sure it goes
					offset = itemWidth * index;
					goto( $slider[ 0 ], offset, false, function(){
						// snap the scroll to the right position
						snapScroll();

						// invoke the callback if it was supplied
						if( typeof args[1] === "function" ){
							args[1]();
						}
					});
					break;
				case "getIndex":
					innerResult = Math.floor($slider[ 0 ].scrollLeft / itemWidth);
					break;
				case "updateWidths":
					updateWidths();
					break;
				}

				return;
			}

			// NOTE all state manipulation has to come after method invocation to
			// avoid monkeying with the DOM when it's unwarranted
			var $navInner = $nav.find( "." + pluginName + "_nav_inner" );
			if( !$navInner.length ){
				$navInner = $( '

' ).append( $nav.children() ).appendTo( $nav );
			}

			// give the pane a tabindex for arrow key handling
			$slider.attr("tabindex", "0");

			// this function updates the widths of the items within the slider, and their container.
			// It factors in margins and converts those to values that make sense when all items are placed in a long row
			function updateWidths(){
				var itemsContainStyle = $itemsContain.attr( "style" );
				$itemsContain.attr( "style", "" );
				var itemStyle = $items.eq(0).attr( "style" );
				$items.eq(0).attr( "style", "" );
				var sliderWidth = $slider.width();
				var itemWidth = $items.eq(0).width();
				var computed = w.getComputedStyle( $items[ 0 ], null );
				var itemLeftMargin = parseFloat( computed.getPropertyValue( "margin-left" ) );
				var itemRightMargin = parseFloat( computed.getPropertyValue( "margin-right" ) );
				var outerItemWidth = itemWidth + itemLeftMargin + itemRightMargin;
				$items.eq(0).attr( "style", itemStyle );
				$itemsContain.attr( "style", itemsContainStyle );
				var parentWidth = numItems / Math.round(sliderWidth / outerItemWidth) * 100;
				var iPercentWidth = itemWidth / sliderWidth * 100;
				var iPercentRightMargin = itemRightMargin / sliderWidth * 100;
				var iPercentLeftMargin = itemLeftMargin / sliderWidth * 100;
				var outerPercentWidth = iPercentWidth + iPercentLeftMargin + iPercentRightMargin;
				var percentAsWidth = iPercentWidth / outerPercentWidth;
				var percentAsRightMargin = iPercentRightMargin / outerPercentWidth;
				var percentAsLeftMargin = iPercentLeftMargin / outerPercentWidth;
				$itemsContain.css( "width", parentWidth + "%");
				$items.css( "width", 100 / numItems * percentAsWidth + "%" );
				$items.css( "margin-left", 100 / numItems * percentAsLeftMargin + "%" );
				$items.css( "margin-right", 100 / numItems * percentAsRightMargin + "%" );
			}

			updateWidths();
			$( self ).addClass( enhancedClass );

			// if the nextprev option is set, add the nextprev nav
			if( addNextPrev ){
				var	$nextprev = $( '

Prev

Next

' );
				var $nextprevContain = $( ".snapper_nextprev_contain", self );
				if( !$nextprevContain.length ){
					$nextprevContain = $( self );
				}
				$nextprev.appendTo( $nextprevContain );
			}

			// This click binding will allow deep-linking to slides without causing the page to scroll to the carousel container
			// this also supports click handling for generated next/prev links
			$( "a", this ).bind( "click", function( e ){
				clearInterval(autoInterval);
				var slideID = $( this ).attr( "href" );

				if( $( this ).is( ".snapper_nextprev_next" ) ){
					e.preventDefault();
					return arrowNavigate( true );
				}
				else if( $( this ).is( ".snapper_nextprev_prev" ) ){
					e.preventDefault();
					return arrowNavigate( false );
				}
				// internal links to slides
				else if( slideID.indexOf( "#" ) === 0 && slideID.length > 1 ){
					e.preventDefault();

					var $slide = $( slideID, self );
					if( $slide.length ){
						goto( $slider[ 0 ], $slide[ 0 ].offsetLeft );
						if( useDeepLinking && "replaceState" in w.history ){
							w.history.replaceState( {}, document.title, slideID );
						}
					}
				}
			});

			// arrow key bindings for next/prev
			$( this )
				.bind( "keydown", function( e ){
					if( e.keyCode === 37 || e.keyCode === 38 ){
						clearInterval(autoInterval);
						e.preventDefault();
						e.stopImmediatePropagation();
						arrowNavigate( false );
					}
					if( e.keyCode === 39 || e.keyCode === 40 ){
						clearInterval(autoInterval);
						e.preventDefault();
						e.stopImmediatePropagation();
						arrowNavigate( true );
					}
				} );

			// snap to nearest slide. Useful after a scroll stops, for polyfilling snap points
			function snapScroll(){
				if(snapForbidden){
					return;
				}

				var currScroll = $slider[ 0 ].scrollLeft;
				var width = $itemsContain.width();
				var itemWidth = $items[ 1 ] ? $items[ 1 ].offsetLeft : outerWidth( $items.eq( 0 ) );
				var roundedScroll = Math.round(currScroll/itemWidth)*itemWidth;
				var maxScroll = width - $slider.width();
				if( roundedScroll > maxScroll ){
					roundedScroll = maxScroll;
				}
				if( currScroll !== roundedScroll ){
					if( snapSupported ){
						snapEvent( $slider[ 0 ], roundedScroll );
						snapEvent( elem, x, "after" );
					}
					else {
						goto( $slider[ 0 ], roundedScroll );
					}
				}
			}

			// retain snapping on resize (necessary even in scroll-snap supporting browsers currently, unfortunately)
			var startSlide;
			var afterResize;
			function snapStay(){
				var currScroll = $slider[ 0 ].scrollLeft;
				var numItems = $items.length;
				var width = $itemsContain.width();
				if( startSlide === undefined ){
					startSlide = Math.round( currScroll / width * numItems );
				}
				if( afterResize ){
					clearTimeout( afterResize );
				}
				afterResize = setTimeout( function(){
					updateWidths();
					goto( $slider[ 0 ], $items[ startSlide ].offsetLeft, true );
					startSlide = afterResize = undefined;
				}, 50 );
			}
			$( w ).bind( "resize", snapStay );

			// next/prev links or arrows should loop back to the other end when an extreme is reached
			function arrowNavigate( forward ){
				var currScroll = $slider[ 0 ].scrollLeft;
				var width = $itemsContain.width();
				var itemWidth = outerWidth( $slider );
				var maxScroll = width - itemWidth - 5;
				if( forward ){
					if( currScroll >= maxScroll ){
						return first();
					}
					else {
						return next();
					}
				}
				else {
					if( currScroll === 0 ){
						return last();
					}
					else {
						return prev();
					}
				}
			}

			// advance slide one full scrollpane's width forward
			function next(){
				goto( $slider[ 0 ], $slider[ 0 ].scrollLeft + ( $itemsContain.width() / numItems ), false, function(){
					$slider.trigger( pluginName + ".after-next" );
				});
			}

			// advance slide one full scrollpane's width backwards
			function prev(){
				goto( $slider[ 0 ], $slider[ 0 ].scrollLeft - ( $itemsContain.width() / numItems ), false, function(){
					$slider.trigger( pluginName + ".after-prev" );
				});
			}

			// go to first slide
			function first(){
				goto( $slider[ 0 ], 0 );
			}

			// go to last slide
			function last(){
				goto( $slider[ 0 ], $itemsContain.width() - $slider.width() );
			}

			// update thumbnail state on pane scroll
			if( $nav.length ){
				// function for scrolling to the xy of the active thumbnail
				function scrollNav(elem, x, y){
					if( typeof w.toss !== "undefined" ){
						w.toss( elem, { left: x, top:y });
					}
					else {
						elem.scrollLeft = x;
						elem.scrollTop = y;
					}
				}

				function activeItem(){
					var currScroll = $slider[ 0 ].scrollLeft;
					var width = outerWidth( $itemsContain );
					var navWidth = outerWidth( $nav );
					var navHeight = outerHeight( $nav );
					var activeIndex = Math.round( currScroll / width * numItems ) || 0;
					var childs = $nav.find( "a" ).removeClass( navSelectedClass );
					var activeChild = childs.eq( activeIndex ).addClass( navSelectedClass );

					var thumbX = activeChild[ 0 ].offsetLeft - (navWidth/2);
					var thumbY = activeChild[ 0 ].offsetTop - (navHeight/2);

					scrollNav( $navInner[ 0 ], thumbX, thumbY );
				}
				// set active item on scroll
				$slider.bind( "scroll", activeItem );
				// set active item on init
				activeItem();
			}

			// apply snapping after scroll, in browsers that don't support CSS scroll-snap
			var scrollStop;
			$slider.bind( "scroll", function(e){
				if( scrollStop ){
					clearTimeout( scrollStop );
				}
				scrollStop = setTimeout( snapScroll, 50 );
			});

			// if a touch event is fired on the snapper we know the user is trying to
			// interact with it and we should disable the auto play
			$self.bind("touchstart", function(){
				clearTimeout(autoInterval);
			});

			// if the `data-autotplay` attribute is assigned a natural number value
			// use it to make the slides cycle until there is a user interaction
			if(autoTiming){
				var parseError = false;

				try {
					autoTiming = parseInt(autoTiming, 10);
				} catch(e) {
					parseError = true;
				}

				// if NaN or there was an error throw an exception
				if( !autoTiming || parseError ) {
					var msg = "Snapper: `data-autoplay` must have an natural number value.";
					throw new Error(msg);
				}

				// autoInterval is cleared in each user interaction binding
				autoInterval = setInterval(function(){
					arrowNavigate(true);
				}, autoTiming);
			}
		});

		return (innerResult !== undefined ? innerResult : result);
	};
}( this, jQuery ));
<commit_msg>elem was undefined in snap supporting browsers<commit_after>/* snapper css snap points carousel */
;(function( w, $ ){
	var pluginName = "snapper";
	$.fn[ pluginName ] = function(optionsOrMethod){
		var pluginArgs = arguments;
		var snapForbidden = false;

		// css snap points feature test.
		// even if this test passes, several behaviors will still be polyfilled, such as snapping after resize, and animated advancing of slides with anchor links or next/prev links
		var testProp = "scroll-snap-type";
		var snapSupported = w.CSS && w.CSS.supports && ( w.CSS.supports( testProp, "mandatory") || w.CSS.supports("-webkit-" + testProp, "mandatory")	 || w.CSS.supports("-ms-" + testProp, "mandatory") );

		// get the snapper_item elements whose left offsets fall within the scroll pane. Returns a wrapped array.
		function itemsAtOffset( elem, offset ){
			var $childNodes = $( elem ).find( "." + pluginName + "_item" );
			var containWidth = $( elem ).width();
			var activeItems = [];

			$childNodes.each(function( i ){
				if( this.offsetLeft >= offset - 5 && this.offsetLeft < offset + containWidth - 5 ){
					activeItems.push( this );
				}
			});
			return $( activeItems );
		}

		function outerWidth( $elem ){
			return $elem.width() + parseFloat( $elem.css( "margin-left" ) ) + parseFloat( $elem.css( "margin-right" ) );
		}

		function outerHeight( $elem ){
			return $elem.height() + parseFloat( $elem.css( "margin-bottom" ) ) + parseFloat( $elem.css( "margin-top" ) );
		}

		// snapEvent dispatches the "snapper.snap" event.
		// The snapper_item elements with left offsets that are inside the scroll viewport are listed in an array in the second callback argument's activeSlides property.
		// use like this: $( ".snapper" ).bind( "snapper.snap", function( event, data ){ console.log( data.activeSlides );	} );
		function snapEvent( elem, x, prefix ){
			prefix = prefix ? prefix + "-" : "";
			var activeSlides = itemsAtOffset( elem, x );
			$( elem ).trigger( pluginName + "." + prefix + "snap", { activeSlides: activeSlides } );
		}

		// optional: include toss() in your page to get a smooth scroll, otherwise it'll just jump to the slide
		function goto( elem, x, nothrow, callback ){
			snapEvent( elem, x );

			var after = function(){
				$( elem ).trigger( pluginName + ".after-goto", {
					activeSlides: itemsAtOffset( elem, x )
				});

				// resume snapping on scroll
				snapForbidden = false;
				if( callback ){ callback(); };
				snapEvent( elem, x, "after" );
			};

			// disable snapping on scroll since we're going to scroll right now
			// programatically using toss where available
			snapForbidden = true;

			// backport to old toss for compat
			if( !w.toss && w.overthrow ){
				w.toss = w.overthrow.toss;
			}
			if( typeof w.toss !== "undefined" && !nothrow ){
				w.toss( elem, { left: x, finished: after });
			}
			else {
				elem.scrollLeft = x;
				after();
			}
		}

		var result, innerResult;

		// Loop through snapper elements and enhance/bind events
		result = this.each(function(){
			if( innerResult !== undefined ){
				return;
			}

			var self = this;
			var $self = $( self );
			var addNextPrev = $self.is( "[data-" + pluginName + "-nextprev]" );
			var autoTiming =
				$self.attr( "data-autoplay" ) || $self.attr( "data-snapper-autoplay" );
			var autoInterval;
			var $slider = $( "." + pluginName + "_pane", self );
			var enhancedClass = pluginName + "-enhanced";
			var $itemsContain = $slider.find( "." + pluginName + "_items" );
			var $items = $itemsContain.children();
			$items.addClass( pluginName + "_item" );
			var numItems = $items.length;
			var $nav = $( "." + pluginName + "_nav", self );
			var navSelectedClass = pluginName + "_nav_item-selected";
			var useDeepLinking = $self.attr( "data-snapper-deeplinking" ) !== "false";

			if( typeof optionsOrMethod === "string" ){
				var args = Array.prototype.slice.call(pluginArgs, 1);
				var index;
				var itemWidth = ( $itemsContain.width() / numItems );

				switch(optionsOrMethod) {
				case "goto":
					index = args[0] % numItems;

					// width / items * index to make sure it goes
					offset = itemWidth * index;
					goto( $slider[ 0 ], offset, false, function(){
						// snap the scroll to the right position
						snapScroll();

						// invoke the callback if it was supplied
						if( typeof args[1] === "function" ){
							args[1]();
						}
					});
					break;
				case "getIndex":
					innerResult = Math.floor($slider[ 0 ].scrollLeft / itemWidth);
					break;
				case "updateWidths":
					updateWidths();
					break;
				}

				return;
			}

			// NOTE all state manipulation has to come after method invocation to
			// avoid monkeying with the DOM when it's unwarranted
			var $navInner = $nav.find( "." + pluginName + "_nav_inner" );
			if( !$navInner.length ){
				$navInner = $( '

' ).append( $nav.children() ).appendTo( $nav );
			}

			// give the pane a tabindex for arrow key handling
			$slider.attr("tabindex", "0");

			// this function updates the widths of the items within the slider, and their container.
			// It factors in margins and converts those to values that make sense when all items are placed in a long row
			function updateWidths(){
				var itemsContainStyle = $itemsContain.attr( "style" );
				$itemsContain.attr( "style", "" );
				var itemStyle = $items.eq(0).attr( "style" );
				$items.eq(0).attr( "style", "" );
				var sliderWidth = $slider.width();
				var itemWidth = $items.eq(0).width();
				var computed = w.getComputedStyle( $items[ 0 ], null );
				var itemLeftMargin = parseFloat( computed.getPropertyValue( "margin-left" ) );
				var itemRightMargin = parseFloat( computed.getPropertyValue( "margin-right" ) );
				var outerItemWidth = itemWidth + itemLeftMargin + itemRightMargin;
				$items.eq(0).attr( "style", itemStyle );
				$itemsContain.attr( "style", itemsContainStyle );
				var parentWidth = numItems / Math.round(sliderWidth / outerItemWidth) * 100;
				var iPercentWidth = itemWidth / sliderWidth * 100;
				var iPercentRightMargin = itemRightMargin / sliderWidth * 100;
				var iPercentLeftMargin = itemLeftMargin / sliderWidth * 100;
				var outerPercentWidth = iPercentWidth + iPercentLeftMargin + iPercentRightMargin;
				var percentAsWidth = iPercentWidth / outerPercentWidth;
				var percentAsRightMargin = iPercentRightMargin / outerPercentWidth;
				var percentAsLeftMargin = iPercentLeftMargin / outerPercentWidth;
				$itemsContain.css( "width", parentWidth + "%");
				$items.css( "width", 100 / numItems * percentAsWidth + "%" );
				$items.css( "margin-left", 100 / numItems * percentAsLeftMargin + "%" );
				$items.css( "margin-right", 100 / numItems * percentAsRightMargin + "%" );
			}

			updateWidths();
			$( self ).addClass( enhancedClass );

			// if the nextprev option is set, add the nextprev nav
			if( addNextPrev ){
				var	$nextprev = $( '

Prev

Next

' );
				var $nextprevContain = $( ".snapper_nextprev_contain", self );
				if( !$nextprevContain.length ){
					$nextprevContain = $( self );
				}
				$nextprev.appendTo( $nextprevContain );
			}

			// This click binding will allow deep-linking to slides without causing the page to scroll to the carousel container
			// this also supports click handling for generated next/prev links
			$( "a", this ).bind( "click", function( e ){
				clearInterval(autoInterval);
				var slideID = $( this ).attr( "href" );

				if( $( this ).is( ".snapper_nextprev_next" ) ){
					e.preventDefault();
					return arrowNavigate( true );
				}
				else if( $( this ).is( ".snapper_nextprev_prev" ) ){
					e.preventDefault();
					return arrowNavigate( false );
				}
				// internal links to slides
				else if( slideID.indexOf( "#" ) === 0 && slideID.length > 1 ){
					e.preventDefault();

					var $slide = $( slideID, self );
					if( $slide.length ){
						goto( $slider[ 0 ], $slide[ 0 ].offsetLeft );
						if( useDeepLinking && "replaceState" in w.history ){
							w.history.replaceState( {}, document.title, slideID );
						}
					}
				}
			});

			// arrow key bindings for next/prev
			$( this )
				.bind( "keydown", function( e ){
					if( e.keyCode === 37 || e.keyCode === 38 ){
						clearInterval(autoInterval);
						e.preventDefault();
						e.stopImmediatePropagation();
						arrowNavigate( false );
					}
					if( e.keyCode === 39 || e.keyCode === 40 ){
						clearInterval(autoInterval);
						e.preventDefault();
						e.stopImmediatePropagation();
						arrowNavigate( true );
					}
				} );

			// snap to nearest slide. Useful after a scroll stops, for polyfilling snap points
			function snapScroll(){
				if(snapForbidden){
					return;
				}

				var currScroll = $slider[ 0 ].scrollLeft;
				var width = $itemsContain.width();
				var itemWidth = $items[ 1 ] ? $items[ 1 ].offsetLeft : outerWidth( $items.eq( 0 ) );
				var roundedScroll = Math.round(currScroll/itemWidth)*itemWidth;
				var maxScroll = width - $slider.width();
				if( roundedScroll > maxScroll ){
					roundedScroll = maxScroll;
				}
				if( currScroll !== roundedScroll ){
					if( snapSupported ){
						snapEvent( $slider[ 0 ], roundedScroll );
						snapEvent( $slider[ 0 ], x, "after" );
					}
					else {
						goto( $slider[ 0 ], roundedScroll );
					}
				}
			}

			// retain snapping on resize (necessary even in scroll-snap supporting browsers currently, unfortunately)
			var startSlide;
			var afterResize;
			function snapStay(){
				var currScroll = $slider[ 0 ].scrollLeft;
				var numItems = $items.length;
				var width = $itemsContain.width();
				if( startSlide === undefined ){
					startSlide = Math.round( currScroll / width * numItems );
				}
				if( afterResize ){
					clearTimeout( afterResize );
				}
				afterResize = setTimeout( function(){
					updateWidths();
					goto( $slider[ 0 ], $items[ startSlide ].offsetLeft, true );
					startSlide = afterResize = undefined;
				}, 50 );
			}
			$( w ).bind( "resize", snapStay );

			// next/prev links or arrows should loop back to the other end when an extreme is reached
			function arrowNavigate( forward ){
				var currScroll = $slider[ 0 ].scrollLeft;
				var width = $itemsContain.width();
				var itemWidth = outerWidth( $slider );
				var maxScroll = width - itemWidth - 5;
				if( forward ){
					if( currScroll >= maxScroll ){
						return first();
					}
					else {
						return next();
					}
				}
				else {
					if( currScroll === 0 ){
						return last();
					}
					else {
						return prev();
					}
				}
			}

			// advance slide one full scrollpane's width forward
			function next(){
				goto( $slider[ 0 ], $slider[ 0 ].scrollLeft + ( $itemsContain.width() / numItems ), false, function(){
					$slider.trigger( pluginName + ".after-next" );
				});
			}

			// advance slide one full scrollpane's width backwards
			function prev(){
				goto( $slider[ 0 ], $slider[ 0 ].scrollLeft - ( $itemsContain.width() / numItems ), false, function(){
					$slider.trigger( pluginName + ".after-prev" );
				});
			}

			// go to first slide
			function first(){
				goto( $slider[ 0 ], 0 );
			}

			// go to last slide
			function last(){
				goto( $slider[ 0 ], $itemsContain.width() - $slider.width() );
			}

			// update thumbnail state on pane scroll
			if( $nav.length ){
				// function for scrolling to the xy of the active thumbnail
				function scrollNav(elem, x, y){
					if( typeof w.toss !== "undefined" ){
						w.toss( elem, { left: x, top:y });
					}
					else {
						elem.scrollLeft = x;
						elem.scrollTop = y;
					}
				}

				function activeItem(){
					var currScroll = $slider[ 0 ].scrollLeft;
					var width = outerWidth( $itemsContain );
					var navWidth = outerWidth( $nav );
					var navHeight = outerHeight( $nav );
					var activeIndex = Math.round( currScroll / width * numItems ) || 0;
					var childs = $nav.find( "a" ).removeClass( navSelectedClass );
					var activeChild = childs.eq( activeIndex ).addClass( navSelectedClass );

					var thumbX = activeChild[ 0 ].offsetLeft - (navWidth/2);
					var thumbY = activeChild[ 0 ].offsetTop - (navHeight/2);

					scrollNav( $navInner[ 0 ], thumbX, thumbY );
				}
				// set active item on scroll
				$slider.bind( "scroll", activeItem );
				// set active item on init
				activeItem();
			}

			// apply snapping after scroll, in browsers that don't support CSS scroll-snap
			var scrollStop;
			$slider.bind( "scroll", function(e){
				if( scrollStop ){
					clearTimeout( scrollStop );
				}
				scrollStop = setTimeout( snapScroll, 50 );
			});

			// if a touch event is fired on the snapper we know the user is trying to
			// interact with it and we should disable the auto play
			$self.bind("touchstart", function(){
				clearTimeout(autoInterval);
			});

			// if the `data-autotplay` attribute is assigned a natural number value
			// use it to make the slides cycle until there is a user interaction
			if(autoTiming){
				var parseError = false;

				try {
					autoTiming = parseInt(autoTiming, 10);
				} catch(e) {
					parseError = true;
				}

				// if NaN or there was an error throw an exception
				if( !autoTiming || parseError ) {
					var msg = "Snapper: `data-autoplay` must have an natural number value.";
					throw new Error(msg);
				}

				// autoInterval is cleared in each user interaction binding
				autoInterval = setInterval(function(){
					arrowNavigate(true);
				}, autoTiming);
			}
		});

		return (innerResult !== undefined ? innerResult : result);
	};
}( this, jQuery ));
<|endoftext|>


<commit_before>/*
 * Fuel UX Spinner
 * https://github.com/ExactTarget/fuelux
 *
 * Copyright (c) 2012 ExactTarget
 * Licensed under the MIT license.
 */

define(function(require) {

	var $   = require('jquery');
	var old = $.fn.spinner;

	// SPINNER CONSTRUCTOR AND PROTOTYPE

	var Spinner = function (element, options) {
		this.$element = $(element);
		this.options = $.extend({}, $.fn.spinner.defaults, options);
		this.$input = this.$element.find('.spinner-input');
		this.$element.on('keyup', this.$input, $.proxy(this.change, this));

		if (this.options.hold) {
			this.$element.on('mousedown', '.spinner-up', $.proxy(function() { this.startSpin(true); } , this));
			this.$element.on('mouseup', '.spinner-up, .spinner-down', $.proxy(this.stopSpin, this));
			this.$element.on('mouseout', '.spinner-up, .spinner-down', $.proxy(this.stopSpin, this));
			this.$element.on('mousedown', '.spinner-down', $.proxy(function() {this.startSpin(false);} , this));
		} else {
			this.$element.on('click', '.spinner-up', $.proxy(function() { this.step(true); } , this));
			this.$element.on('click', '.spinner-down', $.proxy(function() { this.step(false); }, this));
		}

		this.switches = {
			count: 1,
			enabled: true
		};

		if (this.options.speed === 'medium') {
			this.switches.speed = 300;
		} else if (this.options.speed === 'fast') {
			this.switches.speed = 100;
		} else {
			this.switches.speed = 500;
		}

		this.lastValue = null;

		this.render();

		if (this.options.disabled) {
			this.disable();
		}
	};

	Spinner.prototype = {
		constructor: Spinner,

		render: function () {
			var inputValue = this.$input.val();

			if (inputValue) {
				this.value(inputValue);
			} else {
				this.$input.val(this.options.value);
			}

			this.$input.attr('maxlength', (this.options.max + '').split('').length);
		},

		change: function () {
			var newVal = this.$input.val();

			if(newVal/1){
				this.options.value = newVal/1;
			}else{
				newVal = newVal.replace(/[^0-9]/g,'') || '';
				this.$input.val(newVal);
				this.options.value = newVal/1;
			}

			this.triggerChangedEvent();
		},

		stopSpin: function () {
            if(this.switches.timeout!==undefined){
                clearTimeout(this.switches.timeout);
                this.switches.count = 1;
                this.triggerChangedEvent();
            }
		},

		triggerChangedEvent: function () {
			var currentValue = this.value();
			if (currentValue === this.lastValue) return;

			this.lastValue = currentValue;

			// Primary changed event
			this.$element.trigger('changed', currentValue);

			// Undocumented, kept for backward compatibility
			this.$element.trigger('change');
		},

		startSpin: function (type) {

			if (!this.options.disabled) {
				var divisor = this.switches.count;

				if (divisor === 1) {
					this.step(type);
					divisor = 1;
				} else if (divisor < 3){
					divisor = 1.5;
				} else if (divisor < 8){
					divisor = 2.5;
				} else {
					divisor = 4;
				}

				this.switches.timeout = setTimeout($.proxy(function() {this.iterator(type);} ,this),this.switches.speed/divisor);
				this.switches.count++;
			}
		},

		iterator: function (type) {
			this.step(type);
			this.startSpin(type);
		},

		step: function (dir) {
			var curValue = this.options.value;
			var limValue = dir ? this.options.max : this.options.min;

			if ((dir ? curValue < limValue : curValue > limValue)) {
				var newVal = curValue + (dir ? 1 : -1) * this.options.step;

				if (dir ? newVal > limValue : newVal < limValue) {
					this.value(limValue);
				} else {
					this.value(newVal);
				}
			} else if (this.options.cycle) {
				var cycleVal = dir ? this.options.min : this.options.max;
				this.value(cycleVal);
			}
		},

		value: function (value) {
			if (!isNaN(parseFloat(value)) && isFinite(value)) {
				value = parseFloat(value);
				this.options.value = value;
				this.$input.val(value);
				return this;
			} else {
				return this.options.value;
			}
		},

		disable: function () {
			this.options.disabled = true;
			this.$input.attr('disabled','');
			this.$element.find('button').addClass('disabled');
		},

		enable: function () {
			this.options.disabled = false;
			this.$input.removeAttr("disabled");
			this.$element.find('button').removeClass('disabled');
		}
	};

	// SPINNER PLUGIN DEFINITION

	$.fn.spinner = function (option) {
		var args = Array.prototype.slice.call( arguments, 1 );
		var methodReturn;

		var $set = this.each(function () {
			var $this   = $( this );
			var data    = $this.data( 'spinner' );
			var options = typeof option === 'object' && option;

			if( !data ) $this.data('spinner', (data = new Spinner( this, options ) ) );
			if( typeof option === 'string' ) methodReturn = data[ option ].apply( data, args );
		});

		return ( methodReturn === undefined ) ? $set : methodReturn;
	};

	$.fn.spinner.defaults = {
		value: 1,
		min: 1,
		max: 999,
		step: 1,
		hold: true,
		speed: 'medium',
		disabled: false
	};

	$.fn.spinner.Constructor = Spinner;

	$.fn.spinner.noConflict = function () {
		$.fn.spinner = old;
		return this;
	};

	// SPINNER DATA-API

	$(function () {
		$('body').on('mousedown.spinner.data-api', '.spinner', function () {
			var $this = $(this);
			if ($this.data('spinner')) return;
			$this.spinner($this.data());
		});
	});
});<commit_msg>fix for issue 357 - spinner results in weird values when incrementing while using floating points<commit_after>/*
 * Fuel UX Spinner
 * https://github.com/ExactTarget/fuelux
 *
 * Copyright (c) 2012 ExactTarget
 * Licensed under the MIT license.
 */

define(function(require) {

	var $   = require('jquery');
	var old = $.fn.spinner;

	// SPINNER CONSTRUCTOR AND PROTOTYPE

	var Spinner = function (element, options) {
		this.$element = $(element);
		this.options = $.extend({}, $.fn.spinner.defaults, options);
		this.$input = this.$element.find('.spinner-input');
		this.$element.on('keyup', this.$input, $.proxy(this.change, this));

		if (this.options.hold) {
			this.$element.on('mousedown', '.spinner-up', $.proxy(function() { this.startSpin(true); } , this));
			this.$element.on('mouseup', '.spinner-up, .spinner-down', $.proxy(this.stopSpin, this));
			this.$element.on('mouseout', '.spinner-up, .spinner-down', $.proxy(this.stopSpin, this));
			this.$element.on('mousedown', '.spinner-down', $.proxy(function() {this.startSpin(false);} , this));
		} else {
			this.$element.on('click', '.spinner-up', $.proxy(function() { this.step(true); } , this));
			this.$element.on('click', '.spinner-down', $.proxy(function() { this.step(false); }, this));
		}

		this.switches = {
			count: 1,
			enabled: true
		};

		if (this.options.speed === 'medium') {
			this.switches.speed = 300;
		} else if (this.options.speed === 'fast') {
			this.switches.speed = 100;
		} else {
			this.switches.speed = 500;
		}

		this.lastValue = null;

		this.render();

		if (this.options.disabled) {
			this.disable();
		}
	};

	Spinner.prototype = {
		constructor: Spinner,

		render: function () {
			var inputValue = this.$input.val();

			if (inputValue) {
				this.value(inputValue);
			} else {
				this.$input.val(this.options.value);
			}

			this.$input.attr('maxlength', (this.options.max + '').split('').length);
		},

		change: function () {
			var newVal = this.$input.val();

			if(newVal/1){
				this.options.value = newVal/1;
			}else{
				newVal = newVal.replace(/[^0-9]/g,'') || '';
				this.$input.val(newVal);
				this.options.value = newVal/1;
			}

			this.triggerChangedEvent();
		},

		stopSpin: function () {
            if(this.switches.timeout!==undefined){
                clearTimeout(this.switches.timeout);
                this.switches.count = 1;
                this.triggerChangedEvent();
            }
		},

		triggerChangedEvent: function () {
			var currentValue = this.value();
			if (currentValue === this.lastValue) return;

			this.lastValue = currentValue;

			// Primary changed event
			this.$element.trigger('changed', currentValue);

			// Undocumented, kept for backward compatibility
			this.$element.trigger('change');
		},

		startSpin: function (type) {

			if (!this.options.disabled) {
				var divisor = this.switches.count;

				if (divisor === 1) {
					this.step(type);
					divisor = 1;
				} else if (divisor < 3){
					divisor = 1.5;
				} else if (divisor < 8){
					divisor = 2.5;
				} else {
					divisor = 4;
				}

				this.switches.timeout = setTimeout($.proxy(function() {this.iterator(type);} ,this),this.switches.speed/divisor);
				this.switches.count++;
			}
		},

		iterator: function (type) {
			this.step(type);
			this.startSpin(type);
		},

		step: function (dir) {
			var curValue = this.options.value;
			var limValue = dir ? this.options.max : this.options.min;
            var digits, multiple;

			if ((dir ? curValue < limValue : curValue > limValue)) {
				var newVal = curValue + (dir ? 1 : -1) * this.options.step;

                if(this.options.step % 1 !== 0){
                    digits = (this.options.step + '').split('.')[1].length;
                    multiple = Math.pow(10, digits);
                    newVal = Math.round(newVal * multiple) / multiple;
                }

				if (dir ? newVal > limValue : newVal < limValue) {
					this.value(limValue);
				} else {
					this.value(newVal);
				}
			} else if (this.options.cycle) {
				var cycleVal = dir ? this.options.min : this.options.max;
				this.value(cycleVal);
			}
		},

		value: function (value) {
			if (!isNaN(parseFloat(value)) && isFinite(value)) {
				value = parseFloat(value);
				this.options.value = value;
				this.$input.val(value);
				return this;
			} else {
				return this.options.value;
			}
		},

		disable: function () {
			this.options.disabled = true;
			this.$input.attr('disabled','');
			this.$element.find('button').addClass('disabled');
		},

		enable: function () {
			this.options.disabled = false;
			this.$input.removeAttr("disabled");
			this.$element.find('button').removeClass('disabled');
		}
	};

	// SPINNER PLUGIN DEFINITION

	$.fn.spinner = function (option) {
		var args = Array.prototype.slice.call( arguments, 1 );
		var methodReturn;

		var $set = this.each(function () {
			var $this   = $( this );
			var data    = $this.data( 'spinner' );
			var options = typeof option === 'object' && option;

			if( !data ) $this.data('spinner', (data = new Spinner( this, options ) ) );
			if( typeof option === 'string' ) methodReturn = data[ option ].apply( data, args );
		});

		return ( methodReturn === undefined ) ? $set : methodReturn;
	};

	$.fn.spinner.defaults = {
		value: 1,
		min: 1,
		max: 999,
		step: 1,
		hold: true,
		speed: 'medium',
		disabled: false
	};

	$.fn.spinner.Constructor = Spinner;

	$.fn.spinner.noConflict = function () {
		$.fn.spinner = old;
		return this;
	};

	// SPINNER DATA-API

	$(function () {
		$('body').on('mousedown.spinner.data-api', '.spinner', function () {
			var $this = $(this);
			if ($this.data('spinner')) return;
			$this.spinner($this.data());
		});
	});
});<|endoftext|>