How to use Magiczoom Callbacks - magiczoom

The magiczoom documentation describes callbacks that will execute at given times, but it's unclear how to use or assign those callbacks.
For example, how would I print a console message onZoomReady?
The closest I've found is a MagicZoom.defaults.onready property, but it's unclear how to set it via javascript (my attempts aren't working as expected).

The callbacks are configured via mzOptions, for example:
var mzOptions = {
onZoomReady: function() { … } }
;
Or:
var mzOptions = {};
mzOptions.onZoomReady = function() { … };

You can do something like this:
MagicZoom.registerCallback('onUpdate',
function() {
console.log('onUpdated', arguments[0], arguments[1], arguments[2]);
});
That will log stuff in the console like this:
onUpdated (id-of-mz-wraper) (html of old element) (html of new element)
Other options that you can use are as per the documentation:
MagicZoom.registerCallback('onZoomReady', function() {
console.log('onReady', arguments[0]);
});
MagicZoom.registerCallback('onZoomIn', function() {
console.log('onZoomIn', arguments[0]);
});
MagicZoom.registerCallback('onZoomOut', function() {
console.log('onZoomOut', arguments[0]);
});

Related

Running into Error while waiting for Protractor to sync with the page with basic protractor test

describe('my homepage', function() {
var ptor = protractor.getInstance();
beforeEach(function(){
// ptor.ignoreSynchronization = true;
ptor.get('http://localhost/myApp/home.html');
// ptor.sleep(5000);
})
describe('login', function(){
var email = element.all(protractor.By.id('email'))
, pass = ptor.findElement(protractor.By.id('password'))
, loginBtn = ptor.findElement(protractor.By.css('#login button'))
;
it('should input and login', function(){
// email.then(function(obj){
// console.log('email', obj)
// })
email.sendKeys('josephine#hotmail.com');
pass.sendKeys('shakalakabam');
loginBtn.click();
})
})
});
the above code returns
Error: Error while waiting for Protractor to sync with the page: {}
and I have no idea why this is, ptor load the page correctly, it seem to be the selection of the elements that fails.
TO SSHMSH:
Thanks, your almost right, and gave me the right philosophy, so the key is to ptor.sleep(3000) to have each page wait til ptor is in sync with the project.
I got the same error message (Angular 1.2.13). My tests were kicked off too early and Protractor didn't seem to wait for Angular to load.
It appeared that I had misconfigured the protractor config file. When the ng-app directive is not defined on the BODY-element, but on a descendant, you have to adjust the rootElement property in your protractor config file to the selector that defines your angular root element, for example:
// protractor-conf.js
rootElement: '.my-app',
when your HTML is:
<div ng-app="myApp" class="my-app">
I'm using ChromeDriver and the above error usually occurs for the first test. I've managed to get around it like this:
ptor.ignoreSynchronization = true;
ptor.get(targetUrl);
ptor.wait(
function() {
return ptor.driver.getCurrentUrl().then(
function(url) {
return targetUrl == url;
});
}, 2000, 'It\'s taking too long to load ' + targetUrl + '!'
);
Essentially you are waiting for the current URL of the browser to become what you've asked for and allow 2s for this to happen.
You probably want to switch the ignoreSynchronization = false afterwards, possibly wrapping it in a ptor.wait(...). Just wondering, would uncommenting the ptor.sleep(5000); not help?
EDIT:
After some experience with Promise/Deferred I've realised the correct way of doing this would be:
loginBtn.click().then(function () {
ptor.getCurrentUrl(targetUrl).then(function (newURL){
expect(newURL).toBe(whatItShouldBe);
});
});
Please note that if you are changing the URL (that is, moving away from the current AngularJS activated page to another, implying the AngularJS library needs to reload and init) than, at least in my experience, there's no way of avoiding the ptor.sleep(...) call. The above will only work if you are staying on the same Angular page, but changing the part of URL after the hashtag.
In my case, I encountered the error with the following code:
describe("application", function() {
it("should set the title", function() {
browser.getTitle().then(function(title) {
expect(title).toEqual("Welcome");
});
});
});
Fixed it by doing this:
describe("application", function() {
it("should set the title", function() {
browser.get("#/home").then(function() {
return browser.getTitle();
}).then(function(title) {
expect(title).toEqual("Welcome");
});
});
});
In other words, I was forgetting to navigate to the page I wanted to test, so Protractor was having trouble finding Angular. D'oh!
The rootElement param of the exports.config object defined in your protractor configuration file must match the element containing your ng-app directive. This doesn't have to be uniquely identifying the element -- 'div' suffices if the directive is in a div, as in my case.
From referenceConf.js:
// Selector for the element housing the angular app - this defaults to
// body, but is necessary if ng-app is on a descendant of <body>
rootElement: 'div',
I got started with Protractor by watching the otherwise excellent egghead.io lecture, where he uses a condensed exports.config. Since rootElement defaults to body, there is no hint as to what is wrong with your configuration if you don't start with a copy of the provided reference configuration, and even then the
Error while waiting for Protractor to sync with the page: {}
message doesn't give much of a clue.
I had to switch from doing this:
describe('navigation', function(){
browser.get('');
var navbar = element(by.css('#nav'));
it('should have a link to home in the navbar', function(){
//validate
});
it('should have a link to search in the navbar', function(){
//validate
});
});
to doing this:
describe('navigation', function(){
beforeEach(function(){
browser.get('');
});
var navbar = element(by.css('#nav'));
it('should have a link to home in the navbar', function(){
//validate
});
it('should have a link to search in the navbar', function(){
//validate
});
});
the key diff being:
beforeEach(function(){
browser.get('');
});
hope this may help someone.
I was getting this error:
Failed: Error while waiting for Protractor to sync with the page: "window.angular is undefined. This could be either because this is a non-angular page or because your test involves client-side navigation, which can interfere with Protractor's bootstrapping. See http://git.io/v4gXM for details"
The solution was to call page.navigateTo() before page.getTitle().
Before:
import { AppPage } from './app.po';
describe('App', () => {
let page: AppPage;
beforeEach(() => {
page = new AppPage();
});
it('should have the correct title', () => {
expect(page.getTitle()).toEqual('...');
})
});
After:
import { AppPage } from './app.po';
describe('App', () => {
let page: AppPage;
beforeEach(() => {
page = new AppPage();
page.navigateTo();
});
it('should have the correct title', () => {
expect(page.getTitle()).toEqual('...');
})
});
If you are using
browser.restart()
in your spec some times, it throws the same error.
Try to use
await browser.restart()

jQuery wait for an AJAX request to be completed before refreshing the page

I've got the following jQuery:
$("#delete_products").click(function() {
$(":checkbox:checked").each(function() {
var pid = $(this).val();
$.get('delete_product.php',{pid: pid});
});
location.reload();
});
There is a problem with this since the page doesn't wait for the AJAX request to be completed (MULTIPLE AJAX REQUESTS), and refreshes the page immediately and makes the AJAX request to not run and fail.
How can I do that the page will only refresh when it done loading?
I've been given this code:
$("#delete_products").click(function () {
var promises = [];
$(":checkbox:checked").each(function () {
var pid = $(this).val();
promises.push($.get('delete_product.php', {
pid: pid
}));
});
$.when.apply($, promises).done(function () {
location.reload();
});
return false;
});
But this solution just doesn't work.
any suggestions?
your code seems like it should work, but i would recommend to delete all products with one call by passing array of ids.
less work for the browser, less work for the server, faster results.
UPDATED ANSWER
$("#delete_products").click(function () {
var ids = [];
$(":checkbox:checked").each(function () {
ids.push($(this).val());
});
$.post('delete.php', { 'ids': ids } }).done(function() {
alert('hells yeah!');
});
return false;
});
and as for the server side:
$commaSeperatedIds = explode(',', $_POST['ids']);
mysql_query('DELETE FROM products WHERE id IN('.mysql_real_escape_string($commaSeperatedIds).')');
Use "success" parameter of "get"
EDIT: add counter of requests.
total_requests = $(":checkbox:checked").length;
total_success = 0;
...
$.get('delete_product.php',{pid: pid}, function (data, status, xx) {
...
total_success++;
if (total_success >= total_requests) {
location.reload();
}
});
...
Possible Solution #1:
The async parameter could help. Set it to false.
Since you`re using the $.get() function, this is done with:
$.ajaxSetup({
async: false
});
 
With the $.ajax() function, you'd simply set it like:
$.ajax({
url: ...,
async: false,
success: function(data) {}
});
More info on this can be found here.
 
Possible Solution #2:
Use the .success() callback hook OR .complete() if you want to refresh the page no mather if the request failed or not.
$.get('delete_product.php',{pid: pid}).success(function(response)
{
location.reload();
});
 
Happy coding!
 
Edit:
The questioner seems to prever sUP's answer. I'd like to provide an example of how to achieve the desired functionality with jQuery:
$("#delete_products").click(function()
{
var products = [];
$(":checkbox:checked").each(function()
{
var pid = $(this).val();
products.push(pid);
});
$.post('delete_products.php', {'products': products}).done(function()
{
location.reload();
});
});
 
If you prefer to use JSON for the post data, try:
$('#delete_products').click(function()
{
var products = [];
$(':checkbox:checked').each(function()
{
products.push($(this).val());
});
// Convert the products array into JSON
products = JSON.stringify(products);
$.post('delete_products.php', {'products': products}).done(function()
{
location.reload();
});
});
In PHP you need to parse the json string as follows:
<?php
// This creates an associative array from the JSON string
$delete_products = json_decode($_POST['products'], true);
// Use explode to make a comma separated string from the array
// for use in a SQL SELECT query:
$delete_products = explode(',', $delete_products);
Info about json_decode can be found in the PHP Manual.
JSON.stringify is not supported in older browsers. Include JSON-js if you need cross browser support.
I too believe that the desired result can probably be achieved best by collecting the product IDs and then sending a single Ajax call to take care of them all.
But since the OP put the interesting question forward of how to handle multiple Ajax requests and wait for them all to be finished I have looked at the when() method again and it seems to me that the original syntax is still faulty.
According to the jQuery manual when() is a method and therefore requires to be called with one or more argument(s) in parentheses. I have not worked with promises yet and I have not tested anything but I assume that something like the following might bring at least a different result:
$("#delete_products").click(function () {
var promises = [];
$(":checkbox:checked").each(function () {
var pid = $(this).val();
promises.push($.get('delete_product.php', {
pid: pid
}));
});
$.when(promises).done(function () {
location.reload();
});
return false;
});
As I said before, I still have not quite grasped the promises mechanisms/syntax yet ...
In the original version $.when does not have any meaningful context to work on, The apply() method does provide context but only after when has done its (unseccessful) work already.

Attach the events ajaxStart() and ajaxStop() only to the current backbone view

I'm using backbone for an app that I'm building. In this app, I have a master view which render a template with 2 other views inside. One header view and another one with some content. The header view is just used to interact with the content view and has specific functions too.
In the header template and content template I have the same piece of code, an hidden DIV with a loader image that is displayed when an ajax call is made. The problem I have is that when I load the app for the first time (or when I refresh the content view), the content view is loading some data from an ajax request, but the loader is showing up in both the header and the content template (like if the ajaxStart() was a global event not attached to the view.
Here is the content view setup:
App.View.Content = Backbone.View.extend({
type:'test',
template: twig({
href: '/js/app/Template/Content.html.twig',
async: false
}),
block:{
test:twig({
href: '/js/app/Template/block/test.html.twig',
async: false
})
},
list:[],
showLoader: function(el){
console.log('loader: ', $('#ajax_loader', el));
$('#ajax_loader', el).show();
console.log('Ajax request started...');
},
hideLoader: function(el){
$('#ajax_loader', el).hide();
console.log('Ajax request ended...');
},
initialize: function(params)
{
this.el = params.el;
this.type = params.type || this.type;
var self = this;
this.el
.ajaxStart(function(){self.showLoader(self.el);})
.ajaxStop(function(){self.hideLoader(self.el);});
this.render(function(){
self.list = new App.Collection.ListCollection();
self.refresh(1, 10);
});
},
refresh:function(page, limit)
{
var self = this;
console.log('Refreshing...');
$('#id-list-content').fadeOut('fast', function(){
$(this).html('');
});
this.list.type = this.type;
this.list.page = page || 1;
this.list.limit = limit || 10;
this.list.fetch({
success: function(data){
//console.log(data.toJSON());
$.each(data.toJSON(), function(){
//console.log(this.type);
var tpl_block = self.block[this.type];
if (tpl_block != undefined) {
var block = tpl_block.render({
test: this
});
$(block).appendTo('#id-list-content');
}
});
$('#id-list-content').fadeIn('fast');
}
});
},
render: function(callback)
{
console.log('Rendering list...');
this.el.html(this.template.render({
}));
if (undefined != callback) {
callback();
}
}
});
As you can see I'm using an ugly piece of code to attach the ajaxStart / ajaxStop event:
this.el
.ajaxStart(function(){self.showLoader(self.el);})
.ajaxStop(function(){self.hideLoader(self.el);});
I use to have it like this:
this.el
.ajaxStart(self.showLoader())
.ajaxStop(self.hideLoader());
But for whatever reason that still undefined on my end, this.el was not defined in the showLoader() and hideLoader().
I was thinking that ajaxStart() and ajaxStop() was attached to the this.el DOM, and that only this view would be able to listen to it. But my headerView which has exactly the same setup (except for the twig template loaded) apparently receive the event and show the loader.
To be sure of this behavior, I've commented out the showLoader() in the content view, and the loader still show up in the header view.
I don't know what I'm doing wrong :(
EDIT (after answer from "mu is too short"):
my content view does now looks like this:
showLoader: function(){
//this.$('#ajax_loader').show();
console.log('Ajax request started...');
},
hideLoader: function(){
this.$('#ajax_loader').hide();
console.log('Ajax request ended...');
},
initialize: function(params)
{
var self = this;
console.log(this.el);
_.bindAll(this, 'showLoader', 'hideLoader');
this.$el
.ajaxStart(this.showLoader)
.ajaxStop(this.hideLoader);
this.render(function(){
self.list = new App.Collection.List();
self.refresh(1, 10);
});
},
...
render: function(callback)
{
console.log('Rendering post by page...');
this.$el.html(this.template.render({
}));
if (undefined != callback) {
callback();
}
}
and my header view:
...
showLoader: function(){
this.$('#ajax_loader').show();
//console.log('Ajax request started...');
},
hideLoader: function(el){
this.$('#ajax_loader').hide();
console.log('Ajax request ended...');
},
initialize: function(params)
{
var self = this;
_.bindAll(this, 'showLoader', 'hideLoader');
this.$el
.ajaxStart(this.showLoader)
.ajaxStop(this.hideLoader);
this.models.Link = new App.Model.Link();
this.render();
},
render: function(callback)
{
this.$el.html(this.template.render({
data: []
}));
if (undefined != callback) {
callback();
}
}
...
But the loader still showing up in the header view template
PS: this.showLoader() was not a typo as I wanted to call the function within the current backbone view.
The context (AKA this) for a JavaScript function depends on how the function is called, not on the context in which the function is defined. Given something like this:
var f = o.m;
f();
When you call o.m through the plain function f, this inside o.m will usually be the global context (window in a browser). You can also use apply and call to choose a different this so this:
f.call(o);
would make this the o that you'd expect it to be. I should mention that you can force your choice of this using bind in most JavaScript environments but I don't want to get too sidetracked.
The point is that this:
this.el
.ajaxStart(this.showLoader)
.ajaxStop(this.hideLoader);
isn't enough to ensure that showLoader and hideLoader will run in the right context; I'm also assuming that the parentheses you had at the end of showLoader and hideLoader were just typos.
The most common way to force a context in a Backbone application is to use _.bindAll in your initialize:
initialize: function(params) {
_.bindAll(this, 'showLoader', 'hideLoader');
//...
That essentially replaces this.showLoader and this.hideLoader with something that's, more or less, equivalent to your wrappers:
function() { self.showLoader(self.el) }
Once you have that _.bindAll in place, this:
this.el
.ajaxStart(this.showLoader)
.ajaxStop(this.hideLoader);
will work fine.
BTW, you don't need to do this:
this.el = params.el;
in your initialize, Backbone does that for you:
constructor / initialize new View([options])
[...] There are several special options that, if passed, will be attached directly to the view: model, collection, el, id, className, tagName and attributes.
And you don't need to do things like this:
$('#ajax_loader', el).show();
either, Backbone gives you a $ method in your view that does the same thing without hiding the el at the end of the argument list; doing it like this:
this.$('#ajax_loader').show();
is more idiomatic in Backbone.
Furthermore, this.el won't necessarily be a jQuery object so don't do this:
this.el.html(this.template.render({ ... }));
in your render, use the cached this.$el instead:
this.$el.html(this.template.render({ ... }));

Handling MVC action result using JQuery post method

I am calling MVC action something like below:
var RestaurantDetailsUIforForeignWidget = {
frmId: '',
onFormSubmit: function () {
var frm = $(RestaurantDetailsUIforForeignWidget.frmId);
var divResult;
$('.offerbox').hide();
$.post(frm.attr('action'), frm.serialize(), function (html) {
// $('#section-time-slots').html(html)
$('#contentAll').html(html);
DisplayOffer();
});
return false;
},
updateTimeDesc: function () {
$('#time').val($('#SittingTime option:selected').html());
},
init: function (frmId) {
RestaurantDetailsUIforForeignWidget.frmId = frmId;
$('#SittingTime').bind('change', RestaurantDetailsUIforForeignWidget.updateTimeDesc);
$(frmId).bind('submit', RestaurantDetailsUIforForeignWidget.onFormSubmit);
RestaurantDetailsUIforForeignWidget.updateTimeDesc();
}
};
As you can see $('#contentAll').html(html); updates whole view content with the result. What I want is to get a single div from html output and update the $('#section-time-slots') instead.
Please help me guys... thanks :)
Try this:
$.post(frm.attr('action'), frm.serialize(), function (html) {
$('#section-time-slots').html($("#IdOfRequiredElementInResponse", html).html());
DisplayOffer();
});

jplayer+Ajax inserted content

I am using jPlayer to play audio files.
If I use the player on content, which is privided, when the page gets loaded, it works without any problems.
I also need it for HTML which is inserted by AJAX. Here it does not work. It seems, that the ready event is not triggered.
I wrote a function, which can be executed by click(). In that way, I can click it manually, when the HTML which contains the player is fully loaded. Here I have the same problem: The ready event is not triggered.
This is my function which works on non ajax inserted players fine:
$('.jp-jplayer').each(function () {
var src = $(this).attr('data-src');
var id = $(this).attr('id');
var post_id = $(this).attr('data-id');
alert('beg');
$('#' + id).jPlayer({
ready: function () {
$(this).jPlayer('setMedia', {
mp3: "/prelisten/_lofidl/change_of_heart_full_lofi.mp3",
});
alert('#' + id);
},
swfPath: "/wp-content/themes/Dark_3Chemical_DE_mit_Pagenavi/Dark_3Chemical_DE/audioplayer/js",
//////ERRRROOOOOR
solution: "flash, html",
supplied: "mp3",
wmode: "window",
cssSelectorAncestor: "#jp_container_" + post_id,
play: function () { // To avoid both jPlayers playing together.
$(this).jPlayer("pauseOthers");
},
repeat: function (event) { // Override the default jPlayer repeat event handler
if(event.jPlayer.options.loop) {
$(this).unbind(".jPlayerRepeat").unbind(".jPlayerNext");
$(this).bind($.jPlayer.event.ended + ".jPlayer.jPlayerRepeat", function () {
$(this).jPlayer("play");
debug($(this));
});
} else {
$(this).unbind(".jPlayerRepeat").unbind(".jPlayerNext");
$(this).bind($.jPlayer.event.ended + ".jPlayer.jPlayerNext", function () {
//$("#jquery_jplayer_4858").jPlayer("play", 0);
});
}
},
});
$("#jplayer_inspector").jPlayerInspector({
jPlayer: $('#' + id)
});
});
Currently I am setting the src manually to exclude any possible errors here.
How can I get this function running on AJAX inserted content?
EDIT:
This is the code, which fetches the html including the players:
$.get('/query_posts.php', {
paged: _page,
cats: cols
}, function(data) {
$('#search-results').append(data).fadeIn(300);
//create_player_scripts();
//set_players();
$('#search-results').find('input[name="cartLink"]').each(function() {
$(this).val($(this).closest('.post1').find('.post_headl a').attr('href'));
});
});
To make an AJAX page reload work I had to first destroy all jplayer instances. So I wrote a little function that grabs all instances of a jplayer on the site (by looking for jp-audio classes) and calls jplayer('destroy'); and jplayer('clearMedia'). This function gets called in the $.ajax({ beforeSend: destroyJplayerInstances(); })
UPDATE:
Here is a statement from the developer of jPlayer, Mark Panaghiston:
https://groups.google.com/forum/#!topic/jplayer/Q_aRhiyYvQo
Hope that helps!

Resources