Jplit reset specific filter(s) - javascript

So, i'm using the jplist plugin to filter a list of flats and i'd need at some point to reset one (or more) specific filters. Point is, there's no api we can use to control the plugin externally.
Here's the code used for the reset button to reset all filters to default:
(function() {
var d = function(b) {
b.$control.on("click", function() {
b.observer.trigger(b.observer.events.unknownStatusesChanged, [!0])
})
},
e = function(b) {
d(b);
return jQuery.extend(this, b)
};
jQuery.fn.jplist.ui.controls.Reset = function(b) {
return new e(b)
};
jQuery.fn.jplist.controlTypes.reset = {
className: "Reset",
options: {}
}
})();
I have to admit that i don't fully get how it works but i've done a quick test with this:
jQuery('#some-id-button').on('click', function() {
b.observer.trigger(b.observer.events.unknownStatusesChanged, [!0])
});
And it does reset all filters from a specific element.
Question is, how could i pass some arguments (id, classes) to target specific filters and not all of them?

Related

Create an instance of a jQuery plugin

I have several pages which I wish to allow the the user to inline edit many fields and update the server DB. To implement this, my intent is to create a jQuery plugin which I can do the typical passing of the configuration options and uses ajax to save the results.
(function($){
var methods = {
init : function (options) {return this.each(function () {/* ... */});},
method1 : function () {return this.each(function () {/* ... */});},
method2 : function () {return this.each(function () {/* ... */});}
};
$.fn.myEditPlugin= function(method) {
if (methods[method]) {
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1)); //Line 10
} else if (typeof method === 'object' || ! method) {
return methods.init.apply(this, arguments); //Line 12
} else {
$.error('Method ' + method + ' does not exist on jQuery.myEditPlugin');
}
};
}(jQuery)
);
For each individual page, there are several options which are common to all (i.e. the url endpoint, the record's primary key, etc) and I would rather not duplicate each when applying the plugin.
Originally, I was just going to define a function on each page which takes some input and applies the common options to each.
function wrapEdit(e,options) {
options.url='/page1/etc';
options.pk=document.getElementById('pk').value;
return $(e).myEditPlugin(options);
}
wrapEdit('.someclass',{foo:123});
It doesn't seem all that professional to me, so in my obsessive quest, thought I would make a class which I could pass the common options to and it would apply the plugin.
class WrapEdit(options)
{
constructor(options) {
this.options = options;
}
this.applyIndividualOptions=function(e, options) {
return $(e).myEditPlugin(Object.assign({}, this->options, options));
}
}
var wrapEdit=new WrapEdit({url: '/page1/etc', pk: document.getElementById('pk').value});
wrapEdit.applyIndividualOptions('.someclass',{foo:123});
Better, but not very jQueryish as I will be passing the select element instead of directly applying the plugin to elements typical of jQuery.
Is it possible to create an instance of a jQuery plugin which keeps previously defined data? Maybe something like the following:
$.myEditPlugin({url: '/page1/etc', pk: document.getElementById('pk').value});
$('.someclass').myEditPlugin({foo:123}); //Will also pass previously defined url and pk to myEditPlugin
Or maybe best to create a custom jQuery plugin per page which just adds the extra options and initiates the real plugin...
$.fn.myEditPluginInstance = function(options) {
return this.myEditPlugin(Object.assign({url: '/page1/etc', pk: document.getElementById('pk').value}, options));
};
Creating a function to be called against a jquery collection
The basic idea is to define a new property (function) in jQuery.fn, before any call to your plugin is made (In other words, any code related to the application is executed). You can use an "Immediately Invoked Function Expressions" (a.k.a. IIFEs) to fence your plugin API in. Then you have to loop over the collection and execute any code your plugin needs to apply on the collection items.
Basic skeleton:
(function ($) {
// Enclosed scope (IIFE)
// You can define private API/variables in here
// …
// Once your plugin API is ready, you have to apply the magic to each item
// in the collection in some ways. You must add a property to jQuery.fn object.
$.fn.myAwesomePlugin = function(Opt) {
var defaultConfig = {option1: 'someValue' /*, …*/};
// Eval supplied Opt object (Validate, reject, etc.)
// If all goes well, eventually merge the object with defaults.
$.extend(defaultConfig, Opt);
// Apply the magic against each item in the jQuery collection
// (Your plugin may not need to use "each" function though)
// Return the jQuery collection anyway to keep chaining possible.
// Once again, this is not required, your plugin may return something else depending on the options passed earlier for instance.
return this.each(function(el, idx) {
// Your plugin magic applied to collection items…
});
}
})(jQuery);
You should be able to call your plugin $('someSelector').myAwesomePlugin(); right after the declaration.
Simple implementation example:
(function ($) {
let required = {url: null, pk: null}
// Function to be executed upon first call to the plugin
, populateCommons = () => {
let ep = $('#someNode').data('endpoint')
, pk = document.querySelector('#pk')
;
// Basic tests to alert in case the page
// doesn't comply with the plugin requirements
if( typeof ep !== 'string' || !/^\/[a-z]+/.test(ep) || !pk) {
throw ` "myEditPlugin" init phase error:
Detected endpoint: '${ep}'
Is PK value found: ${!!pk}
`;
}
[required.url, required.pk] = [ep, +pk.value];
};
$.fn.myEditPlugin = function(Opt) {
let allOpts;
// First call will trigger the retrival of common data
// that should be available as static data somewhere every page source.
!required.url && populateCommons();
allOpts = $.extend({}, Opt, required);
return this.each(function(el, idx) {
// Your logic here, request
console.log("Payload is", allOpts);
});
}
})(jQuery);
function debounce(fn, time) {
debounce.timer && (clearTimeout(debounce.timer));
debounce.timer = setTimeout(() => (fn(), debounce.timer = null), time);
}
$('[type="text"]').keydown(function(e){
debounce(() => this.value && $(this).myEditPlugin({foo:this.value, bar: 'Contextual value'}), 2000);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id="pk" type="hidden" value="5">
<div id="someNode" data-endpoint="/api/endpoint">
Editing the below input will trigger the plug-in code
</div>
<input type="text" title="Edit me"/>
Related documentation here

Apply filters from an array in CamanJS

I want to store all the filters applied by different buttons and then apply then sequentially on an image. For example, if a user clicks on Brigthness, Noise, Contrast. I want to store these filters and once a user clicks on Apply Filters. I want to apply them all. I tried the following method:
Caman('#canvas', img, function () {
//this.brightness(10).render();
var filters = ["brightness(10)", "noise(20)"];
filters.forEach(function (item, index) {
this.filters(item);
});
this.render();
});
But this gives me the error this.filters is not a function. I can use the commented out line but that will only apply predetermined filters. I want to apply filters based on user selection and I want to apply them all at once when user clicks on apply filters.
Here is a link to the library: http://camanjs.com/examples/
Can anyone please guide me how can I achieve what I want? Let me know if I did not explain the question clearly before downvoting.
That error is showing up because when you use this inside foreach the value of this points the filter array and not caman object Try this
Caman('#canvas', img, function () {
//this.brightness(10).render();
var that = this;
var filters = ["brightness(10)", "noise(20)"];
filters.forEach(function (item, index) {
eval('that.'+item);
});
this.render();
});
In above code a copy of this is made which is then passes to inside the loop with name as that
this.filters won't work because 'this' refers to the scope of function(item, index) {...}
I would do something like this:
Caman('#canvas', img, function () {
// make 'this' available in the scope through 'self' variable
var self = this;
// Filters must hold the function and not a string of the function.
// so something like:
var filters = [
function() { self.brightness(10); },
function() { self.noise(20); }
];
filters.forEach(function (fn) {
fn(); // this will execute the anonymous functions in the filters array
});
this.render();
});
You can define objects in your array and loop over the effects using forEach():
Caman('#canvas', img, function () {
var filters = [
{ name: "brightness", val:10 },
{ name: "noise", val:20 }
];
var that = this;
filters.forEach(function(effect) {
that[effect.name](effect.val);
});
this.render();
});

jQuery Plugin return Values through options function

I'm making my own jQuery plugin (I am still learning!). The plugin needs to return values once clicked on a certain element.
It looks along the lines of this:
$.fn.testPlugin = function(options) {
var defaultOptions = {
values: {
value1: "",
value2: ""
},
onDivNameClick: function() {}
};
options = $.extend(true, defaultOptions, options);
//Do some initializing here
divName.click(function () {
//Do some other plugin stuff on click here
// onClick User Function
if ($.isFunction(options.onDivNameClick)) {
options.onDivNameClick.call(options.values);
}
})
//Other code here.
return this;
}
I initialize the plugin and pass commands that I would like to be executed however the plugin does not return the values I need through values instead it is 'undefined'.
Debugging shows me that options.values does have data inside the plugin so I'm not entirely sure this is the correct way to do it.
$("#elName").testPlugin({
onDivNameClick: function (values) { //values returns 'undefined'
model.value1 = values.value1;
model.value2 = values.value2;
},
option1: "",
//other options
});
onDivNameClick should pass options.values into the function in options but it currently does not. What am I doing wrong?
The javascript prototype.call() method needs one more parameter referring to this.
if ($.isFunction(options.onDivNameClick)) {
options.onDivNameClick.call(this, options.values);
}
Because of this - no actual values were passed through the function.

How to (and should we) test UI element visibility in jasmine?

I have a function that hides and shows items on my page based on what a factory provides me:
function toggleMenuItems(config) {
// hide all our elements first
$(".js-quickMenuElement").hide();
config.data = config.data || [];
config.data.forEach(function (d) {
if (d === config.viewConfigCatalog.CalendarLink) {
$("#CalendarLink.js-quickMenuElement").show();
}
if (d === config.viewConfigCatalog.ProductCreation) {
$("#ProductCreation.js-quickMenuElement").show();
}
// etc etc etc
});
};
We've been using Jasmine for our javascript unit tests and we're discussing whether we should test this function.
Some say that we don't need to because testing this is coupling the view to the javascript test, but at the same time, if instead of jquery .show and .hide functions those were wrappers, or other functions we would test them.
Following on this what would be the best way to test this?
Making a wrapper function that takes in a string and injects the string name in the jQuery select seems wrong.
Another option we thought of is spying on ($.fn, "show") but that would only let us test if show was called X amount of time and not what was hidden...
Thanks,
You can use jQuery to test the visibility of an element.
$(element).is(":visible");
code taken from a related question
Of course in doing this as you say you're coupling the view with the test. You could move the logic which determines the outcome of this function into a separate function and then test that functions result instead.
** Edit **
Below illustrates what I meant regarding simplification with a KVP list, and you could write a test for the function which gets the value from the KVP.
var config = {
data: [],
viewConfigCatalog: {
CalendarLink: "CalendarLink",
ProductCreation: "ProductCreation",
}
};
var kvp = [{
name: config.viewConfigCatalog.CalendarLink,
value: "#CalendarLink.js-quickMenuElement"
}, {
name: config.viewConfigCatalog.ProductCreation,
value: "#ProductCreation.js-quickMenuElement"
}];
function getSelectorString(name) {
var i = kvp.length;
while (i--) {
var pair = kvp[i];
if (pair.name === name)
return pair.value;
}
return null;
}
function toggleMenuItems(config) {
// hide all our elements first
$(".js-quickMenuElement").hide();
config.data = config.data || [];
config.data.forEach(function(d) {
$(getSelectorString(d)).show();
});
};
document.writeln(getSelectorString(config.viewConfigCatalog.CalendarLink)+'<br/>');
document.writeln(getSelectorString(config.viewConfigCatalog.ProductCreation)+'<br/>');
document.writeln(getSelectorString("hi"));

Update ordered list when array changes in Javascript

So I've used Backbone and Angular quite a bit and gotten used to data binding / view updating within those ecosystems, but I don't know how I would achieve this in plain JS (no frameworks/libraries).
Right now I have a simple UserList, and I would like to watch for changes to it and trigger and update of an unordered list when it happens.
var ContactList = {
list: [],
push: function(obj) {
this.storage.push(obj);
},
remove: functon(obj) {
return this.storage.splice(this.storage.indexOf(obj), 1);
}
};
var Contact = function(attributes) {
this.attributes = attributes || {};
};
Contact.prototype.get = function(property) {
return this.attributes[property];
};
Contact.prototype.set = function(property, value) {
this.attributes[property] = value;
};
Ideally the following would automatically add to a list. I could just add a callback to the push and remove methods, but that seems like it doesn't scale very well if I get to a point where I'm adding more methods to operate on my list. I've been reading a bit about the observer pattern, but not sure if that's really what I'm looking for here.
You don't want to pass the callback to every call of ContactList.push and ContactList.remove and all the ContactList methods you are yet to write. Instead, the ContactList will know when he has changed and then announce that fact to the world. In a simple implementation, ContactList could have his own onChange method which he could call:
var ContactList = {
list: [],
push: function (obj) {
this.list.push(obj);
this.onChange();
},
remove: function (obj) {
var index = this.list.indexOf(obj);
if (index > -1) {
var removed = this.list.splice(index, 1);
this.onChange();
return removed;
} else {
return null;
}
}
};
You would then, obviously, have to define ConactList.onChange:
ContactList.onChange = function () {
console.log(this.list);
/* update your ul element */
};
This solution will not allow you to add subscribers dynamically to Contact List's change event, but it might be a helpful starting point.

Categories