Does Hammer 2.0 allow me to to everything I do in 1.0.5? - hammer.js

My HTPC project (open source) uses Hammer 1.0.5 extensively - 32 separate hammer instances in many pages; 184 different event handlers; handles many different event types (tap, doubletap, touch, release, hold, drag, dragup, dragdown, swipeleft, swiperight, swipeup, swipedown). All the event handlers use delegation.
When I found I had a glitch on Safari on an iPad (every other touch is ignored), I came to look for anyone with the same problem and discovered I had missed the release of version 2. So ...
Is it wise to upgrade?
Can I do everything in version 2 that I could with version 1?
Is the upgrade fairly mechanical? (Set options to enable capability, use new names for events)?
I can't find any guidance for upgrading in the hammer documentation, which is a shame.
Thanks
Brian

Well the simple answer is that "No I can't easily port my Hammer 1.x implementation to 2.0".
Below is a snippet of one of my HTPC JS files, that adds actions to a <div> containing a EPG listing of TV programmes. This works very well with 1.x.
var listingsHammer = null;
function AddListingsHammerActions() {
$("#guideBrowserItems").each(function () {
var h = $(window).height() - 24
var t = $(this).offset().top
console.log("#guideBrowserItems h=" + h + "; t=" + t + " => " + (h - t))
$(this).height(h - t)
});
if (!listingsHammer) {
listingsHammer = $(".guideBrowserItems").hammer({ prevent_default: true });
}
EnableDragScroll(listingsHammer)
listingsHammer.on("tap", ".guideEpgProgramme", function (e) {
var programItem = this;
$(".guideEpgProgrammeDescription").remove();
$(".guideEpgProgrammeCancel").remove();
$(".guideEpgProgrammeRecord").remove();
$(".guideEpgProgrammeRecordSeries").remove();
$.ajax({
url: "/Guide/Description?id=" + programItem.id,
success: function (description) {
if (hasClass(programItem, "guideEpgProgrammeScheduled")) {
$(programItem).prepend('<img class="guideEpgProgrammeCancel" id="' + programItem.id + '" src="/Content/Buttons/SmallRound/Exit.png" />')
} else {
$(programItem).prepend('<img class="guideEpgProgrammeRecordSeries" id="' + programItem.id + '" src="/Content/Buttons/SmallRound/Transport.Rec.Series.png" />')
$(programItem).prepend('<img class="guideEpgProgrammeRecord" id="' + programItem.id + '" src="/Content/Buttons/SmallRound/Transport.Rec.png" />')
}
$(programItem).append('<div class="guideProgrammeInfo guideEpgProgrammeDescription">' + description + '</div>')
cache: false
}
})
});
listingsHammer.on("tap", ".guideEpgProgrammeRecord", function (e) {
var programItem = this;
$.ajax({
url: "/Guide/Record?id=" + programItem.id,
success: function (error) {
if (error == "") {
$(".guideSelectorItems").html("")
ReplacePane("guideBrowserItems", "/Guide/ListingsPane?mode=GuideSchedule", "none")
}
else {
alert(error)
}
cache: false
}
})
});
listingsHammer.on("tap", ".guideEpgProgrammeRecordSeries", function (e) {
var programItem = this;
$.ajax({
url: "/Guide/RecordSeries?id=" + programItem.id,
success: function (error) {
if (error == "") {
$(".guideSelectorItems").html("")
ReplacePane("guideBrowserItems", "/Guide/ListingsPane?mode=GuideSchedule", "none")
}
else {
alert(error)
}
cache: false
}
})
});
listingsHammer.on("tap", ".guideEpgProgrammeCancel", function (e) {
var programItem = this;
$.ajax({
url: "/Guide/Cancel?id=" + programItem.id,
success: function (error) {
if (error == "") {
$(".guideSelectorItems").html("")
ReplacePane("guideBrowserItems", "/Guide/ListingsPane?mode=GuideSchedule", "none")
}
else {
alert(error)
}
cache: false
}
})
});
listingsHammer.on("tap", ".guideEpgSeriesCancel", function (e) {
var seriesItem = this;
$.ajax({
url: "/Guide/CancelSeries?id=" + seriesItem.id,
success: function () {
$(".guideSelectorItems").html("")
ReplacePane("guideBrowserItems", "/Guide/ListingsPane?mode=GuideSeries", "none")
cache: false
}
})
});
}
There are a few things to note.
I only use one Hammer for the entire <div>, with delegation to each of the individual guideEpgProgramme sub-elements, so that when I touch a listed programme, the handler acts on that one sub-element.
This allows me to dynamically replace the contents of the <div> via Ajax (e.g. for another channel), with no need to re-register handlers.
The Record and Cancel button images are dynamically added and removed as needed and automatically gain the desired pre-registered actions.
The Hammer object is passed to a common utility function EnableDragScroll, which adds handlers for scrolling the (long, variable length) contents within the fixed size guideBrowserItems page area.
There are many instances of ReplacePane("guideBrowserItems", <NEW URL>) to dynamically alter the contents of the <div> without affecting the rest of the page or the script.
And this is just one of the 30+ different Hammer instance with corresponding handling.
None of this way of working appears to be possible in Hammer 2.0.
So for now I will stick with the old mechanisms.

Related

XMLHTTPRequest dealing with event

I am performing an Ajax (JQuery) request on a php script which iterates. At each iteration I do an "echo" of the value of the loop counter, encoded in JSon. The goal is to manage the display of a progress bar.
By doing this I hoped to intercept and retrieve each of the counter values in a separate response thanks to the "progress" event.
Actually I only have one answer which contains all the counter values.
My research always leads me to information about downloading files, which is not my situation.
Maybe I should use the "onreadystatechange" event, but I don't see how to fit it into my code: $ Ajax parameter or separate function?
If anyone has an idea to solve my problem.
Here is my JS code
function DiffuseOffre(envoi, tab, paquet, dest) {
//$('#cover-spin').show(0);
$("#xhr-rep").css("display", "block");
var server = '/Admin/Offres/DiffuseOffre.php';
$.ajax({
url: server,
type: 'Post',
dataType: 'json',
data: {
envoi: envoi,
tab: tab,
paquet: paquet,
dest: dest
},
xhr: function() {
var xhr = new window.XMLHttpRequest();
xhr.addEventListener("progress", function(evt) {
if (evt.lengthComputable) {
var compteur = evt.text;
$("#xhr-rep").html(compteur);
}
}, false);
return xhr;
},
success: function(msg) {
//$('#cover-spin').css("display", "none");
$('#xhr-rep').css("display", "none");
if (msg.hasOwnProperty('erreur')) {
$("#dialog-erreur").html(msg.erreur);
$("#dialog-erreur").dialog("open");
$("#dialog-erreur").dialog({
width: '600px',
title: msg.title
})
} else {
$("#dialog-message").html(msg.message);
$("#dialog-message").dialog("open");
$("#dialog-message").dialog({
width: '600px',
title: msg.title
})
if (paquet == 1) {
$("#envoi_" + dest).remove();
$("#diffuser").remove();
}
if (msg.hasOwnProperty('encours')) {
$("#en_cours").html(msg.encours);
}
if (msg.hasOwnProperty('fieldset')) {
$("#" + msg.fieldset).remove();
}
}
}
})
}

knockout computed not updating while change in view model being made from ajax

How much I know about knockout Js is that a computed gets updated anyhow depending on the viewmodel, but in my case its not happening. So basically I have a radio button which turns off and on and changes the date in the database, and the ajax calls returns and pushes the new date in the viewmodel so that the data changes.
So thats the summary. But the thing I want is that while the radio button is being updated I want a part of the html to change to active or disabled based on the radio button.
Firstly here is the HTML code.
<div class="col-sm-4">
<p>
<span data-bind="text : $data.basketStatusValue"></span>
</p>
</div>
<div class="col-sm-4">
<div class="on_off">
<input type="checkbox" data-bind="bootstrapSwitchOn: {
tocall: $root.changeActiveBasketStatus
}" />
</div>
</div>
Here is the JS code.
function MoneyInvestedViewModel(root /* root not needed */, money) {
var self = this;
self.ID = money.ID;
self.ORIG_ID = money.ORIG_ID;
self.Available = money.Available;
self.basketStatusValue = ko.computed (function () {
if (self.Available == '9999-12-01') {
return "Active";
} else {
return "Disabled";
}
});
};
And next is the code which is updating the view model moneyInvested . So the checkbox can show on or off.
self.changeActiveBasketStatus = function (bindingContext) {
console.log(bindingContext);
var Id = bindingContext.$data.ORIG_ID;
var Available = bindingContext.$data.Available;
if (Available == '9999-12-01') {
$.ajax({
type: 'POST',
url: BASEURL + 'index.php/moneyexchange/changeBasketStatus/' + auth + "/" + Id + "/" + 1,
contentType: 'application/json; charset=utf-8'
})
.done(function (newAvailableDate) {
bindingContext.$data.Available = newAvailableDate;
})
.fail(function (jqXHR, textStatus, errorThrown) {
self.errorMessage(errorThrown);
})
.always(function (data){
});
} else {
$.ajax({
type: 'POST',
url: BASEURL + 'index.php/moneyexchange/changeBasketStatus/' + auth + "/" + Id + "/" + 0,
contentType: 'application/json; charset=utf-8'
})
.done(function (newAvailableDate) {
bindingContext.$data.Available = newAvailableDate;
})
.fail(function (jqXHR, textStatus, errorThrown) {
self.errorMessage(errorThrown);
})
.always(function (data) {
});
}
};
So basically the PROBLEM is that when all this update is done, the computed self.basketStatusValue does not get updated. So when I click the checkbox on, it doesnt show Active, or off for disabled, the checkbox is working perfectly, only the html $data.basketStatusValue is not updating through the computed function.
Just incase if necessary here is the code for the checkbox.
(function ($) {
ko.bindingHandlers.bootstrapSwitchOn = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
var options = ko.utils.unwrapObservable(valueAccessor());
var tocall = ko.utils.unwrapObservable(options.tocall);
$elem = $(element);
$(element).bootstrapSwitch();
$(element).bootstrapSwitch('setState', bindingContext.$data.Available === '9999-12-01' ? true : false); // Set intial state
$elem.on('switch-change', function (e, data) {
tocall(bindingContext);
// valueAccessor()(data.value);
});
},
update: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
}
};
})(jQuery);
To summarize, all I want to do is have the $data.basketStatusValue have "active" or "disabled" when the checkbox is on or off.
A dirty trick you can use to pull this off is empty the whole observable and push it with the new data. But honestly its not the right way to use it. I am assuming right now thats its a array, but you can remove observables too. Just put the observable name instead of YourArray().
self.refresh = function(){
var data = YourArray().slice(0);
YourArray.removeAll();
self.YourArray(data);
};
And place this function right after the done function
.done(function(newAvailableDate) {
bindingContext.$data.Available = newAvailableDate;
// here self.refresh();
})
Your binding handler is wrong, let's start with that.
It should:
Set up Bootstrap switch on the element in init()
React to change in update()
Bind to an observabe (don't use a callback function). In our case the observable should contain the checkbox state, i.e. true or false.
Properly dispose of the Bootstrap widget when the time comes.
So, this would work better:
(function ($) {
ko.bindingHandlers.bootstrapSwitch = {
init: function (element, valueAccessor) {
var options = valueAccessor();
// set up bootstrap in init()
$(element).bootstrapSwitch().on('switch-change', function (e, data) {
options.value(data.value);
});
// see http://knockoutjs.com/documentation/custom-bindings-disposal.html
ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
$(element).bootstrapSwitch("destroy");
});
},
update: function(element, valueAccessor) {
var options = valueAccessor();
// react to change in update()
$(element).bootstrapSwitch('setState', options.value());
}
};
})(jQuery);
Next we need to set up the viewmodel accordingly.
Available needs to be observable. View changes depend on it.
we need an observable that returns true or false, depending on Available
we need an observable that returns "Active" or "Disabled", depending on that
we need a function that updates the server on change (through a subscription)
like this:
function MoneyInvestedViewModel(money) {
var self = this;
self.ID = money.ID;
self.ORIG_ID = money.ORIG_ID;
self.Available = ko.observable(money.Available);
self.errorMessage = ko.observable();
self.BasketStatus = ko.computed(function () {
return self.Available() == '9999-12-01';
});
self.BasketStatusText = ko.computed(function () {
return self.basketStatus() ? "Active" : "Disabled";
});
// BEWARE: this is not actually correct (cicular dependency)
self.BasketStatus.subscribe(function () {
$.ajax({
type: 'POST',
url: BASEURL + 'index.php/moneyexchange/changeBasketStatus/' + auth + "/" + self.ID + "/" + 1,
contentType: 'application/json; charset=utf-8'
})
.done(function (newAvailableDate) {
self.Available(newAvailableDate);
})
.fail(function (jqXHR, textStatus, errorThrown) {
self.errorMessage(errorThrown);
});
};
}
Note: Subscribe to the correct observable to update the server with the proper value. It was not clear from your question what value the server update depends on.
And now it's straightforward to bind a view to that:
<div class="col-sm-4">
<p><span data-bind="text: BasketStatusText"></span></p>
</div>
<div class="col-sm-4">
<div class="on_off">
<input type="checkbox" data-bind="bootstrapSwitch: {
value: BasketStatus
}" />
</div>
</div>
if(self.Available == '9999-12-01'){ return "Active"; }else{ return "Disabled";}
The trouble with this line is that it does not look up the value of any observable or computed, and thus it does not cause the computed to ever be updated.
You need to make self.Available an observable and then do self.Available()
This is how computeds work, they are recomputed when any of their observable/computed dependencies change. Knockout will not see a simple property update like you are doing.

CKEditor and Ajax - not updating on second instance

Im having trouble getting my CKEditor to work properly.
I am inserting my instance using Jquery. And set up my own Ajax save function for the editor.
My instances are always inserted just fine. The editor appears as it should, - and seem to work.
HOWEVER: it turns out that the 2. time I insert the instance, - the textarea is not being updated - thus no updated data sent to the Ajaxcall. It sends the old data.
After sending to the ajax call, I destroy my instance:
CKEDITOR.instances[currentInstance].destroy();
And the editor seem to be destroyed properly, every time.
OnClick I then re-insert the editor (Same instancename. I also remove the textarea when I destroy the instance, and then reinsert the textarea, when I reinsert the instance. Same textareaname).
Can anyone tell me why I can insert the instance again and again, - but the editor only updates the textarea the first time?
Ive tried:
for ( var instance in CKEDITOR.instances ) {
CKEDITOR.instances[instance].updateElement(); }
inserted in the save function - just before the ajaxcall. But still no update.
This is the build of the instance. Via Jquery I insert this as html() :
<script type="text/javascript">
CKEDITOR.replace('tekstindhold233',
{ height:'250px',width:'575px' });
</script>
And heres the savefunction:
CKEDITOR.plugins.add('popwebsave',
{
init: function(editor)
{
var pluginName = 'popwebsave';
editor.addCommand( pluginName,
{
exec : function( editor )
{
for ( var i in CKEDITOR.instances ){
var currentInstance = i;
break;
}
for ( var instance in CKEDITOR.instances ) {
CKEDITOR.instances[instance].updateElement(); }
var sprog = $('#lan_holder').text();
var vis = $('#vis_holder').text();
var pageId = $('#pageId_holder').text();
var tekstIndhold = CKEDITOR.instances[currentInstance].getData();
var tekstIndholdBox = currentInstance.replace('tekstindhold','');
var contentOrden = $('#content_orden' + tekstIndholdBox).text();
var dataString = 'lan=' + sprog + '&tekstindhold=' + tekstIndhold + '&eid=' + tekstIndholdBox + '&vis=' + vis + '&id=' + pageId + '&contentOrden=' + contentOrden;
$("#tekstindhold_box" + tekstIndholdBox).animate({
opacity: 0
} , {
duration: 500,
complete: function() {
$("#textarea_box" + tekstIndholdBox).text('');
$.ajax({
type: "POST",
url: "includes/JQ_opdater_tekst.php",
data: dataString,
dataType: "json",
cache: false,
success: function(databack){
$("#textarea_box" + tekstIndholdBox).animate({
height: 100
}, 500, function() {
$("#tekstindhold_box" + tekstIndholdBox).html(databack.tekstindhold_db);
$("#tekstindhold_box" + tekstIndholdBox).animate({
opacity: 1
}, 500, function() {
CKEDITOR.instances[currentInstance].destroy();
});
});
}
});
}
});
},
canUndo : true
});
/*
editor.addCommand(pluginName,
new CKEDITOR.dialogCommand(pluginName)
);
*/
editor.ui.addButton('Popwebsave',
{
label: 'Gem',
command: pluginName,
className : 'cke_button_save'
});
}
});
Nevermind!
I figured it out. Turns out that the instance needs to be present in the DOM, on destroy() I switched the order of events around so that destroy() happens first, and THEN removed the textarea. Works just fine now.

Kill an ajax process

GET_DATA()
GET_DATA() contains this:
var xhr;
...
function get_data( phrase ) {
xhr = function get_data( phrase ) {
$.ajax({
type: 'POST',
url: 'http://intranet/webservice.asmx/GetData',
data: '{phrase: "' + phrase + '"}',
contentType: 'application/json; charset=utf-8',
dataType: 'json',
success: function( results ) {
$("#div1").empty();
if( results.d[0] ) {
$.each( results.d, function( index, data ) {
$("#div1").append( data.Group + ':' + data.Count + '<br />' );
});
} else {
alert( "results.d does not exist..." );
}
},
error: function(xhr, status, error) {
$('#spanLoading').empty();
var err = eval("(" + xhr.responseText + ")");
alert(err.Message) ;
}
});
}
function get_default() {
$('#div1').empty().append("default stuff goes here");
}
UPDATE 2 CODE
I've also tried this, which doesn't work either, no error messages, just returns the results of when the textbox had 2 characters when it finishes processing even if I delete everything before the process has finished:
$('#TextBox1').keyup( function() {
if(xhr && xhr.readystate != 4){
xhr.abort();
}
if ($("#TextBox1").val().length >= 2) {
get_data( $("#TextBox1").val() );
} else {
get_default();
}
});
UPDATE 1 CODE:
$('#TextBox1').keyup( function() {
if ($("#TextBox1").val().length >= 2) {
get_data( $("#TextBox1").val() );
} else {
if(xhr)
{
xhr.abort();
}
get_default();
}
});
ORIGINAL QUESTION:
I have the following code:
$('#TextBox1').keyup( function() {
if ($("#TextBox1").val().length >= 2) {
get_data( $("#TextBox1").val() );
} else {
get_default();
}
});
This has a slight glitch where if I type something really fast and then I delete it equaly fast, I see the data from get_default() flash on the screen, then it gets replaced by a previous ajax request where the value in the textbox was 2 which had not finished processing.
So basically, what I think is happening is that when the textbox has 2 characters in it, the ajax request starts which takes a second or 2. While this is happening, if I delete the 2 characters, I see the get_default() being successful, but it seems to replace it with the ajax data when the ajax data finishes.
How do I stop this from happening?
Thank you for posting get_data.
The reason why your AJAX call is not getting aborted is that xhr is not defined in the appropriate (window) scope; therefor, xhr.abort() doesn't do anything (and quite probably throws an error if you take a look at your console).
Please try the following:
var xhr = false;
function get_data( phrase ) {
xhr = $.ajax({ /* ... etc */
}
The rest should work as is.
Place a time delay before you execute your ajax request.
function pausecomp(ms) {
ms += new Date().getTime();
while (new Date() < ms){}
}

JSON, jQueryUI Autocomplete, AJAX - Cannot get data when array is not local

I have searched stackoverflow, as well as the web for some insight into how to get the jQueryUI Autocomplete plugin working with my JSON data, and I'm at a loss. I had it working like a charm with a local data array. I was able to pull values and build html.
I ran into a problem when I had to pull JSON form this source:
/Search/AjaxFindPeopleProperties2
?txtSearch=ca pulls up the test data that I am trying to loop through, when I type in 'ca' to populate the autocomplete list. One of the problems is that ?term=ca is appended to the url instead of ?txtSearch=ca and I'm not sure how to change it.
This is an example of the data:
{
"MatchedProperties": [
{
"Id": 201,
"Name": "Carlyle Center",
"Description": "Comfort, convenience and style are just a few of the features you'll ...",
"ImageUrl": "/Photos/n/225/4989/PU__ThumbnailRS.jpg"
}
]
}
...and here is the ajax call I'm trying to implement:
$(document).ready(function () {
val = $("#txtSearch").val();
$.ajax({
type: "POST",
url: "/Search/AjaxFindPeopleProperties2",
dataType: "json",
data: "{}",
contentType: "application/json; charset=utf-8",
success: function (data) {
$('#txtSearch').autocomplete({
minLength: 0,
source: data.d, //not sure what this is or if it's correct
focus: function (event, ui) {
$('#txtSearch').val(ui.item.MatchedProperties.Name);
$.widget("custom.catcomplete", $.ui.autocomplete, {
//customize menu item html here
_renderItem: function (ul, item) {
return $("<li class='suggested-search-item" + " " + currentCategory + "'></li>")
.data("item.autocomplete", item)
.append($("<a><img src='" + item.MatchedProperties.ImageUrl + "' /><span class='name'>" + item.MatchedProperties.Name + "</span><span class='location'>" + "item.location" + "</span><span class='description'>" + item.MatchedProperties.Description + "</span></a>"))
.appendTo(ul);
},
_renderMenu: function (ul, items) {
var self = this,
currentCategory = "Properties Static Test Category";
$.each(items, function (index, item) {
if (item.category != currentCategory) {
ul.append("<li class='suggested-search-category ui-autocomplete-category'>" + currentCategory + "</li>");
//currentCategory = item.category;
}
self._renderItem(ul, item);
});
}
}//END: widget
//return false;
},
select: function (event, ui) {
$('#txtSearch').val(ui.item.MatchedProperties.Name);
//$('#selectedValue').text("Selected value:" + ui.item.Abbreviation);
return false;
}
});
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
alert(textStatus);
}
}); //END ajax
}); //END: doc ready
and I'm initializing here:
<script type="text/javascript">
//initialize autocomplete
$("#txtSearch").autocomplete({ //autocomplete with category support
/*basic settings*/
delay: 0,
source: "/Search/AjaxFindPeopleProperties2",
autoFocus: true,
minLength: 2 //can adjust this to determine how many characters need to be entered before autocomplete will kick in
});
//set auto fucus
$("#txtSearch").autocomplete("option", "autoFocus", true);
</script>
any help would be great...
Thanks!

Resources