0

I am writing an add-on. I need to modify the existing javascript/jquery in some ways. It calculates a price based on inputs. Pseudocode (original js I need to modify):

input.onEvent
    get Input Values
    calculate 'price' from input values

I need to modify the calculated price. So I would need to modify the original js in a way like this:

    modified price = triggerEvent('modifyThePrice', price)

In words: I need to call a hook, where other javascript then can do their work and modify the price if they need to (or maybe something like an event with an return statement)...

on request I'll also add more precise code..

$('form#buy').bind('price-update', function(){
/*
* a LOT of code, getting input, calculating price. at the end we have a var 
*price which is numeric
*/

//What I need to do now
var modified_price = modifyPrice(price);
});

just that modifyPrice(price) has to call every other js on the page, so everyone has the chance to interact.

to the guys of you knowing wordpress: I need to do something that would be done in wp with:

$modified_price = apply_filters('modify_price', $price);

where everyone can bind an function to the 'modify-price' hook

I hope that helps to understand the question

kave
  • 461
  • 1
  • 6
  • 17
  • 1
    you should post the relevant code you are trying to modify, it could be easier to understand what you are looking for – A. Wolff Nov 21 '13 at 20:31

2 Answers2

2

How about this. First, the filter module:

var filter = {

    filters: {},

    add: function (tag, filter) {
        (this.filters[tag] || (this.filters[tag] = [])).push(filter);
    },

    apply: function (tag, val) {
        if(this.filters[tag]){
            var filters = this.filters[tag];
            for(var i = 0; i < filters.length; i++){
                val = filters[i](val);
            }
        }
        return val;
    }
}

Then to add a filter:

filter.add('modify_name', function(name){
    name = 'MR. ' + name;
    return name;
});

And to apply the filters:

name = filter.apply('modify_name', name);

Jsfiddle demo

Alternatively, here is how you can do it using dom events:

$(window).on('modify_name', function (e, data) {
    data.name = 'MR. ' + data.name;
});

var data = {};
data.name = 'joe';
$(window).trigger('modify_name', data); 

Note: jQuery.trigger does not return the values from the event listeners, so the hooks must directly alter the data, instead of returning it. Furthermore, in Javascript you cant pass a value by reference, except for an object. So to pass the data for modifications, we must first store it in an object, and then pass the object to the hooks.

Jsfiddle demo

levi
  • 23,693
  • 18
  • 59
  • 73
  • 1
    thanks a lot for the effort! this is really nice way to solve it. but isn't there some easier method that I don't need to implement? There must be a 'normal' way of giving other people the opoturnity to make changes to your code if you write something where you know plugins will be written for or not? – kave Nov 21 '13 at 22:55
  • 2
    You can use `dom` events, but it wont be a "clean" solution for a few reasons: a) `jQuery.trigger` does not return a value from the listeners, so you would have to directly alter the value within the hook. b) In JS you cant pass a value by reference, except for an object. So every time you want to apply-filters, you would have to first store the data in an object, and then trigger the filters, passing the object it can alter. c) it would be ugly: `$(window).on('modify_name', function(e, data){ data.name = 'new name'; });` – levi Nov 21 '13 at 23:16
  • 1
    See - http://stackoverflow.com/questions/9145347/jquery-returning-value-from-trigger & http://stackoverflow.com/questions/7744611/pass-variables-by-reference-in-javascript – levi Nov 21 '13 at 23:18
2

This simple JavaScript hook system with namespace support will store all of your hooks function in an object called hooks:

var hooks = {}; // store all hooks here

function on(name, fn) {
    name = name.split('.');
    if (!hooks[name[0]]) hooks[name[0]] = {};
    var i = name[1] || Object.keys(hooks[name[0]]).length;
    hooks[name[0]][i] = fn;
}

function off(name) {
    if (!name) {
        return hooks = {};
    }
    name = name.split('.');
    if (name[1]) {
        delete hooks[name[0]][name[1]];
    } else {
        delete hooks[name[0]];
    }
}

function trigger(name, param) {
    name = name.split('.');
    if (!hooks[name[0]]) hooks[name[0]] = {};
    if (name[1]) {
        if (hooks[name[0]][name[1]]) hooks[name[0]][name[1]].apply({}, param);
    } else {
        for (var i in hooks[name[0]]) {
            hooks[name[0]][i].apply({}, param);
        }
    }
}
<p>
  <button onclick="on('click',function(){alert('hook `'+(new Date()).getTime()+'` added!')});">Add a <code>click</code> Hook</button>
  <button onclick="on('click.foo',function(){alert('hook `click.foo` added!')});">Add a <code>click</code> Hook with ID <code>foo</code></button>
</p>
<p>
  <button onclick="off('click');">Remove All <code>click</code> Hooks</button>
  <button onclick="off('click.foo');">Remove a <code>click</code> Hook with ID <code>foo</code></button>
</p>
<p>
  <button onclick="trigger('click');">Trigger All <code>click</code> Hooks</button>
  <button onclick="trigger('click.foo');">Trigger <code>click</code> Hook with ID `foo`</button>
</p>

Usage

// add a `click` hook
on("click", function(x) {
    alert('test ' + x + ' ok!');
});

// add a `click` hook with `foo` namespace
on("click.foo", function(x) {
    alert('test ' + x + ' foo ok!');
});

// remove all `click` hooks
off("click");

// remove `click.foo` hook only
off("click.foo");

// trigger all `click` hooks
trigger("click", ['this will be the `x`', 'this will be the second function parameter…']);

// trigger `click.foo` hook only
trigger("click.foo", ['this will be the `x`', 'this will be the second function parameter…']);

If you want to return a value, use this snippet instead:

var hooks = {}; // store all hooks here

function add_filter(name, fn) {
    name = name.split('.');
    if (!hooks[name[0]]) hooks[name[0]] = {};
    var i = name[1] || Object.keys(hooks[name[0]]).length;
    hooks[name[0]][i] = fn;
}

function remove_filter(name) {
    if (!name) {
        return hooks = {};
    }
    name = name.split('.');
    if (name[1]) {
        delete hooks[name[0]][name[1]];
    } else {
        delete hooks[name[0]];
    }
}

function apply_filter(name, value, param) {
    name = name.split('.');
    if (!hooks[name[0]]) hooks[name[0]] = {};
    if (name[1]) {
        if (hooks[name[0]][name[1]]) {
            value = hooks[name[0]][name[1]].apply(value, param);
        }
    } else {
        for (var i in hooks[name[0]]) {
            value = hooks[name[0]][i].apply(value, param);
        }
    }
    return value;
}

Usage

// add a `test` filter
add_filter("test", function(v) {
    return v + ' test!';
});

// add a `test` filter with `foo` namespace
add_filter("test.foo", function(v) {
    return v + ' test.foo!';
});

// remove all `test` filters
remove_filter("test");

// remove `test.foo` filter only
remove_filter("test.foo");

// apply all `test` filters
return apply_filter("test", 'initial value', [param_1, param_2]);

// apply `test.foo` filter only
return apply_filter("test.foo", 'initial value', [param_1, param_2]);
Taufik Nurrohman
  • 3,329
  • 24
  • 39