Sign in to follow this  
Followers 0
guinness

PubSub pattern in JavaScript

11 posts in this topic

#1 ·  Posted (edited)

For those who are unfamiliar with the PubSub pattern, then perhaps look at this playlist I posted here today before proceeding. I admit it was a new one for me, but then there are so many patterns that it's hard to keep track of 'em.

I basically took the PubSub sample from the video and made a few tweaks here and there. Before you could only pass an event name and single argument, which lets face it is not brilliant if you want to "publish" with multiple arguments. Passing a single object literal is a horrible workaround.

With this version it uses the 'arguments' (https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/arguments) array-like object to obtain the arguments passed and then splices the first element, as this is the event name which is not required to be passed to the callback. I have just noticed there is a huge red warning on that page about Google's V8 engine, but for now I will leave as is.

Enjoy! (Test by running on JSBin.com if you want)

//
// PubSub pattern in JavaScript
//      By LearnCodeAcademy, URL: https://gist.github.com/learncodeacademy/777349747d8382bfb722
//
// Additional info:
//      Tuts+, URL: https://www.youtube.com/watch?v=U4cigUpzW2U&list=PL15G0RGjxzGdBqDPF4DDrlOt9YmjampzO&index=8
//      Peter Higgins, URL: https://github.com/phiggins42/bloody-jquery-plugins/blob/master/pubsub.js
//      PubSubJS, handle/token concept, URL: https://github.com/mroderick/PubSubJS/blob/master/src/pubsub.js
//      PubSub, subscription array concept, URL: https://github.com/drublic/PubSub/
//      PubSub Wikipedia, URL: https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern
//
// Performance tests:
//      forEach-vs-for-loop: http://jsperf.com/array-foreach-vs-for-loop/5
//      for-loop-vs-indexOf: http://jsperf.com/js-for-loop-vs-array-indexof/8
//      shift-vs-splice: http://jsperf.com/shift-vs-splice
//

/**
 * PubSub module
 *
 * Modified: 2015/08/28
 * @author softwarespot
 */
var PubSub = (function (Array, Object) {
    // Constants

    // Version number of the module
    var VERSION = '0.1.0';

    // Array constant enumeration
    var HANDLE_ID = 0,
        HANDLE_SUBSCRIPTION = 1,
        HANDLE_CALLBACK = 2,
        HANDLE_MAX = 3;

    // Return strings of toString() found on the Object prototype
    var ObjectStrings = {
        FUNCTION: '[object Function]',
        STRING: '[object String]'
    };

    // Fields

    // Hold event names with an array of callbacks for each one
    var _subscribers = {};

    // Unique identifier. Leet speak for PubSub_Module
    var _handleId = '|>|_|85|_|8_|\\/|0|)|_|13';

    // Generic handle for an error. This is an array so the reference can be used as a
    // way of verifying that it's an error
    var _handleError = [_handleId];

    // Store the Object toString method
    var _objectToString = Object.prototype.toString;

    // Methods

    // Check if a value is a function. Based on the idea by lodash
    function isFunction(value) {

        return isObject(value) && _objectToString.call(value) === ObjectStrings.FUNCTION;

    }

    // Check if an opaque 'PubSub' handle is valid
    function isHandle(handle) {

        return Array.isArray(handle) && handle.length === HANDLE_MAX && handle[HANDLE_ID] === _handleId;

    }

    // Check if a value is an object. Based on the idea by lodash
    function isObject(value) {

        // Store the typeof
        var type = typeof value;

        // !!value is basically checking if value is not 'truthy' e.g. null or zero and then inverts that boolean value
        // So, !'Some test' is false and then inverting false is true. There if value contains 'something', continue
        return !!value && (type === 'object' || type === 'function');

    }

    // Check if a value is a string datatype with a length greater than zero when whitespace is stripped. Based partially on the idea by lodash
    function isString(value) {

        return (typeof value === 'string' || _objectToString.call(value) === ObjectStrings.STRING) && value.trim().length > 0;

    }

    // Public API
    return {
        // Subscribe to a subscription with a callback function. It's best practice not to make this an anonymous function
        // as then you can't properly unsubscribe, since the callback function reference is required
        // Returns an opaque handle for use with unsubscribe(), though it's optional to use
        subscribe: function (subscriptions, callbacks) { // on()
            // Store whether the first param is a string
            var isStringTypes = isString(subscriptions) && isFunction(callbacks);

            // If a string and a function datatype, then create an array for each
            if (isStringTypes) {
                callbacks = [callbacks];
                subscriptions = [subscriptions];
            }

            // If either of the arguments are not an array or the lengths simply mismatch
            if (!Array.isArray(subscriptions) || !Array.isArray(callbacks) || subscriptions.length !== callbacks.length) {
                return _handleError;
            }

            // Variables used with inside the loop(s)
            var subscription = null;
            var functions = null;
            var callback = null;

            // Return array of handles i.e. [handle id, subscription, callback]
            var handles = [];

            // Iterate through all the subscriptions
            for (var i = 0, subscriptionsLength = subscriptions.length; i < subscriptionsLength; i++) {
                // Store the subscription
                subscription = subscriptions[i];

                // If an array for the event name doesn't exist, then generate a new empty array
                // This cannot be done on the function datatype for obvious reasons
                _subscribers[subscription] = _subscribers[subscription] || [];

                // Retrieve the callbacks for the subscription
                functions = _subscribers[subscription];

                // Store the callback
                callback = callbacks[i];

                // The callback should be a function datatype
                if (!isFunction(callback)) {
                    continue;
                }

                // Check if the callback hasn't already been registered for the event name
                if (functions.indexOf(callback) === -1) {
                    // Push the callback function to the event name array
                    functions.push(callback);

                    // An opaque 'PubSub' handle
                    handles.push([_handleId, subscription, callback]);
                }
            }

            // An error occurred as no opaque 'PubSub' handles were pushed to the handles array
            if (handles.length === 0) {
                return isStringTypes ? _handleError : [_handleError];
            }

            // If a string was passed as the first parameter, then return a single handle instead of an array of handles
            return isStringTypes ? handles[0] : handles;
        },

        // Unsubscribe from a subscription. A string and callback function reference are expected OR
        // the handle returned from subscribe()
        // Returns true or false
        unsubscribe: function (subscriptions, callbacks) { // off()
            // If the reference is equal to the handle error array, then an error occurred with subscribing
            if (subscriptions === _handleError) {
                return false;
            }

            // Set the following variable(s), if it's an opaque 'PubSub' handle returned from subscribe()
            if (isHandle(subscriptions)) {
                // Do not swap, otherwise it will cause an error with overwriting subscriptions
                // The value of callbacks will be ignored
                callbacks = [subscriptions[HANDLE_CALLBACK]];
                subscriptions = [subscriptions[HANDLE_SUBSCRIPTION]];
                // If a string and a function datatype, then create an array for each
            } else if (isString(subscriptions) && isFunction(callbacks)) {
                callbacks = [callbacks];
                subscriptions = [subscriptions];
            }

            // If either of the arguments are not an array or the lengths simply mismatch
            if (!Array.isArray(subscriptions) || !Array.isArray(callbacks) || subscriptions.length !== callbacks.length) {
                return false;
            }

            // Variables used with the loop(s)
            var functions = null;
            var index = 0;

            // Iterate through all the subscriptions
            for (var i = 0, subscriptionsLength = subscriptions.length; i < subscriptionsLength; i++) {
                // Retrieve the callbacks for the subscription
                functions = _subscribers[subscriptions[i]];

                // The subscription hasn't been created or there are simply no callbacks assigned
                if (!functions) {
                    continue;
                }

                // If a callback function reference exists for the subscription,
                // then remove from the array using the index value
                index = functions.indexOf(callbacks[i]);
                if (index !== -1) {
                    // Only remove one value
                    functions.splice(index, 1);
                }
            }

            return true;
        },

        // Publish a subscription to all subscribers with an unlimited number of arguments. The subscription is the last argument i.e. arguments[arguments.length]
        // Returns number of subscriptions published
        publish: function (subscriptions) { // emit()
            // Set the following variable(s), if it's an opaque 'PubSub' handle returned from subscribe()
            if (isHandle(subscriptions)) {
                // Convert to an array datatype
                subscriptions = [subscriptions[HANDLE_SUBSCRIPTION]];
                // If a string has been passed, then convert to an array datatype
            } else if (isString(subscriptions)) {
                subscriptions = [subscriptions];
            }

            // Store the number of subscriptions published
            var published = 0;

            // If not an array, then the subscription was either not a valid array, handle or string
            if (!Array.isArray(subscriptions)) {
                return published;
            }

            var i = 0;
            var length = 0;

            // Construct new arguments to pass to the callback function

            // Convert the array-like object to an array
            // URL: https://shifteleven.com/articles/2007/06/28/array-like-objects-in-javascript/
            // var callbackParams = Array.prototype.slice.call(arguments);

            // Remove the first element from the arguments array-like object, as this contains
            // the subscription name that is not required
            // callbackParams.shift(); // Quicker than .splice()

            // According to MDN, this is a safer approach to constructing a new array. It starts from 1 to
            // skip the first parameter which is the subscription name
            // URL: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/arguments
            var callbackParams = [];
            for (i = 1, length = arguments.length; i < length; i++) {
                callbackParams.push(arguments[i]);
            }

            // Push the subscription to the end of the arguments array as a comma separated string,
            // just in case it's required
            callbackParams.push(subscriptions.join(','));

            // Variables used with the loop(s)
            var functionsLength = 0;
            var functions = null;
            var j = 0;

            // Iterate through all the subscriptions
            for (i = 0, length = subscriptions.length; i < length; i++) {
                // Retrieve the callbacks for the subscription
                functions = _subscribers[subscriptions[i]];

                // The subscription hasn't been created or there are simply no callbacks assigned
                if (!functions) {
                    continue;
                }

                // For each callback function, call the function with the callback arguments
                // by using apply() and passing the (new) array of arguments
                for (j = 0, functionsLength = functions.length; j < functionsLength; j++) {
                    functions[j].apply(this, callbackParams);
                    // Increase the number of publish subscriptions
                    published++;
                }
            }

            return published;
        },

        // Clear the internal subscribers store
        clear: function () {
            _subscribers = {};
        },

        // Get the version number of the module
        getVersion: function () {
            return VERSION;
        }
    };
})(Array, Object);

// Demo

// A simple demo of how to use the following PubSub module
// DO NOT USE ANONYMOUS FUNCTIONS AS YOU WILL BE UNABLE TO UNSUBSCRIBE LATER ON

function fireFirst() {
    console.log('fireFirst: %o', arguments);
}

function fireSecond(arg1, arg2) {
    console.log('fireSecond: %o', arguments);
    // console.log('Arguments: %s, %s', arg1, arg2);
}

console.log('!!! START PubSub DEMO');

// Subscribe to an event and bind a callback for each by passing a function reference
var handle = PubSub.subscribe('search/twitter', fireFirst);

// Display the handle returned by the subscription
console.log('Handle: %o', handle);

PubSub.subscribe('search/twitter', fireSecond);

// Publish the event
PubSub.publish('search/twitter', 'Example arg1', 'Example arg2');

// Look in the browser's console

// Destroy the event only for the second function by passing the event name and the function reference i.e. fireSecond (no parentheses)
PubSub.unsubscribe('search/twitter', fireSecond);

// Publish the event
// Notice how there is only one console.log() displayed, which is for the fireFirst() function
console.log('This should NOT show for the fireSecond function');
PubSub.publish('search/twitter', 'Example arg1', 'Example arg2');

// Destroy the event for the fist function by passing the handle
PubSub.unsubscribe(handle);

// Publish the event
// Notice no event fired
console.log('This should NOT show for any events, as all were unsubscribed previously');
PubSub.publish('search/twitter', 'Example arg1', 'Example arg2');

// Test subscribing to multiple subscriptions and unsubscribing
console.log('Testing multiple subscriptions:');
PubSub.subscribe(['search/github', 'search/js'], [fireFirst, fireSecond]);
console.log('Events should display below:');
PubSub.publish(['search/github', 'search/js']);
PubSub.unsubscribe(['search/github', 'search/js'], [fireFirst, fireSecond]);
console.log('Events should NOT display below:');
PubSub.publish(['search/github', 'search/js']);

console.log('!!! END PubSub DEMO');

 

Edited by guinness
Minor updates

_AdapterConnections()_AlwaysRun()_AppMon()_AppMonEx()_BinaryBin()_CheckMsgBox()_CmdLineRaw()_ContextMenu()_ConvertLHWebColor()/_ConvertSHWebColor()_DesktopDimensions()_DisplayPassword()_DotNet_Load()/_DotNet_Unload()_Fibonacci()_FileCompare()_FileCompareContents()_FileNameByHandle()_FilePrefix/SRE()_FindInFile()_GetBackgroundColor()/_SetBackgroundColor()_GetConrolID()_GetCtrlClass()_GetDirectoryFormat()_GetDriveMediaType()_GetFilename()/_GetFilenameExt()_GetHardwareID()_GetIP()_GetIP_Country()_GetOSLanguage()_GetSavedSource()_GetStringSize()_GetSystemPaths()_GetURLImage()_GIFImage()_GoogleWeather()_GUICtrlCreateGroup()_GUICtrlListBox_CreateArray()_GUICtrlListView_CreateArray()_GUICtrlListView_SaveCSV()_GUICtrlListView_SaveHTML()_GUICtrlListView_SaveTxt()_GUICtrlListView_SaveXML()_GUICtrlMenu_Recent()_GUICtrlMenu_SetItemImage()_GUICtrlTreeView_CreateArray()_GUIDisable()_GUIImageList_SetIconFromHandle()_GUIRegisterMsg()_GUISetIcon()_Icon_Clear()/_Icon_Set()_IdleTime()_InetGet()_InetGetGUI()_InetGetProgress()_IPDetails()_IsFileOlder()_IsGUID()_IsHex()_IsPalindrome()_IsRegKey()_IsStringRegExp()_IsSystemDrive()_IsUPX()_IsValidType()_IsWebColor()_Language()_Log()_MicrosoftInternetConnectivity()_MSDNDataType()_PathFull/GetRelative/Split()_PathSplitEx()_PrintFromArray()_ProgressSetMarquee()_ReDim()_RockPaperScissors()/_RockPaperScissorsLizardSpock()_ScrollingCredits_SelfDelete()_SelfRename()_SelfUpdate()_SendTo()_ShellAll()_ShellFile()_ShellFolder()_SingletonHWID()_SingletonPID()_Startup()_StringCompact()_StringIsValid()_StringRegExpMetaCharacters()_StringReplaceWholeWord()_StringStripChars()_Temperature()_TrialPeriod()_UKToUSDate()/_USToUKDate()_WinAPI_Create_CTL_CODE()_WinAPI_CreateGUID()_WMIDateStringToDate()/_DateToWMIDateString()Au3 script parsingAutoIt SearchAutoIt3 PortableAutoIt3WrapperToPragmaAutoItWinGetTitle()/AutoItWinSetTitle()CodingDirToHTML5FileInstallrFileReadLastChars()GeoIP databaseGUI - Only Close ButtonGUI ExamplesGUICtrlDeleteImage()GUICtrlGetBkColor()GUICtrlGetStyle()GUIEventsGUIGetBkColor()Int_Parse() & Int_TryParse()IsISBN()LockFile()Mapping CtrlIDsOOP in AutoItParseHeadersToSciTE()PasswordValidPasteBinPosts Per DayPreExpandProtect GlobalsQueue()Resource UpdateResourcesExSciTE JumpSettings INISHELLHOOKShunting-YardSignature CreatorStack()Stopwatch()StringAddLF()/StringStripLF()StringEOLToCRLF()VSCROLLWM_COPYDATAMore Examples...

Updated: 04/09/2015

Share this post


Link to post
Share on other sites



Wow, time to refactor based on these results >> http://jsperf.com/js-for-loop-vs-array-indexof/8


_AdapterConnections()_AlwaysRun()_AppMon()_AppMonEx()_BinaryBin()_CheckMsgBox()_CmdLineRaw()_ContextMenu()_ConvertLHWebColor()/_ConvertSHWebColor()_DesktopDimensions()_DisplayPassword()_DotNet_Load()/_DotNet_Unload()_Fibonacci()_FileCompare()_FileCompareContents()_FileNameByHandle()_FilePrefix/SRE()_FindInFile()_GetBackgroundColor()/_SetBackgroundColor()_GetConrolID()_GetCtrlClass()_GetDirectoryFormat()_GetDriveMediaType()_GetFilename()/_GetFilenameExt()_GetHardwareID()_GetIP()_GetIP_Country()_GetOSLanguage()_GetSavedSource()_GetStringSize()_GetSystemPaths()_GetURLImage()_GIFImage()_GoogleWeather()_GUICtrlCreateGroup()_GUICtrlListBox_CreateArray()_GUICtrlListView_CreateArray()_GUICtrlListView_SaveCSV()_GUICtrlListView_SaveHTML()_GUICtrlListView_SaveTxt()_GUICtrlListView_SaveXML()_GUICtrlMenu_Recent()_GUICtrlMenu_SetItemImage()_GUICtrlTreeView_CreateArray()_GUIDisable()_GUIImageList_SetIconFromHandle()_GUIRegisterMsg()_GUISetIcon()_Icon_Clear()/_Icon_Set()_IdleTime()_InetGet()_InetGetGUI()_InetGetProgress()_IPDetails()_IsFileOlder()_IsGUID()_IsHex()_IsPalindrome()_IsRegKey()_IsStringRegExp()_IsSystemDrive()_IsUPX()_IsValidType()_IsWebColor()_Language()_Log()_MicrosoftInternetConnectivity()_MSDNDataType()_PathFull/GetRelative/Split()_PathSplitEx()_PrintFromArray()_ProgressSetMarquee()_ReDim()_RockPaperScissors()/_RockPaperScissorsLizardSpock()_ScrollingCredits_SelfDelete()_SelfRename()_SelfUpdate()_SendTo()_ShellAll()_ShellFile()_ShellFolder()_SingletonHWID()_SingletonPID()_Startup()_StringCompact()_StringIsValid()_StringRegExpMetaCharacters()_StringReplaceWholeWord()_StringStripChars()_Temperature()_TrialPeriod()_UKToUSDate()/_USToUKDate()_WinAPI_Create_CTL_CODE()_WinAPI_CreateGUID()_WMIDateStringToDate()/_DateToWMIDateString()Au3 script parsingAutoIt SearchAutoIt3 PortableAutoIt3WrapperToPragmaAutoItWinGetTitle()/AutoItWinSetTitle()CodingDirToHTML5FileInstallrFileReadLastChars()GeoIP databaseGUI - Only Close ButtonGUI ExamplesGUICtrlDeleteImage()GUICtrlGetBkColor()GUICtrlGetStyle()GUIEventsGUIGetBkColor()Int_Parse() & Int_TryParse()IsISBN()LockFile()Mapping CtrlIDsOOP in AutoItParseHeadersToSciTE()PasswordValidPasteBinPosts Per DayPreExpandProtect GlobalsQueue()Resource UpdateResourcesExSciTE JumpSettings INISHELLHOOKShunting-YardSignature CreatorStack()Stopwatch()StringAddLF()/StringStripLF()StringEOLToCRLF()VSCROLLWM_COPYDATAMore Examples...

Updated: 04/09/2015

Share this post


Link to post
Share on other sites

Fixed performance of the code. See first post for update.


_AdapterConnections()_AlwaysRun()_AppMon()_AppMonEx()_BinaryBin()_CheckMsgBox()_CmdLineRaw()_ContextMenu()_ConvertLHWebColor()/_ConvertSHWebColor()_DesktopDimensions()_DisplayPassword()_DotNet_Load()/_DotNet_Unload()_Fibonacci()_FileCompare()_FileCompareContents()_FileNameByHandle()_FilePrefix/SRE()_FindInFile()_GetBackgroundColor()/_SetBackgroundColor()_GetConrolID()_GetCtrlClass()_GetDirectoryFormat()_GetDriveMediaType()_GetFilename()/_GetFilenameExt()_GetHardwareID()_GetIP()_GetIP_Country()_GetOSLanguage()_GetSavedSource()_GetStringSize()_GetSystemPaths()_GetURLImage()_GIFImage()_GoogleWeather()_GUICtrlCreateGroup()_GUICtrlListBox_CreateArray()_GUICtrlListView_CreateArray()_GUICtrlListView_SaveCSV()_GUICtrlListView_SaveHTML()_GUICtrlListView_SaveTxt()_GUICtrlListView_SaveXML()_GUICtrlMenu_Recent()_GUICtrlMenu_SetItemImage()_GUICtrlTreeView_CreateArray()_GUIDisable()_GUIImageList_SetIconFromHandle()_GUIRegisterMsg()_GUISetIcon()_Icon_Clear()/_Icon_Set()_IdleTime()_InetGet()_InetGetGUI()_InetGetProgress()_IPDetails()_IsFileOlder()_IsGUID()_IsHex()_IsPalindrome()_IsRegKey()_IsStringRegExp()_IsSystemDrive()_IsUPX()_IsValidType()_IsWebColor()_Language()_Log()_MicrosoftInternetConnectivity()_MSDNDataType()_PathFull/GetRelative/Split()_PathSplitEx()_PrintFromArray()_ProgressSetMarquee()_ReDim()_RockPaperScissors()/_RockPaperScissorsLizardSpock()_ScrollingCredits_SelfDelete()_SelfRename()_SelfUpdate()_SendTo()_ShellAll()_ShellFile()_ShellFolder()_SingletonHWID()_SingletonPID()_Startup()_StringCompact()_StringIsValid()_StringRegExpMetaCharacters()_StringReplaceWholeWord()_StringStripChars()_Temperature()_TrialPeriod()_UKToUSDate()/_USToUKDate()_WinAPI_Create_CTL_CODE()_WinAPI_CreateGUID()_WMIDateStringToDate()/_DateToWMIDateString()Au3 script parsingAutoIt SearchAutoIt3 PortableAutoIt3WrapperToPragmaAutoItWinGetTitle()/AutoItWinSetTitle()CodingDirToHTML5FileInstallrFileReadLastChars()GeoIP databaseGUI - Only Close ButtonGUI ExamplesGUICtrlDeleteImage()GUICtrlGetBkColor()GUICtrlGetStyle()GUIEventsGUIGetBkColor()Int_Parse() & Int_TryParse()IsISBN()LockFile()Mapping CtrlIDsOOP in AutoItParseHeadersToSciTE()PasswordValidPasteBinPosts Per DayPreExpandProtect GlobalsQueue()Resource UpdateResourcesExSciTE JumpSettings INISHELLHOOKShunting-YardSignature CreatorStack()Stopwatch()StringAddLF()/StringStripLF()StringEOLToCRLF()VSCROLLWM_COPYDATAMore Examples...

Updated: 04/09/2015

Share this post


Link to post
Share on other sites

#4 ·  Posted (edited)

jQuery version I modified, though not as robust as the one above/below.

/*
 * jQuery Tiny Pub/Sub - v0.1 - 2015/08/07
 * http://benalman.com/
 *
 * Original Copyright (c) 2010 'Cowboy' Ben Alman
 * Dual licensed under the MIT and GPL licenses.
 * http://benalman.com/about/license/
 *
 * Made awesome by Rick Waldron, with additional ideas by Jeffrey Way and softwarespot
 * URL: https://gist.github.com/rwaldron/705311
 */
(function($) {

    // API for PubSub e.g. $.subscribe('example/test'); or $.publish('example/test', function (event, results) {});
    var pubSub = {
            'subscribe': 'on',
            'unsubscribe': 'off',
            'publish': 'trigger'

        },
        store = $({});

    $.each(pubSub, function(callback, api) {
        // Apply the likes of $.subscribe() or $.publish() to jQuery
        $[callback] = function() {
            // When called then invoke the jQuery versions e.g. .on() or .trigger()
            store[api].apply(store, arguments);
        };
    });

})(jQuery);

// Demo

// A simple demo of how to use the following PubSub module
// DO NOT USE ANONYMOUS FUNCTIONS AS YOU WILL BE UNABLE TO UNSUBSCRIBE

function fireFirst() {
    console.log('fireFirst: %o', arguments);
}

function fireSecond(_, arg1) { // _ = event, therefore this can be ignored
    console.log('fireSecond: %o', arguments);
    console.log('Arguments: %s', arg1);
}

// Subscribe to an event and bind a callback for each by passing a function reference
$.subscribe('search/twitter', fireFirst);
$.subscribe('search/twitter', fireSecond);

// Publish the event
$.publish('search/twitter', ['Example arg1', 'Example arg2']); // Must be an array of arguments

// Look in the browser's console

// Destroy the event only for the second function by passing the event name and the function reference i.e. fireSecond (no parentheses)
$.unsubscribe('search/twitter', fireSecond);

// Publish the event
// Notice how there is only one console.log() displayed, which is for the fireFirst() function
$.publish('search/twitter', ['Example arg1', 'Example arg2']); // Must be an array of arguments

 

Edited by guinness

_AdapterConnections()_AlwaysRun()_AppMon()_AppMonEx()_BinaryBin()_CheckMsgBox()_CmdLineRaw()_ContextMenu()_ConvertLHWebColor()/_ConvertSHWebColor()_DesktopDimensions()_DisplayPassword()_DotNet_Load()/_DotNet_Unload()_Fibonacci()_FileCompare()_FileCompareContents()_FileNameByHandle()_FilePrefix/SRE()_FindInFile()_GetBackgroundColor()/_SetBackgroundColor()_GetConrolID()_GetCtrlClass()_GetDirectoryFormat()_GetDriveMediaType()_GetFilename()/_GetFilenameExt()_GetHardwareID()_GetIP()_GetIP_Country()_GetOSLanguage()_GetSavedSource()_GetStringSize()_GetSystemPaths()_GetURLImage()_GIFImage()_GoogleWeather()_GUICtrlCreateGroup()_GUICtrlListBox_CreateArray()_GUICtrlListView_CreateArray()_GUICtrlListView_SaveCSV()_GUICtrlListView_SaveHTML()_GUICtrlListView_SaveTxt()_GUICtrlListView_SaveXML()_GUICtrlMenu_Recent()_GUICtrlMenu_SetItemImage()_GUICtrlTreeView_CreateArray()_GUIDisable()_GUIImageList_SetIconFromHandle()_GUIRegisterMsg()_GUISetIcon()_Icon_Clear()/_Icon_Set()_IdleTime()_InetGet()_InetGetGUI()_InetGetProgress()_IPDetails()_IsFileOlder()_IsGUID()_IsHex()_IsPalindrome()_IsRegKey()_IsStringRegExp()_IsSystemDrive()_IsUPX()_IsValidType()_IsWebColor()_Language()_Log()_MicrosoftInternetConnectivity()_MSDNDataType()_PathFull/GetRelative/Split()_PathSplitEx()_PrintFromArray()_ProgressSetMarquee()_ReDim()_RockPaperScissors()/_RockPaperScissorsLizardSpock()_ScrollingCredits_SelfDelete()_SelfRename()_SelfUpdate()_SendTo()_ShellAll()_ShellFile()_ShellFolder()_SingletonHWID()_SingletonPID()_Startup()_StringCompact()_StringIsValid()_StringRegExpMetaCharacters()_StringReplaceWholeWord()_StringStripChars()_Temperature()_TrialPeriod()_UKToUSDate()/_USToUKDate()_WinAPI_Create_CTL_CODE()_WinAPI_CreateGUID()_WMIDateStringToDate()/_DateToWMIDateString()Au3 script parsingAutoIt SearchAutoIt3 PortableAutoIt3WrapperToPragmaAutoItWinGetTitle()/AutoItWinSetTitle()CodingDirToHTML5FileInstallrFileReadLastChars()GeoIP databaseGUI - Only Close ButtonGUI ExamplesGUICtrlDeleteImage()GUICtrlGetBkColor()GUICtrlGetStyle()GUIEventsGUIGetBkColor()Int_Parse() & Int_TryParse()IsISBN()LockFile()Mapping CtrlIDsOOP in AutoItParseHeadersToSciTE()PasswordValidPasteBinPosts Per DayPreExpandProtect GlobalsQueue()Resource UpdateResourcesExSciTE JumpSettings INISHELLHOOKShunting-YardSignature CreatorStack()Stopwatch()StringAddLF()/StringStripLF()StringEOLToCRLF()VSCROLLWM_COPYDATAMore Examples...

Updated: 04/09/2015

Share this post


Link to post
Share on other sites

First post updated. Now using a IIFE, as a way to encapsulate the private variable. I was happy with the previous object literal module pattern, but I think I am going to publish this on github for reals! So kind of want to to be up to scratch.


_AdapterConnections()_AlwaysRun()_AppMon()_AppMonEx()_BinaryBin()_CheckMsgBox()_CmdLineRaw()_ContextMenu()_ConvertLHWebColor()/_ConvertSHWebColor()_DesktopDimensions()_DisplayPassword()_DotNet_Load()/_DotNet_Unload()_Fibonacci()_FileCompare()_FileCompareContents()_FileNameByHandle()_FilePrefix/SRE()_FindInFile()_GetBackgroundColor()/_SetBackgroundColor()_GetConrolID()_GetCtrlClass()_GetDirectoryFormat()_GetDriveMediaType()_GetFilename()/_GetFilenameExt()_GetHardwareID()_GetIP()_GetIP_Country()_GetOSLanguage()_GetSavedSource()_GetStringSize()_GetSystemPaths()_GetURLImage()_GIFImage()_GoogleWeather()_GUICtrlCreateGroup()_GUICtrlListBox_CreateArray()_GUICtrlListView_CreateArray()_GUICtrlListView_SaveCSV()_GUICtrlListView_SaveHTML()_GUICtrlListView_SaveTxt()_GUICtrlListView_SaveXML()_GUICtrlMenu_Recent()_GUICtrlMenu_SetItemImage()_GUICtrlTreeView_CreateArray()_GUIDisable()_GUIImageList_SetIconFromHandle()_GUIRegisterMsg()_GUISetIcon()_Icon_Clear()/_Icon_Set()_IdleTime()_InetGet()_InetGetGUI()_InetGetProgress()_IPDetails()_IsFileOlder()_IsGUID()_IsHex()_IsPalindrome()_IsRegKey()_IsStringRegExp()_IsSystemDrive()_IsUPX()_IsValidType()_IsWebColor()_Language()_Log()_MicrosoftInternetConnectivity()_MSDNDataType()_PathFull/GetRelative/Split()_PathSplitEx()_PrintFromArray()_ProgressSetMarquee()_ReDim()_RockPaperScissors()/_RockPaperScissorsLizardSpock()_ScrollingCredits_SelfDelete()_SelfRename()_SelfUpdate()_SendTo()_ShellAll()_ShellFile()_ShellFolder()_SingletonHWID()_SingletonPID()_Startup()_StringCompact()_StringIsValid()_StringRegExpMetaCharacters()_StringReplaceWholeWord()_StringStripChars()_Temperature()_TrialPeriod()_UKToUSDate()/_USToUKDate()_WinAPI_Create_CTL_CODE()_WinAPI_CreateGUID()_WMIDateStringToDate()/_DateToWMIDateString()Au3 script parsingAutoIt SearchAutoIt3 PortableAutoIt3WrapperToPragmaAutoItWinGetTitle()/AutoItWinSetTitle()CodingDirToHTML5FileInstallrFileReadLastChars()GeoIP databaseGUI - Only Close ButtonGUI ExamplesGUICtrlDeleteImage()GUICtrlGetBkColor()GUICtrlGetStyle()GUIEventsGUIGetBkColor()Int_Parse() & Int_TryParse()IsISBN()LockFile()Mapping CtrlIDsOOP in AutoItParseHeadersToSciTE()PasswordValidPasteBinPosts Per DayPreExpandProtect GlobalsQueue()Resource UpdateResourcesExSciTE JumpSettings INISHELLHOOKShunting-YardSignature CreatorStack()Stopwatch()StringAddLF()/StringStripLF()StringEOLToCRLF()VSCROLLWM_COPYDATAMore Examples...

Updated: 04/09/2015

Share this post


Link to post
Share on other sites

Kind of finalised now. Just added an opaque handle that is returned from subscribe() which can optionally be passed to publish or unsubscribe, though the conventional string and callback function approach are still available. It's basically a lot more flexible. An idea I got from >> https://github.com/mroderick/PubSubJS/blob/master/src/pubsub.js


_AdapterConnections()_AlwaysRun()_AppMon()_AppMonEx()_BinaryBin()_CheckMsgBox()_CmdLineRaw()_ContextMenu()_ConvertLHWebColor()/_ConvertSHWebColor()_DesktopDimensions()_DisplayPassword()_DotNet_Load()/_DotNet_Unload()_Fibonacci()_FileCompare()_FileCompareContents()_FileNameByHandle()_FilePrefix/SRE()_FindInFile()_GetBackgroundColor()/_SetBackgroundColor()_GetConrolID()_GetCtrlClass()_GetDirectoryFormat()_GetDriveMediaType()_GetFilename()/_GetFilenameExt()_GetHardwareID()_GetIP()_GetIP_Country()_GetOSLanguage()_GetSavedSource()_GetStringSize()_GetSystemPaths()_GetURLImage()_GIFImage()_GoogleWeather()_GUICtrlCreateGroup()_GUICtrlListBox_CreateArray()_GUICtrlListView_CreateArray()_GUICtrlListView_SaveCSV()_GUICtrlListView_SaveHTML()_GUICtrlListView_SaveTxt()_GUICtrlListView_SaveXML()_GUICtrlMenu_Recent()_GUICtrlMenu_SetItemImage()_GUICtrlTreeView_CreateArray()_GUIDisable()_GUIImageList_SetIconFromHandle()_GUIRegisterMsg()_GUISetIcon()_Icon_Clear()/_Icon_Set()_IdleTime()_InetGet()_InetGetGUI()_InetGetProgress()_IPDetails()_IsFileOlder()_IsGUID()_IsHex()_IsPalindrome()_IsRegKey()_IsStringRegExp()_IsSystemDrive()_IsUPX()_IsValidType()_IsWebColor()_Language()_Log()_MicrosoftInternetConnectivity()_MSDNDataType()_PathFull/GetRelative/Split()_PathSplitEx()_PrintFromArray()_ProgressSetMarquee()_ReDim()_RockPaperScissors()/_RockPaperScissorsLizardSpock()_ScrollingCredits_SelfDelete()_SelfRename()_SelfUpdate()_SendTo()_ShellAll()_ShellFile()_ShellFolder()_SingletonHWID()_SingletonPID()_Startup()_StringCompact()_StringIsValid()_StringRegExpMetaCharacters()_StringReplaceWholeWord()_StringStripChars()_Temperature()_TrialPeriod()_UKToUSDate()/_USToUKDate()_WinAPI_Create_CTL_CODE()_WinAPI_CreateGUID()_WMIDateStringToDate()/_DateToWMIDateString()Au3 script parsingAutoIt SearchAutoIt3 PortableAutoIt3WrapperToPragmaAutoItWinGetTitle()/AutoItWinSetTitle()CodingDirToHTML5FileInstallrFileReadLastChars()GeoIP databaseGUI - Only Close ButtonGUI ExamplesGUICtrlDeleteImage()GUICtrlGetBkColor()GUICtrlGetStyle()GUIEventsGUIGetBkColor()Int_Parse() & Int_TryParse()IsISBN()LockFile()Mapping CtrlIDsOOP in AutoItParseHeadersToSciTE()PasswordValidPasteBinPosts Per DayPreExpandProtect GlobalsQueue()Resource UpdateResourcesExSciTE JumpSettings INISHELLHOOKShunting-YardSignature CreatorStack()Stopwatch()StringAddLF()/StringStripLF()StringEOLToCRLF()VSCROLLWM_COPYDATAMore Examples...

Updated: 04/09/2015

Share this post


Link to post
Share on other sites

#7 ·  Posted (edited)

This version has a sophisticated API compared to the first version and I have implemented the ability to register multiple subscriptions at once e.g.

// New API

// Single or Multiple registration
PubSub.subscribe(['sub1', 'sub2'], [callback1, callback2]);
PubSub.subscribe('pubsub', callback3);
PubSub.publish(['sub1', 'sub2', 'sub3']);
PubSub.unsubscribe(['sub1', 'sub2', 'sub3'], [callback1, callback2, callback3]);

// Handle/token usage
var handle = PubSub.subscribe('sub4', callback4);
PubSub.publish(handle);
PubSub.unsubscribe(handle);

See post #1 for PubSub module.

Edited by guinness

_AdapterConnections()_AlwaysRun()_AppMon()_AppMonEx()_BinaryBin()_CheckMsgBox()_CmdLineRaw()_ContextMenu()_ConvertLHWebColor()/_ConvertSHWebColor()_DesktopDimensions()_DisplayPassword()_DotNet_Load()/_DotNet_Unload()_Fibonacci()_FileCompare()_FileCompareContents()_FileNameByHandle()_FilePrefix/SRE()_FindInFile()_GetBackgroundColor()/_SetBackgroundColor()_GetConrolID()_GetCtrlClass()_GetDirectoryFormat()_GetDriveMediaType()_GetFilename()/_GetFilenameExt()_GetHardwareID()_GetIP()_GetIP_Country()_GetOSLanguage()_GetSavedSource()_GetStringSize()_GetSystemPaths()_GetURLImage()_GIFImage()_GoogleWeather()_GUICtrlCreateGroup()_GUICtrlListBox_CreateArray()_GUICtrlListView_CreateArray()_GUICtrlListView_SaveCSV()_GUICtrlListView_SaveHTML()_GUICtrlListView_SaveTxt()_GUICtrlListView_SaveXML()_GUICtrlMenu_Recent()_GUICtrlMenu_SetItemImage()_GUICtrlTreeView_CreateArray()_GUIDisable()_GUIImageList_SetIconFromHandle()_GUIRegisterMsg()_GUISetIcon()_Icon_Clear()/_Icon_Set()_IdleTime()_InetGet()_InetGetGUI()_InetGetProgress()_IPDetails()_IsFileOlder()_IsGUID()_IsHex()_IsPalindrome()_IsRegKey()_IsStringRegExp()_IsSystemDrive()_IsUPX()_IsValidType()_IsWebColor()_Language()_Log()_MicrosoftInternetConnectivity()_MSDNDataType()_PathFull/GetRelative/Split()_PathSplitEx()_PrintFromArray()_ProgressSetMarquee()_ReDim()_RockPaperScissors()/_RockPaperScissorsLizardSpock()_ScrollingCredits_SelfDelete()_SelfRename()_SelfUpdate()_SendTo()_ShellAll()_ShellFile()_ShellFolder()_SingletonHWID()_SingletonPID()_Startup()_StringCompact()_StringIsValid()_StringRegExpMetaCharacters()_StringReplaceWholeWord()_StringStripChars()_Temperature()_TrialPeriod()_UKToUSDate()/_USToUKDate()_WinAPI_Create_CTL_CODE()_WinAPI_CreateGUID()_WMIDateStringToDate()/_DateToWMIDateString()Au3 script parsingAutoIt SearchAutoIt3 PortableAutoIt3WrapperToPragmaAutoItWinGetTitle()/AutoItWinSetTitle()CodingDirToHTML5FileInstallrFileReadLastChars()GeoIP databaseGUI - Only Close ButtonGUI ExamplesGUICtrlDeleteImage()GUICtrlGetBkColor()GUICtrlGetStyle()GUIEventsGUIGetBkColor()Int_Parse() & Int_TryParse()IsISBN()LockFile()Mapping CtrlIDsOOP in AutoItParseHeadersToSciTE()PasswordValidPasteBinPosts Per DayPreExpandProtect GlobalsQueue()Resource UpdateResourcesExSciTE JumpSettings INISHELLHOOKShunting-YardSignature CreatorStack()Stopwatch()StringAddLF()/StringStripLF()StringEOLToCRLF()VSCROLLWM_COPYDATAMore Examples...

Updated: 04/09/2015

Share this post


Link to post
Share on other sites

Updated the PubSub module in post #1.


_AdapterConnections()_AlwaysRun()_AppMon()_AppMonEx()_BinaryBin()_CheckMsgBox()_CmdLineRaw()_ContextMenu()_ConvertLHWebColor()/_ConvertSHWebColor()_DesktopDimensions()_DisplayPassword()_DotNet_Load()/_DotNet_Unload()_Fibonacci()_FileCompare()_FileCompareContents()_FileNameByHandle()_FilePrefix/SRE()_FindInFile()_GetBackgroundColor()/_SetBackgroundColor()_GetConrolID()_GetCtrlClass()_GetDirectoryFormat()_GetDriveMediaType()_GetFilename()/_GetFilenameExt()_GetHardwareID()_GetIP()_GetIP_Country()_GetOSLanguage()_GetSavedSource()_GetStringSize()_GetSystemPaths()_GetURLImage()_GIFImage()_GoogleWeather()_GUICtrlCreateGroup()_GUICtrlListBox_CreateArray()_GUICtrlListView_CreateArray()_GUICtrlListView_SaveCSV()_GUICtrlListView_SaveHTML()_GUICtrlListView_SaveTxt()_GUICtrlListView_SaveXML()_GUICtrlMenu_Recent()_GUICtrlMenu_SetItemImage()_GUICtrlTreeView_CreateArray()_GUIDisable()_GUIImageList_SetIconFromHandle()_GUIRegisterMsg()_GUISetIcon()_Icon_Clear()/_Icon_Set()_IdleTime()_InetGet()_InetGetGUI()_InetGetProgress()_IPDetails()_IsFileOlder()_IsGUID()_IsHex()_IsPalindrome()_IsRegKey()_IsStringRegExp()_IsSystemDrive()_IsUPX()_IsValidType()_IsWebColor()_Language()_Log()_MicrosoftInternetConnectivity()_MSDNDataType()_PathFull/GetRelative/Split()_PathSplitEx()_PrintFromArray()_ProgressSetMarquee()_ReDim()_RockPaperScissors()/_RockPaperScissorsLizardSpock()_ScrollingCredits_SelfDelete()_SelfRename()_SelfUpdate()_SendTo()_ShellAll()_ShellFile()_ShellFolder()_SingletonHWID()_SingletonPID()_Startup()_StringCompact()_StringIsValid()_StringRegExpMetaCharacters()_StringReplaceWholeWord()_StringStripChars()_Temperature()_TrialPeriod()_UKToUSDate()/_USToUKDate()_WinAPI_Create_CTL_CODE()_WinAPI_CreateGUID()_WMIDateStringToDate()/_DateToWMIDateString()Au3 script parsingAutoIt SearchAutoIt3 PortableAutoIt3WrapperToPragmaAutoItWinGetTitle()/AutoItWinSetTitle()CodingDirToHTML5FileInstallrFileReadLastChars()GeoIP databaseGUI - Only Close ButtonGUI ExamplesGUICtrlDeleteImage()GUICtrlGetBkColor()GUICtrlGetStyle()GUIEventsGUIGetBkColor()Int_Parse() & Int_TryParse()IsISBN()LockFile()Mapping CtrlIDsOOP in AutoItParseHeadersToSciTE()PasswordValidPasteBinPosts Per DayPreExpandProtect GlobalsQueue()Resource UpdateResourcesExSciTE JumpSettings INISHELLHOOKShunting-YardSignature CreatorStack()Stopwatch()StringAddLF()/StringStripLF()StringEOLToCRLF()VSCROLLWM_COPYDATAMore Examples...

Updated: 04/09/2015

Share this post


Link to post
Share on other sites

Updated the PubSub module in post #1.


_AdapterConnections()_AlwaysRun()_AppMon()_AppMonEx()_BinaryBin()_CheckMsgBox()_CmdLineRaw()_ContextMenu()_ConvertLHWebColor()/_ConvertSHWebColor()_DesktopDimensions()_DisplayPassword()_DotNet_Load()/_DotNet_Unload()_Fibonacci()_FileCompare()_FileCompareContents()_FileNameByHandle()_FilePrefix/SRE()_FindInFile()_GetBackgroundColor()/_SetBackgroundColor()_GetConrolID()_GetCtrlClass()_GetDirectoryFormat()_GetDriveMediaType()_GetFilename()/_GetFilenameExt()_GetHardwareID()_GetIP()_GetIP_Country()_GetOSLanguage()_GetSavedSource()_GetStringSize()_GetSystemPaths()_GetURLImage()_GIFImage()_GoogleWeather()_GUICtrlCreateGroup()_GUICtrlListBox_CreateArray()_GUICtrlListView_CreateArray()_GUICtrlListView_SaveCSV()_GUICtrlListView_SaveHTML()_GUICtrlListView_SaveTxt()_GUICtrlListView_SaveXML()_GUICtrlMenu_Recent()_GUICtrlMenu_SetItemImage()_GUICtrlTreeView_CreateArray()_GUIDisable()_GUIImageList_SetIconFromHandle()_GUIRegisterMsg()_GUISetIcon()_Icon_Clear()/_Icon_Set()_IdleTime()_InetGet()_InetGetGUI()_InetGetProgress()_IPDetails()_IsFileOlder()_IsGUID()_IsHex()_IsPalindrome()_IsRegKey()_IsStringRegExp()_IsSystemDrive()_IsUPX()_IsValidType()_IsWebColor()_Language()_Log()_MicrosoftInternetConnectivity()_MSDNDataType()_PathFull/GetRelative/Split()_PathSplitEx()_PrintFromArray()_ProgressSetMarquee()_ReDim()_RockPaperScissors()/_RockPaperScissorsLizardSpock()_ScrollingCredits_SelfDelete()_SelfRename()_SelfUpdate()_SendTo()_ShellAll()_ShellFile()_ShellFolder()_SingletonHWID()_SingletonPID()_Startup()_StringCompact()_StringIsValid()_StringRegExpMetaCharacters()_StringReplaceWholeWord()_StringStripChars()_Temperature()_TrialPeriod()_UKToUSDate()/_USToUKDate()_WinAPI_Create_CTL_CODE()_WinAPI_CreateGUID()_WMIDateStringToDate()/_DateToWMIDateString()Au3 script parsingAutoIt SearchAutoIt3 PortableAutoIt3WrapperToPragmaAutoItWinGetTitle()/AutoItWinSetTitle()CodingDirToHTML5FileInstallrFileReadLastChars()GeoIP databaseGUI - Only Close ButtonGUI ExamplesGUICtrlDeleteImage()GUICtrlGetBkColor()GUICtrlGetStyle()GUIEventsGUIGetBkColor()Int_Parse() & Int_TryParse()IsISBN()LockFile()Mapping CtrlIDsOOP in AutoItParseHeadersToSciTE()PasswordValidPasteBinPosts Per DayPreExpandProtect GlobalsQueue()Resource UpdateResourcesExSciTE JumpSettings INISHELLHOOKShunting-YardSignature CreatorStack()Stopwatch()StringAddLF()/StringStripLF()StringEOLToCRLF()VSCROLLWM_COPYDATAMore Examples...

Updated: 04/09/2015

Share this post


Link to post
Share on other sites

Opted for using leet speak identifier instead of guid. In the next couple of days I will be working an ES2015 version, which can easily be converted to ES5 using a transpiler e.g. babel.


_AdapterConnections()_AlwaysRun()_AppMon()_AppMonEx()_BinaryBin()_CheckMsgBox()_CmdLineRaw()_ContextMenu()_ConvertLHWebColor()/_ConvertSHWebColor()_DesktopDimensions()_DisplayPassword()_DotNet_Load()/_DotNet_Unload()_Fibonacci()_FileCompare()_FileCompareContents()_FileNameByHandle()_FilePrefix/SRE()_FindInFile()_GetBackgroundColor()/_SetBackgroundColor()_GetConrolID()_GetCtrlClass()_GetDirectoryFormat()_GetDriveMediaType()_GetFilename()/_GetFilenameExt()_GetHardwareID()_GetIP()_GetIP_Country()_GetOSLanguage()_GetSavedSource()_GetStringSize()_GetSystemPaths()_GetURLImage()_GIFImage()_GoogleWeather()_GUICtrlCreateGroup()_GUICtrlListBox_CreateArray()_GUICtrlListView_CreateArray()_GUICtrlListView_SaveCSV()_GUICtrlListView_SaveHTML()_GUICtrlListView_SaveTxt()_GUICtrlListView_SaveXML()_GUICtrlMenu_Recent()_GUICtrlMenu_SetItemImage()_GUICtrlTreeView_CreateArray()_GUIDisable()_GUIImageList_SetIconFromHandle()_GUIRegisterMsg()_GUISetIcon()_Icon_Clear()/_Icon_Set()_IdleTime()_InetGet()_InetGetGUI()_InetGetProgress()_IPDetails()_IsFileOlder()_IsGUID()_IsHex()_IsPalindrome()_IsRegKey()_IsStringRegExp()_IsSystemDrive()_IsUPX()_IsValidType()_IsWebColor()_Language()_Log()_MicrosoftInternetConnectivity()_MSDNDataType()_PathFull/GetRelative/Split()_PathSplitEx()_PrintFromArray()_ProgressSetMarquee()_ReDim()_RockPaperScissors()/_RockPaperScissorsLizardSpock()_ScrollingCredits_SelfDelete()_SelfRename()_SelfUpdate()_SendTo()_ShellAll()_ShellFile()_ShellFolder()_SingletonHWID()_SingletonPID()_Startup()_StringCompact()_StringIsValid()_StringRegExpMetaCharacters()_StringReplaceWholeWord()_StringStripChars()_Temperature()_TrialPeriod()_UKToUSDate()/_USToUKDate()_WinAPI_Create_CTL_CODE()_WinAPI_CreateGUID()_WMIDateStringToDate()/_DateToWMIDateString()Au3 script parsingAutoIt SearchAutoIt3 PortableAutoIt3WrapperToPragmaAutoItWinGetTitle()/AutoItWinSetTitle()CodingDirToHTML5FileInstallrFileReadLastChars()GeoIP databaseGUI - Only Close ButtonGUI ExamplesGUICtrlDeleteImage()GUICtrlGetBkColor()GUICtrlGetStyle()GUIEventsGUIGetBkColor()Int_Parse() & Int_TryParse()IsISBN()LockFile()Mapping CtrlIDsOOP in AutoItParseHeadersToSciTE()PasswordValidPasteBinPosts Per DayPreExpandProtect GlobalsQueue()Resource UpdateResourcesExSciTE JumpSettings INISHELLHOOKShunting-YardSignature CreatorStack()Stopwatch()StringAddLF()/StringStripLF()StringEOLToCRLF()VSCROLLWM_COPYDATAMore Examples...

Updated: 04/09/2015

Share this post


Link to post
Share on other sites

#11 ·  Posted (edited)

Here is an ES2015 version

//
// PubSub pattern in JavaScript
//      By LearnCodeAcademy, URL: https://gist.github.com/learncodeacademy/777349747d8382bfb722
//
// Additional info:
//      Tuts+, URL: https://www.youtube.com/watch?v=U4cigUpzW2U&list=PL15G0RGjxzGdBqDPF4DDrlOt9YmjampzO&index=8
//      Peter Higgins, URL: https://github.com/phiggins42/bloody-jquery-plugins/blob/master/pubsub.js
//      PubSubJS, handle/token concept, URL: https://github.com/mroderick/PubSubJS/blob/master/src/pubsub.js
//      PubSub, subscription array concept, URL: https://github.com/drublic/PubSub/
//      PubSub Wikipedia, URL: https://en.wikipedia.org/wiki/Publish–subscribe_pattern
//
// Performance tests:
//      forEach-vs-for-loop: http://jsperf.com/array-foreach-vs-for-loop/5
//      for-loop-vs-indexOf: http://jsperf.com/js-for-loop-vs-array-indexof/8
//      shift-vs-splice: http://jsperf.com/shift-vs-splice
//

/**
 * PubSub module
 *
 * Modified: 2015/08/27
 * Note: The following is written in ES2015/6, therefore it will not work on all browsers just yet
 * @author softwarespot
 */
let PubSub = (function (Array, Object) {
    // Constants

    // Version number of the module
    const VERSION = 0.1;

    // Array constant enumeration
    const HANDLE_ID = 0, HANDLE_SUBSCRIPTION = 1, HANDLE_CALLBACK = 2, HANDLE_MAX = 3;

    // Return strings of toString() found on the Object prototype
    const ObjectStrings = {
        FUNCTION: '[object Function]',
        STRING: '[object String]'
    };

    // Fields

    // Hold event names with an array of callbacks for each one
    let _subscribers = {};

    // Unique identifier. Leet speak for PubSub_Module
    let _handleId = '|>|_|85|_|8_|\\/|0|)|_|13';

    // Generic handle for an error. This is an array so the reference can be used as a
    // way of verifying that it's an error
    let _handleError = [_handleId];

    // Store the Object toString method
    let _objectToString = Object.prototype.toString;

    // Methods

    // Check if a value is a function. Based on the idea by lodash
    let isFunction = function (value) {

        return isObject(value) && _objectToString.call(value) === ObjectStrings.FUNCTION;

    };

    // Check if an opaque 'PubSub' handle is valid
    let isHandle = function (handle) {

        return Array.isArray(handle) && handle.length === HANDLE_MAX && handle[HANDLE_ID] === _handleId;

    };

    // Check if a value is an object. Based on the idea by lodash
    let isObject = function (value) {

        // Store the typeof
        let type = typeof value;

        // !!value is basically checking if value is not 'truthy' e.g. null or zero and then inverts that boolean value
        // So, !'Some test' is false and then inverting false is true. There if value contains 'something', continue
        return !!value && (type === 'object' || type === 'function');

    };

    // Check if a value is a string datatype with a length greater than zero when whitespace is stripped. Based partially on the idea by lodash
    let isString = function (value) {

        return (typeof value  === 'string' || _objectToString.call(value) === ObjectStrings.STRING) && value.trim().length > 0;

    };

    // Public API
    return {
        // Subscribe to a subscription with a callback function. It's best practice not to make this an anonymous function
        // as then you can't properly unsubscribe, since the callback function reference is required
        // Returns an opaque handle for use with unsubscribe(), though it's optional to use
        subscribe: function (subscriptions, callbacks) { // on()
            // Store whether the first param is a string
            let isStringTypes = isString(subscriptions) && isFunction(callbacks);

            // If a string and a function datatype, then create an array for each
            if (isStringTypes) {
                callbacks = [callbacks];
                subscriptions = [subscriptions];
            }

            // If either of the arguments are not an array or the lengths simply mismatch
            if (!Array.isArray(subscriptions) || !Array.isArray(callbacks) || subscriptions.length !== callbacks.length) {
                return _handleError;
            }

            // Return array of handles i.e. [handle id, subscription, callback]
            let handles = [];

            // Iterate through all the subscriptions
            for(let i = 0, subscriptionsLength = subscriptions.length; i < subscriptionsLength; i++) {
                // Store the subscription
                let subscription = subscriptions[i];

                // If an array for the event name doesn't exist, then generate a new empty array
                // This cannot be done on the function datatype for obvious reasons
                _subscribers[subscription] = _subscribers[subscription] || [];

                // Retrieve the callbacks for the subscription
                let functions = _subscribers[subscription];

                // Store the callback
                let callback = callbacks[i];

                // The callback should be a function datatype
                if (!isFunction(callback)) {
                    continue;
                }

                // Check if the callback hasn't already been registered for the event name
                if (!functions.includes(callback)) {
                    // Push the callback function to the event name array
                    functions.push(callback);

                    // An opaque 'PubSub' handle
                    handles.push([_handleId, subscription, callback]);
                }
            }

            // An error occurred as no opaque 'PubSub' handles were pushed to the handles array
            if (handles.length === 0) {
                return isStringTypes ? _handleError : [_handleError];
            }

            // If a string was passed as the first parameter, then return a single handle instead of an array of handles
            return isStringTypes ? handles[0] : handles;
        },

        // Unsubscribe from a subscription. A string and callback function reference are expected OR
        // the handle returned from subscribe()
        // Returns true or false
        unsubscribe: function (subscriptions, callbacks) { // off()
            // If the reference is equal to the handle error array, then an error occurred with subscribing
            if (subscriptions === _handleError) {
                return false;
            }

            // Set the following variable(s), if it's an opaque 'PubSub' handle returned from subscribe()
            if (isHandle(subscriptions)) {
                // Do not swap, otherwise it will cause an error with overwriting subscriptions
                // The value of callbacks will be ignored
                callbacks = [subscriptions[HANDLE_CALLBACK]];
                subscriptions = [subscriptions[HANDLE_SUBSCRIPTION]];
            // If a string and a function datatype, then create an array for each
            } else if (isString(subscriptions) && isFunction(callbacks)) {
                callbacks = [callbacks];
                subscriptions = [subscriptions];
            }

            // If either of the arguments are not an array or the lengths simply mismatch
            if (!Array.isArray(subscriptions) || !Array.isArray(callbacks) || subscriptions.length !== callbacks.length) {
                return false;
            }

            // Iterate through all the subscriptions
            for(let i = 0, subscriptionsLength = subscriptions.length; i < subscriptionsLength; i++) {
                // Retrieve the callbacks for the subscription
                let functions = _subscribers[subscriptions[i]];

                // The subscription hasn't been created or there are simply no callbacks assigned
                if (!functions) {
                    continue;
                }

                // If a callback function reference exists for the subscription,
                // then remove from the array using the index value
                let index = functions.indexOf(callbacks[i]);
                if (index !== -1) {
                    // Only remove one value
                    functions.splice(index, 1);
                }
            }

            return true;
        },

        // Publish a subscription to all subscribers with an unlimited number of arguments. The subscription is the last argument i.e. arguments[arguments.length]
        // Returns number of subscriptions published
        publish: function (subscriptions, ...args) { // emit()
            // Set the following variable(s), if it's an opaque 'PubSub' handle returned from subscribe()
            if (isHandle(subscriptions)) {
                // Convert to an array datatype
                subscriptions = [subscriptions[HANDLE_SUBSCRIPTION]];
            // If a string has been passed, then convert to an array datatype
            } else if (isString(subscriptions)) {
                subscriptions = [subscriptions];
            }

            // Store the number of subscriptions published
            let published = 0;

            // If not an array, then the subscription was either not a valid array, handle or string
            if (!Array.isArray(subscriptions)) {
                return published;
            }

            // Push the subscription to the end of the arguments array as a comma separated string,
            // just in case it's required
            args.push(subscriptions.join(','));

            // Iterate through all the subscriptions
            for(let i = 0, length = subscriptions.length; i < length; i++) {
                // Retrieve the callbacks for the subscription
                let functions = _subscribers[subscriptions[i]];

                // The subscription hasn't been created or there are simply no callbacks assigned
                if (!functions) {
                    continue;
                }

                // For each callback function, call the function with the callback arguments
                // by using apply() and passing the (new) array of arguments
                for (let j = 0, functionsLength = functions.length; j < functionsLength; j++) {
                    functions[j].apply(this, args);
                    // Increase the number of publish subscriptions
                    published++;
                }
            }

            return published;
        },

        // Clear the internal subscribers store
        clear: function () {
            _subscribers = {};
        },

        // Get the version number of the module
        getVersion: function () {
            return VERSION;
        }
    };
})(Array, Object);

// Demo

// A simple demo of how to use the following PubSub module
// DO NOT USE ANONYMOUS FUNCTIONS AS YOU WILL BE UNABLE TO UNSUBSCRIBE LATER ON

function fireFirst() {
    console.log('fireFirst: %o', arguments);
}

function fireSecond(arg1, arg2) {
    console.log('fireSecond: %o', arguments);
    // console.log('Arguments: %s, %s', arg1, arg2);
}

console.log('!!! START PubSub DEMO');

// Subscribe to an event and bind a callback for each by passing a function reference
let handle = PubSub.subscribe('search/twitter', fireFirst);

// Display the handle returned by the subscription
console.log('Handle: %o', handle);

PubSub.subscribe('search/twitter', fireSecond);

// Publish the event
PubSub.publish('search/twitter', 'Example arg1', 'Example arg2');

// Look in the browser's console

// Destroy the event only for the second function by passing the event name and the function reference i.e. fireSecond (no parentheses)
PubSub.unsubscribe('search/twitter', fireSecond);

// Publish the event
// Notice how there is only one console.log() displayed, which is for the fireFirst() function
console.log('This should NOT show for the fireSecond function');
PubSub.publish('search/twitter', 'Example arg1', 'Example arg2');

// Destroy the event for the fist function by passing the handle
PubSub.unsubscribe(handle);

// Publish the event
// Notice no event fired
console.log('This should NOT show for any events, as all were unsubscribed previously');
PubSub.publish('search/twitter', 'Example arg1', 'Example arg2');

// Test subscribing to multiple subscriptions and unsubscribing
console.log('Testing multiple subscriptions:');
PubSub.subscribe(['search/github', 'search/js'], [fireFirst, fireSecond]);
console.log('Events should display below:');
PubSub.publish(['search/github', 'search/js']);
PubSub.unsubscribe(['search/github', 'search/js'], [fireFirst, fireSecond]);
console.log('Events should NOT display below:');
PubSub.publish(['search/github', 'search/js']);

console.log('!!! END PubSub DEMO');

I am loving let (block scope variables) and rest params =)

Edited by guinness
Using JS syntax highlighting

_AdapterConnections()_AlwaysRun()_AppMon()_AppMonEx()_BinaryBin()_CheckMsgBox()_CmdLineRaw()_ContextMenu()_ConvertLHWebColor()/_ConvertSHWebColor()_DesktopDimensions()_DisplayPassword()_DotNet_Load()/_DotNet_Unload()_Fibonacci()_FileCompare()_FileCompareContents()_FileNameByHandle()_FilePrefix/SRE()_FindInFile()_GetBackgroundColor()/_SetBackgroundColor()_GetConrolID()_GetCtrlClass()_GetDirectoryFormat()_GetDriveMediaType()_GetFilename()/_GetFilenameExt()_GetHardwareID()_GetIP()_GetIP_Country()_GetOSLanguage()_GetSavedSource()_GetStringSize()_GetSystemPaths()_GetURLImage()_GIFImage()_GoogleWeather()_GUICtrlCreateGroup()_GUICtrlListBox_CreateArray()_GUICtrlListView_CreateArray()_GUICtrlListView_SaveCSV()_GUICtrlListView_SaveHTML()_GUICtrlListView_SaveTxt()_GUICtrlListView_SaveXML()_GUICtrlMenu_Recent()_GUICtrlMenu_SetItemImage()_GUICtrlTreeView_CreateArray()_GUIDisable()_GUIImageList_SetIconFromHandle()_GUIRegisterMsg()_GUISetIcon()_Icon_Clear()/_Icon_Set()_IdleTime()_InetGet()_InetGetGUI()_InetGetProgress()_IPDetails()_IsFileOlder()_IsGUID()_IsHex()_IsPalindrome()_IsRegKey()_IsStringRegExp()_IsSystemDrive()_IsUPX()_IsValidType()_IsWebColor()_Language()_Log()_MicrosoftInternetConnectivity()_MSDNDataType()_PathFull/GetRelative/Split()_PathSplitEx()_PrintFromArray()_ProgressSetMarquee()_ReDim()_RockPaperScissors()/_RockPaperScissorsLizardSpock()_ScrollingCredits_SelfDelete()_SelfRename()_SelfUpdate()_SendTo()_ShellAll()_ShellFile()_ShellFolder()_SingletonHWID()_SingletonPID()_Startup()_StringCompact()_StringIsValid()_StringRegExpMetaCharacters()_StringReplaceWholeWord()_StringStripChars()_Temperature()_TrialPeriod()_UKToUSDate()/_USToUKDate()_WinAPI_Create_CTL_CODE()_WinAPI_CreateGUID()_WMIDateStringToDate()/_DateToWMIDateString()Au3 script parsingAutoIt SearchAutoIt3 PortableAutoIt3WrapperToPragmaAutoItWinGetTitle()/AutoItWinSetTitle()CodingDirToHTML5FileInstallrFileReadLastChars()GeoIP databaseGUI - Only Close ButtonGUI ExamplesGUICtrlDeleteImage()GUICtrlGetBkColor()GUICtrlGetStyle()GUIEventsGUIGetBkColor()Int_Parse() & Int_TryParse()IsISBN()LockFile()Mapping CtrlIDsOOP in AutoItParseHeadersToSciTE()PasswordValidPasteBinPosts Per DayPreExpandProtect GlobalsQueue()Resource UpdateResourcesExSciTE JumpSettings INISHELLHOOKShunting-YardSignature CreatorStack()Stopwatch()StringAddLF()/StringStripLF()StringEOLToCRLF()VSCROLLWM_COPYDATAMore Examples...

Updated: 04/09/2015

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0

  • Similar Content

    • Patryk
      By Patryk
      Dear Colleagues,
      I have no idea how to refer to style.display in my loop. There is a button like: 
      <input name="ctl00$bodyPlaceholder$btnFecharProcessamento" class="button" id="bodyPlaceholder_btnFecharProcessamento" style="display: none;" onclick="FecharJanelaProcessamento();return false;" type="submit" value="Fechar"> I want it to be clicked as soon as button's style change from style="display: none;" to  style= ="display: inline-block;" 
      This is what I got, but it's not working...
      Func GetButtons() $oButtons = _IEGetObjByName($oForm,"ctl00$bodyPlaceholder$btnFecharProcessamento",-1) $i = 0 While $i <> 1 For $oBtn In $oButtons If _IEFormElementGetValue($oBtn) = "Fechar" And $oBtn.document.style.display = "display: inline-block;" Then $i = 1 _IEAction($oBtn, "focus") _IEAction($oBtn, "click") ExitLoop EndIf Next WEnd EndFunc  
      I will really appreciate if you could help me with these loop!
    • Omnitica
      By Omnitica
      Hello!
      I'm new to the forums! Couldn't find any threads covering this issue. Sorry if I'm wrong.

      I'm having problems changing the text/value of a input box. The particular box doesn't have a name or id, just: class="jq_hotkeys_enabled form-control".
      This class appears multiple times throughout the source, so I don't think It's right to reference that object. 
      See image 1.
      However, I've managed to find a object with the name "ctl00$content$ctrl$ucServiceTime$rpServicetimes$ctl00$edtHiddenResourceHelper" which contains the value of the input box, and the id for the person in the dropdown/combobox left of it. (with "dummy-1" selected).
      See image 2.
      As you can see the value is 3982,4545     ( name , time )
      But when I try to change the value of it by doing:
      #include <IE.au3> Local $oIE = _IEAttach(WinGetHandle("[CLASS:IEFrame]"),"embedded") Local $oForm = _IEFormGetObjByName($oIE, "aspnetForm") Local $oInput = _IEFormElementGetObjByName($oForm, "ctl00$content$ctrl$ucServiceTime$rpServicetimes$ctl00$edtHiddenResourceHelper") _IEFormElementSetValue($oInput, "3982,1337") The box doesn't get its new value. I  can see in the source that it's changed though.
      See image 3.
       
      However, if I do: 
      #include <IE.au3> Local $oIE = _IEAttach(WinGetHandle("[CLASS:IEFrame]"),"embedded") Local $oForm = _IEFormGetObjByName($oIE, "aspnetForm") Local $oInput = _IEFormElementGetObjByName($oForm, "ctl00$content$ctrl$ucServiceTime$rpServicetimes$ctl00$edtHiddenResourceHelper") _IEFormElementSetValue($oInput, "3982,1337") $oIE.document.parentwindow.execScript("theForm.submit()") ; But it needs to reload the page The box gets its new value after the page reloads by the theForm.submit() function. 
      This is a bit unfortunate as I want the changes to happen instantly like when I type in the box with the keyboard... or select in the combobox.
      Maybe someone knows how I can do this? Looking forward to an answer! 
      Thanks in advance.
       
      Note: Unfortunately I can't provide with the real URL as it is constricted to employees in the company I work for, but let me know If I should provide anything else.
       



    • milos83
      By milos83
      How can I check if element is focused in Firefox using FF.au3 UDF?
      This is my try:
      I have prepared a function that checks if the object is focused:
       
      _FFCmd('FFau3.isfocused=function isfocused(a){try{return (a === document.activeElement)}catch(e){return}}') $oElm = _FFXPath("//*[@id='someId']") ConsoleWrite(_FFIsFocused($oElm)) Func _FFIsFocused($sElement = "") Local $isFoc = _FFCmd("FFau3.isfocused(" & $sElement & ")") Return $isFoc EndFunc ;==>_FFIsFocused Now, the javascript part is tested and it does return successfully.
      After a lot of try/fails I inserted an alert box in order to display the object being compared.
      Here is what I got
       
      The focused object is: [object XULElement]
      The object that I send for comparing is [object HTMLInputElement]
      Why is that?
       
       
    • Chimp
      By Chimp
      Hi all.
      I'm playing a bit with the ScriptControl object using as base this example by @genius257.
      here is a very simple try, ... but it fails. what I'm doing wrong?
      Local $oSC = ObjCreate("ScriptControl") $oSC.Language = "JScript" $oSC.ExecuteStatement('alert("I am a javascript alert box!");') MsgBox(0,'AutoIt','Pause') Thanks
    • steveeye
      By steveeye
      Hi, what else can we add in <table> besides <tr> & <td> to make it more interative to the users?