Create options for each object of jquery plugin - jquery-plugins

I need your help again :)
I'm trying to do a plugin with jQuery specifications.
So I started reading this: http://docs.jquery.com/Plugins/Authoring
The document is cool and give nice patterns to follow.
But i have a problem with my plugin.
My plugin appends a div and bind some events to diferents features.
Sometimes i need to accés to the options var but... the problem is, if i do the opt var global it take the last object created options.
And if i put it in the init method, i can't use it in other actions.
I need each new object can acces only his own option set.
(function( $ ) {
//plugin methods
var methods = {
init : function( options ) {
//default options
var opt = $.extend({
'id' : 'test',
'title' : 'Test window',
'type' : 'normal',
'text' : 'test test! <br/> 123',
'shines' : '',
'head_shines' : '',
'body_shines' : '',
'bottom_bar' : true
}, options);
//shine or not shine? that's the matter
if (opt.shines != '') {
opt.shines = "shiny";
opt.head_shines = " shine_head";
opt.body_shines = " shine_body";
}
//maintaining Chainability
return this.each(function() {
var $this = $(this); // $this is now JQuery object
//creating the bottom bar
if (opt.bottom_bar == true && $("#bottom_bar").length == 0) {
$this.append('<div id="bottom_bar"></div>');
}
//creating the new window
$this.append("<div style='display: none;' class='window "+opt.shines+"' id='"+opt.id+"'>...boring html...</div>");
//append new window to the bar
$("#bottom_bar").append("<div style='display: none' class='section' id='s_"+opt.id+"'>"+opt.title+"</div>");
//get a object of the window to interact with
var $window = $("#"+opt.id);
//show the windows
$window.fadeIn().draggable();
$("#s_"+opt.id).fadeIn();
//attach the events to the windows
$window.find('.close').one('click.ventana', methods.close);
$window.find('.max').on('click.ventana', methods.maximize);
$window.find('.min').on('click.ventana', methods.minimize);
$("#s_"+opt.id).on('click.ventana', methods.minimizeBar);
});
},
close : function() {},
maximize : function() {}, //i want acces my opts here!
minimize : function() {},
minimizeBar: function() {} //or here... etc
}; //end methods
//creating the plugin
$.fn.ventana = function( method ) {
if ( methods[method] ) { //if we call a method...
return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ) );
} else if ( typeof method == 'object' || !method ) { //if not, we use init
return methods.init.apply( this, arguments);
} else { //method don't exists (console error)
$.error( 'Method ' + method + ' does not exists in jQuery.ventana');
}
};
}) ( jQuery );
The problem is, if i put where is the first comment:
//plugin methods
this:
//globals define
var opt;
I only get the last object opts...
Example creating new windows
$('body').ventana( {
'id' : 'master',
'title' : 'Afegir Finestres',
'text' : 'test'
});
$('body').ventana( {
'id' : 'master1',
'title' : 'Afegir Finestres1',
});
I just gonna get the master1 opts in both objects

You could use data to store the options object to be retrieved later.
// store it
$this.data("options", opt);
// ...
// use it later
var opt = $this.data("options");

Related

How can I check until an element is clickable using nightwatchjs?

How can I check until an element is clickable using nightwatch js? I want to click on an element but when I run nightwatch, selenium does not click on the element because it is not clickable yet.
Something like this should work. Let me know if you have questions
var util = require('util');
var events = require('events');
/*
* This custom command allows us to locate an HTML element on the page and then wait until the element is both visible
* and does not have a "disabled" state. It rechecks the element state every 500ms until either it evaluates to true or
* it reaches maxTimeInMilliseconds (which fails the test). Nightwatch uses the Node.js EventEmitter pattern to handle
* asynchronous code so this command is also an EventEmitter.
*/
function WaitUntilElementIsClickable() {
events.EventEmitter.call(this);
this.startTimeInMilliseconds = null;
}
util.inherits(WaitUntilElementIsClickable, events.EventEmitter);
WaitUntilElementIsClickable.prototype.command = function (element, timeoutInMilliseconds) {
this.startTimeInMilliseconds = new Date().getTime();
var self = this;
var message;
if (typeof timeoutInMilliseconds !== 'number') {
timeoutInMilliseconds = this.api.globals.waitForConditionTimeout;
}
this.check(element, function (result, loadedTimeInMilliseconds) {
if (result) {
message = '#' + element + ' was clickable after ' + (loadedTimeInMilliseconds - self.startTimeInMilliseconds) + ' ms.';
} else {
message = '#' + element + ' was still not clickable after ' + timeoutInMilliseconds + ' ms.';
}
self.client.assertion(result, 'not visible or disabled', 'visible and not disabled', message, true);
self.emit('complete');
}, timeoutInMilliseconds);
return this;
};
WaitUntilElementIsClickable.prototype.check = function (element, callback, maxTimeInMilliseconds) {
var self = this;
var promises =[];
promises.push(new Promise(function(resolve) {
self.api.isVisible(element, function(result) {
resolve(result.status === 0 && result.value === true);
});
}));
promises.push(new Promise(function(resolve) {
self.api.getAttribute(element, 'disabled', function (result) {
resolve(result.status === 0 && result.value === null);
});
}));
Promise.all(promises)
.then(function(results) {
var now = new Date().getTime();
const visibleAndNotDisabled = !!results[0] && !!results[1];
if (visibleAndNotDisabled) {
callback(true, now);
} else if (now - self.startTimeInMilliseconds < maxTimeInMilliseconds) {
setTimeout(function () {
self.check(element, callback, maxTimeInMilliseconds);
}, 500);
} else {
callback(false);
}
})
.catch(function(error) {
setTimeout(function () {
self.check(element, callback, maxTimeInMilliseconds);
}, 500);
});
};
module.exports = WaitUntilElementIsClickable;
Add this code as a file to your commands folder. It should be called waitUntilElementIsClickable.js or whatever you want your command to be.
Usage is:
browser.waitUntilElementIsClickable('.some.css');
You can also use page elements:
var page = browser.page.somePage();
page.waitUntilElementIsClickable('#someElement');
You can use waitForElementVisible() combined with the :enabled CSS pseudo-class.
For example, the following will wait up to 10 seconds for #element to become enabled, then click it (note that the test will fail if the element doesn't become enabled after 10 seconds):
browser
.waitForElementVisible('#element:enabled', 10000)
.click('#element');
Can you show an example element,usually there should be an attribute name "disabled" if the button is not clickable, this should work.
browser.assert.attributeEquals(yourCSS, 'disabled', true)
I'm unable to comment but there are a couple of issues with the code suggested by Alex R.
First, the code will not work with Firefox as geckodriver does not return a 'status'. So this:
resolve(result.status === 0 && result.value === true)
needs to be changed to this:
resolve(result.value === true).
Second, the line:
self.client.assertion(result, 'not visible or disabled', 'visible and not disabled', message, true);
doesn't work and needs to be commented out in
order to get the code to run.

jquery plugin pass variable inside config settings

I would like to pass an array in my plugin defaults config settings. I am trying to give the user, the option to add as many title and src variable as they like for instance:
desktop_xl: {
"title":"beach",
"src":"http://images.smh.com.au/2013/02/25/4061249/art-Whitehaven-Beach-620x349.jpg"
},
{
"title":"sunset",
"src":"https://lh5.googleusercontent.com/-Oh0HlfM31BQ/TlXHejUNpeI/AAAAAAAABiI/tQbJJEGEOnU/s400/red_sunset_beach.jpg"
}
I have seen this question on stack overflow but could not find an answer that works for me.
I did some reading and figured out that I can create an array of objects, it works well only on my index.html page as per the below fiddle, http://jsfiddle.net/michelm/7gS6g/2/
The issue is that I would like to use this array as a config option in my plugin so users can add as many title and src variables as they need, but the array does not work inside the plugin.
When I did console.log(desktop_xl); on my index.html page, it shows as an object.
I read the documentation at http://api.jquery.com/jquery.extend/ and from what I understand I need to merge my objects to pass them as a config option, here is the link to my plugin from my drop box account (js fiddle did not take the https link for some reasons), please see below link to jquery.myplugin.js (random name for now, but will use unique naming convention once I work out the logic):
https://www.dropbox.com/s/boadofib6nggfzp/jquery.myplugin.js
Can anyone help me figure out how to pass this variable in my config option so users can add as many "title" and "src" from the option desktop_xl please?
UPDATE:
I have figured out how to pull array information and append it to my images, however, I still have no idea on how to "link" this to the plugin option settings as I need to give the user the option to add as many images with title as they would like in the option settings.
Here is how I have figured out how to pull data from array:
//create img desktop_xl loop
$.each(desktop_xl, function( index , value ){
$('#container').append(
$("<img />").attr({
id: value.title,
src: value.src,
title: value.title
})
);
});
UPDATE 2:
I have done some more work on the plugin, here is the code so far:
;(function($, window, document, undefined){
//define MyjQueryPlugin object with default config settings:
$.MyjQueryPlugin = {
defaults: {
imagecontainer: "#container",
version: "v01"
// add my Arrays to default options here?
// arrays should allow users to add as many images to #container div as they require
// arrays are desktop_xl[] , desktop_l[] , ipad_p[] , mobile_l[], mobile_p[]
}
};
//extend jquery with the plugin
$.fn.extend({
MyjQueryPlugin:function(config) {
//use defaults or properties supplied by user
var config = $.extend({}, $.MyjQueryPlugin.defaults, config );
//append slides
$(config.imagecontainer).append('<div class="imagecontainerfordesktop_xlarray" </div>').css('height', $(window).height() );
// append MyjQueryPlugin sidebar
this.append( '<div id="Mysidebar" class="open">' +
'<p class="review">Version ' + config.version + '- ready for review</p>'+
'<hr>' +
'</div>')
.children()
.css('height', $(window).height() );
//create array of objects
var desktop_xl = [
{
"title":"Homepage", // text for sidebar
"src":"slides/1200/Homepage.jpg"// path to image >= 1200px wide
},
{
"title":"Categories", // text for sidebar
"src":"slides/1200/Categories.jpg"// path to image >= 1200px wide
},
{
"title":"Product description", // text for sidebar
"src":"slides/1200/Product_description.jpg" // path to image >= 1200px wide
}
];
var desktop_l = [
// if array is empty, remove elements from the page
];
var ipad_p = [
{
"title":"Homepage", // text for sidebar
"src":"slides/480/Homepage.jpg" // path to image >= 480px wide
}
];
var mobile_l = [];
var mobile_p = [];
// set Global Variables
var width = $(window).width();
var currHeight = $(window).height();
var ctrl = $(".ctrl");
var ulscreenlia = $('ul.screen li a');
var sidebarlia = $('#MyjQueryPluginsidebar li a');
var sidebar = $("#MyjQueryPluginsidebar");
var ulscreenli = $('ul.screen li');
if (desktop_xl.length === 0) {
ulscreenli.eq(0).hide();
$('div.select_join option[value="xld"]').remove();
}
if (desktop_l.length === 0) {
ulscreenli.eq(1).hide();
$('div.select_join option[value="ld"]').remove();
}
if (ipad_p.length === 0) {
ulscreenli.eq(2).hide();
$('div.select_join option[value="ip"]').remove();
}
if (mobile_l.length === 0) {
ulscreenli.eq(3).hide();
$('div.select_join option[value="ml"]').remove();
}
if (mobile_p.length === 0) {
ulscreenli.eq(4).hide();
$('div.select_join option[value="mp"]').remove();
}
//create img desktop_xl loop
$.each(desktop_xl, function( index , value ){
$('#container .slides-xld').append(
//getting values from array but cannot understand how to pass array(s): desktop_xl, desktop_l, ipad_p, mobile_l, mobile_p inside config option
//And Each arrays should allow user to add multiple images to #container dive
$("<img />").attr({
id: value.title,
src: value.src,
title: value.title
})
);
});
//create img ipadp loop
$.each(ipad_p, function( index , value){
$('#container .slides-ipadp').append(
$("<img />").attr({
id: value.title,
src: value.src,
title: value.title
})
);
});
function rundateobject(){
var current_date = new Date ( );
var month_names = new Array ( );
month_names[month_names.length] = "January";
month_names[month_names.length] = "February";
month_names[month_names.length] = "March";
month_names[month_names.length] = "April";
month_names[month_names.length] = "May";
month_names[month_names.length] = "June";
month_names[month_names.length] = "July";
month_names[month_names.length] = "August";
month_names[month_names.length] = "September";
month_names[month_names.length] = "October";
month_names[month_names.length] = "November";
month_names[month_names.length] = "December";
var day_names = new Array ( );
day_names[day_names.length] = "Sunday";
day_names[day_names.length] = "Monday";
day_names[day_names.length] = "Tuesday";
day_names[day_names.length] = "Wednesday";
day_names[day_names.length] = "Thursday";
day_names[day_names.length] = "Friday";
day_names[day_names.length] = "Saturday";
$('#date').html( day_names[current_date.getDay()]
+ ', '
+ month_names[current_date.getMonth()]
+ ' '
+ current_date.getDate()
+ ' '
+ current_date.getFullYear() );
};
//create animation for anchor links with jQuery DOM ready function
$(function(){
$('a').hover(function(){
$(this).animate({
'margin-left':10,
'padding-left':20
},200);
$(this).dequeue();
},
function() {
$(this).animate({
'margin-left':0,
'padding-left':15
},200);
$(this).dequeue();
}
);
});
//on resize browser, adjust elements height
//initialise plugins
$(".nano").nanoScroller();
//initialise functions
rundateobject();
//return the jquery object for chaining
return this;
}// config options
}); // jQuery extend
})(jQuery, window, document);
The answer was given to me on jquery.com by user kbwood.au
defaults: {
imagecontainer: "#container",
version: "v01",
desktop_xl: [] //Array
}
Then in the .each function we need to pass the array as a config.desktop_xl:
//create img desktop_xl loop
$.each(config.desktop_xl, function( index , value ){
$('#container').append(
$("<img />").attr({
id: value.title,
src: value.src,
title: value.title
})
);
});

WP_Query with ajax handler returns the same posts

I have a problem that is getting me bald atm. I have a ajax call that handles a loop that handles some querys and returns posts for me.
So far so good, but the first time the user sees the page we should load 10 posts, and then we want to click a button to request 5 more.
So far so good.
But when we request the 5 more posts we get the 5 first posts again.
My batchloop
<?php
// Our include
define('WP_USE_THEMES', false);
require_once('../../../wp-load.php');
// Our variables
$posts = (isset($_GET['numPosts'])) ? $_GET['numPosts'] : 0;
$page = (isset($_GET['pageNumber'])) ? $_GET['pageNumber'] : 0;
$category = (isset($_GET['category_name'])) ? $_GET['category_name'] : 0;
var_dump($posts);
$args = array(
'posts_per_page' => $posts,
'category_name' => $category,
'post_status' => 'publish',
'orderby' => 'date',
'order' => 'DESC',
'paged' => $page
);
query_posts($args);
// $query = new WP_query($args);
// our loop
if (have_posts()) {
$paged = (get_query_var('paged')) ? get_query_var('paged') : 1; query_posts($args);
while (have_posts()){
the_post();
get_template_part( 'thumbs', get_post_format() );
}
}
// unset($page, $posts, $category);
// wp_reset_postdata();
wp_reset_query();
?>
Does anybody sees what im doing wrong?
EDIT:
batch handler
function _batchhandler() {
var getamount = localStorage.getItem('amount');
console.log('amount of posts to retrive ' + JSON.parse(getamount));
// Ajax call
$.ajax({
type: 'GET',
data: {
posts: getamount,
page: page,
category: 'work'
},
dataType: 'html',
url: 'http://dev.xxx.se/wp-content/themes/xxx/batch.php',
beforeSend: function() {
_setHeader;
if( page != 1 ) {
console.log('Loading');
// Show the preloader
$('body').prepend('<div class="preloader"><span class="rotate"></span></div>');
}
// If we reach the end we hide the show more button
if( page >= total ) {
$('.load').hide();
}
},
success: function(data) {
console.log(page);
var scroll = ($('.thumb').height() * posts);
// If thumbs exist append them
if( data.length ) {
// Append the data
$('#batch').append(data);
// Remove the crappy width and height attrs from the image * Generated by WP *
$('img').removeAttr('height').removeAttr('width');
// Animate each new object in a nice way
(function _showitem() {
$('#batch .thumb:hidden:first').addClass('show', 80, _showitem);
// On the last request do load any more
loading = false;
})();
// Remove the preloader
$('.preloader').fadeOut(200, function() {
$('.preloader').remove();
});
}
// return false;
},
complete: function() {
// Delete storage
localStorage.clear();
// Update the scroller to match the updated content length
if (scroller)
setTimeout("scroller.refresh()", 300);
// Initalize the load more button
_clickhandler();
},
error: function() {
console.log('No page found');
}
});
}
and my load more button function
$('.load').on('click', function(event) {
event.preventDefault();
// Delete storage
localStorage.clear();
if(!loading) {
loading = true;
// Increase our pagenumber per click
page++;
count++;
// Remove preloader
$('.preloader').remove();
setTimeout(function() {
$('#batch').css({
'-webkit-transform' : 'translateY(-' + ($('#batch li').outerHeight() * count) + 'px)'
});
}, 30);
// Clear storage and set a new
localStorage.setItem('amount', JSON.stringify(amount.medium));
var getamount = localStorage.getItem('amount');
// Send the request to the handler
_batchhandler(page);
}
});
Everything seems fine, the first 10 (1-10) posts loads as the should, but the first time "load more" is clicked we get the next 5 results but the results are posts that loaded the first time (5-10). If we click the "load more" again, we get the correct result
I don't think you have defined a start value for page, set it to 1 at the start of your script so when it increments on click it goes to page 2. Otherwise it'll just get the first page.

How to use a normal pagination with jQuery Isotope?

I've implemented Isotope this way http://jsfiddle.net/circlecube/LNRzZ/ to my Joomla website. Sorting and filtering work perfeclty but only in the current page.
I would like to sort/filter all items but display only 20 items per page. I wish to keep a numeric navigation, like this http://tutorials.vinnysingh.co/quicksand/.
Can Isotope handle this or should I use another plugin? Any help is greatly appreciated. Thanks in advance.
Here is my full code:
jQuery( document ).ready( function ($) {
// cache container
var $container = $('#container');
// initialize isotope
$container.isotope({
getSortData : {
author : function ( $elem ) {
return $elem.find('.author').text();
},
city : function ( $elem ) {
return $elem.find('.city').text();
},
country : function ( $elem ) {
return $elem.find('.country').text();
},
price : function( $elem ) {
return parseFloat( $elem.find('.price').text().replace( /[\(\)]/g, '') );
},
rating : function ( $elem ) {
return parseInt( $elem.find('.rating').text(), 10 );
},
review : function ( $elem ) {
return parseInt( $elem.find('.review').text(), 10 );
},
perfDate: function (element) {
// parse out the performance date from the css classes
var classList = element.attr('class').split(/\s+/);
var dateClassPrefix = 'date-';
var date;
$.each(classList, function(index, cssClassName){
if (cssClassName.substring(0, dateClassPrefix.length) === dateClassPrefix) {
// Should be a date in format 'yyyy-MM-dd'
var dateString = cssClassName.substring(dateClassPrefix.length);
date = SF.parseDate('dd/mm/yyyy').getTime();
}
});
return date;
}
}
});
$('#sort-by a').click(function(){
// get href attribute, minus the '#'
var sortName = $(this).attr('href').slice(1);
$('#container').isotope({ sortBy : sortName });
return false;
});
// filter items when filter link is clicked
$('#filters a').click(function(){
var selector = $(this).attr('data-filter');
$container.isotope({ filter: selector });
return false;
});
var $optionSets = $('#options .option-set'),
$optionLinks = $optionSets.find('a');
$optionLinks.click(function(){
var $this = $(this);
// don't proceed if already selected
if ( $this.hasClass('selected') ) {
if ($this.hasClass('lock')){
//return false;
}
else if ($this.hasClass('asc')){
$this.removeClass('asc').addClass('desc');
}
else if ($this.hasClass('desc')){
$this.removeClass('desc').addClass('asc');
}
}
var $optionSet = $this.parents('.option-set');
$optionSet.find('.selected').removeClass('selected');
$this.addClass('selected');
// make option object dynamically, i.e. { filter: '.my-filter-class' }
var options = {},
key = $optionSet.attr('data-option-key'),
value = $this.attr('data-option-value');
// parse 'false' as false boolean
value = value === 'false' ? false : value;
options[ key ] = value;
if ($this.hasClass('asc') || $this.hasClass('desc'))
options[ 'sortAscending' ] = $this.hasClass('asc');
if ( key === 'layoutMode' && typeof changeLayoutMode === 'function' ) {
// changes in layout modes need extra logic
changeLayoutMode( $this, options )
} else {
// otherwise, apply new options
$container.isotope( options );
}
return false;
});
});
Isotope doesn't do pagination. You'll need to implement the pagination in Joomla.

using jquery to validate postal code based on country

Working on site where we currently accept US zip codes. I already have jquery validate working for that. What i want to do now is add a drop down for the user to select country, and then based on the country they selected, it will validate the postal code to make sure it matches.
Can someone give me some pointers on how i can do this? Basically, i just need to change the regex the validator function is using based on the country drop down. The (what i assume is) relevant section of the jquery validator function is this:
(function ($) {
$.fn.validate = function (options) {
var defaults = {
invalidClass: 'error',
rules: {
req: /.{1,}/g,
email: /[\w\.=-]+#[\w\.-]+\.[\w]{2,3}/g,
phone: /\D?(\d{3})\D?\D?(\d{3})\D?(\d{4})/g,
zip: /\d{5}$|^\d{5}-\d{4}/g,
//password: /^(?=.*\d)(?=.*[a-zA-Z]).{8,20}$/g,
password: /^(?=.{8,20}$)(?=.*\d)(?=.*[a-zA-Z]).*/g,
//nospecialchars: /[^<>?,\/\\\[\]\{\}\|!##$%^&*()_+;:"]{1,}/g
nospecialchars: /^(?!.*[?<>;]).+$/g
},
error_messages: {
req: 'Oops..',
email: 'Please enter your email address.',
phone: '',
zip: 'Please give me a valid zip.',
max: 'too many characters.',
password: '',
nospecialchars: ''
},
success: null,
failure: null
},
errors = [],
opts = $.extend(true, defaults, options);
return this.each(function () {
var $this = $(this);
$(this).find('input[type="submit"]:not(.cancel), button').click(function () {
errors = [];
validate_fields($this);
if ($this.find('.error').length === 0) {
if (typeof opts.success == 'function')
return opts.success();
}
else {
if (typeof opts.failure == 'function')
return opts.failure(errors);
}
});
});
I'm not very familiar with jquery so i don't know what the syntax is here for me to create an if-else or case statement to set the regex.
Thanks if advance for any help.
edit: Here's the bit of code that actually calls the validation
<script type="text/javascript">
$(function () {
setForm();
$('form').validate({
error_messages: {
req: null
},
failure: function (errors) {
var sel = '';
$(".errMsg").hide();
for (x in errors) {
sel += ',#' + errors[x][0];
}
if (sel.length > 0) {
sel = sel.substring(1);
$(sel).parent().find(".errMsg").show(0, function () {
$('#home .orange_box, #home .HomeRight').height('auto');
$('#infov .white_box, #infov .orangeRight').height('auto');
$('#findt .orange_box, #findt .HomeRight').height('auto');
if ($.browser.msie && $.browser.version == 8) {
evenColumns('#home .orange_box', '#home .HomeRight', -16);
evenColumns('#infov .white_box', '#infov .orangeRight', -16);
evenColumns('#findt .orange_box', '#findt .HomeRight', -16);
}
else {
evenColumns('#home .orange_box', '#home .HomeRight', -15);
evenColumns('#infov .white_box', '#infoforv .orangeRight', -15);
evenColumns('#findt .orange_box', '#findt .HomeRight', -15);
}
});
}
return false;
},
success: function () {
$(".errMsg").hide();
return true;
}
});
I would create an object, witch has:
function for the validation
object for all other countries
object for default rules
validation = {
validate : function( sCountryName, sToValidate ){
return ( null != sToValidate.match( this.countries[ sCountryName ] ? this.countries[ sCountryName ].zip : this.defaultRules.zip ))
},
countries : {
england : {
zip : /\d{5}$|^\d{5}-\d{4}/g
}
},
defaultRules : {
zip : /\d{5}$|^\d{5}-\d{4}/g
}
}

Resources