i have a problem with a InfiniteScrolls calls, this is a part of code in 'Friends' for example:
var InfiniteScrollView = Backbone.View.extend({
el : window,
container : '#profile-friends',
triggerHeight : 10, //px
events : {
'scroll' : 'throttledDetectBottomPage'
},
initialize : function() {
this.throttledDetectBottomPage = _.throttle(this.detectBottomPage, 1000);
},
detectBottomPage : function() {
var self = this;
var offset = $(this.container).height() - this.$el.height() - this.triggerHeight;
if (this.$el.scrollTop() >= offset) {
self.nextPage();
}
},
stop : function() {
this.$el.unbind('scroll');
},
nextPage : function() {
if (this.collection.activeScroll == true) {
this.collection.nextPage();
if (!this.collection.isPaginated) {
if (this.collection.length == 0) {
this.renderNotFoundPage();
this.stop();
return false;
}
} else {
if (this.collection.length == 0) {
this.renderNotFoundMoreResults();
this.stop();
return false;
}
}
}
},
renderNotFoundMoreResults : function() {
$('#profile-friends').append('No more results');
},
renderNotFoundPage : function() {
var container = $(this.container);
container.html('0 results');
}
});
In this.collection.nextPage() is called 'api/friends/pag', pag = page number.
Here the code of the collection:
// profile friends collection
define(
['underscore',
'backbone',
'models/user'],
function(_, Backbone, User){
var PFriendsCollection = Backbone.Collection.extend({
// Reference to this collection's model.
model: User,
initialize: function(){
this.isPaginated = false;
this.active = false;
},
//Call in render
search: function() {
this.page = 1;
this.isPaginated = false;
this.active = true;
this.fetch();
},
//Call in Infinite Scroll view NextPage
nextPage: function() {
if(this.active) {
this.isPaginated = true;
this.page = parseInt(this.page) + 1;
this.fetch({update: true});
}
},
// Url, points to the server API
url: function() {
return 'api/pfriends/' + this.page;
},
// Url, points to the server API
// ATM it is just a json test file
parse: function(response){
// extract items from response.
return response.items;
}
});
return new PFriendsCollection;
});
I created this view in the render() function of FriendsView, and down I surje a problem: i go bottom and trigger launch, but he launch a lot of times if i move the scroll, he call api/pfriends/2, api/pfriends/3, api/friends/4 (For example, is random the number of calls) in the same moment, because he don't wail the first response and launch trigger :(
I do not know where to put a trigger, result or something that blocks the execution of that scroll trigger whenever there pending fetch response.
Thanks =)
fetch returns a jQuery deferred, so you could try this in your collection's nextPage:
return this.fetch({update: true});
Then in your view:
nextPage : function() {
if (this.collection.activeScroll == true && !this.updating) {
var self = this;
this.updating = true;
// function passed to 'always' is called whether the fetch succeeded or failed
this.collection.nextPage().always(function(){
self.updating = false;
if (!self.collection.isPaginated) {
if (self.collection.length == 0) {
self.renderNotFoundPage();
self.stop();
return false;
}
} else {
if (self.collection.length == 0) {
self.renderNotFoundMoreResults();
self.stop();
return false;
}
}
}
}
},
You might want to actually use done and fail instead of always. Check the documentation for more info.
Related
I have this grid that i am creating using knockoutjs, it works perfectly at first, now i am using a window.location.hash to run another query, it works too and query returns the correct amount of data however when i insert it within the observableArray (which gets inserted correctly as well), the grid doesn't update the data and shows the old data... I'm using removeAll() function on the observableArray as well before inserting new set of data but it wont update my grid, i suspect there is something wrong with the DOM?
I should mention when i reload the page (since the page's url keeps the hash for query) my grid shows the data and works perfectly. for some reason it needs to reload the page and doesn't work without,
Here is my JS:
if (!ilia) var ilia = {};
ilia.models = function () {
var self = this;
this.pageCount = ko.observable(0);
//this is the observableArray that i am talking about ++++++++
this.items = ko.observableArray();
var $pagination = null;
var paginationConfig = {
startPage: 1,
totalPages: 20,
onPageClick: function (evt, page) {
self.generateHash({ pageNum: page });
self.getData();
}
}
var hashDefault = {
pageNum: 1,
pageSize: 20,
catId: null,
search: ""
}
this.dataModel = function (_id, _name, _desc, _thumb, _ext) {
var that = this;
this.Id = ko.observable(_id);
this.Name = ko.observable(_name);
this.Desc = ko.observable(_desc);
this.Url = '/site/ModelDetail?id=' + _id;
var b64 = "data:image/" + _ext + ";base64, ";
this.thumb = ko.observable(b64 + _thumb);
}
this.generateHash = function (opt) {
//debugger;
var props = $.extend(hashDefault, opt);
var jso = JSON.stringify(props);
var hash = window.location.hash;
var newHash = window.location.href.replace(hash, "") + "#" + jso;
window.location.href = newHash;
return jso;
}
this.parseHash = function () {
var hash = window.location.hash.replace("#", "");
var data = JSON.parse(hash);
if (data)
data = $.extend(hashDefault, data);
else
data = hashDefault;
return data;
}
var _cntrl = function () {
var _hdnCatName = null;
this.hdnCatName = function () {
if (_hdnCatName == null)
_hdnCatName = $("hdnCatName");
return _hdnCatName();
};
var _grid = null;
this.grid = function () {
if (_grid == null || !_grid)
_grid = $("#grid");
return _grid;
}
this.rowTemplate = function () {
return $($("#rowTemplate").html());
}
}
this.createPagnation = function (pageCount, pageNum) {
$pagination = $('#pagination-models');
if ($pagination && $pagination.length > 0)
if (paginationConfig.totalPages == pageCount) return;
var currentPage = $pagination.twbsPagination('getCurrentPage');
var opts = $.extend(paginationConfig, {
startPage: pageNum > pageCount ? pageCount : pageNum,
totalPages: pageCount,
onPageClick: self.pageChange
});
$pagination.twbsPagination('destroy');
$pagination.twbsPagination(opts);
}
this.pageChange = function (evt, page) {
var hash = self.parseHash();
if (hash.pageNum != page) {
self.generateHash({ pageNum: page });
self.getData();
}
}
this.getData = function () {
var _hash = self.parseHash();
inputObj = {
pageNum: _hash.pageNum,
pageSize: _hash.pageSize,
categoryId: _hash.catId
}
//debugger;
//console.log(_hash);
if (inputObj.categoryId == null) {
ilia.business.models.getAll(inputObj, function (d) {
//debugger;
if (d && d.IsSuccessfull) {
self.pageCount(d.PageCount);
self.items.removeAll();
_.each(d.Result, function (item) {
self.items.push(new self.dataModel(item.ID, item.Name, item.Description, item.Thumb, item.Extention));
});
if (self.pageCount() > 0)
self.createPagnation(self.pageCount(), inputObj.pageNum);
}
});
}
else {
ilia.business.models.getAllByCatId(inputObj, function (d) {
if (d && d.IsSuccessfull) {
self.pageCount(d.PageCount);
self.items.removeAll();
console.log(self.items());
_.each(d.Result, function (item) {
self.items.push(new self.dataModel(item.ID, item.Name, item.Description, item.Thumb, item.Extention));
});
// initializing the paginator
if (self.pageCount() > 0)
self.createPagnation(self.pageCount(), inputObj.pageNum);
//console.log(d.Result);
}
});
}
}
this.cntrl = new _cntrl();
};
And Initialize:
ilia.models.inst = new ilia.models();
$(document).ready(function () {
if (!window.location.hash) {
ilia.models.inst.generateHash();
$(window).on('hashchange', function () {
ilia.models.inst.getData();
});
}
else {
var obj = ilia.models.inst.parseHash();
ilia.models.inst.generateHash(obj);
$(window).on('hashchange', function () {
ilia.models.inst.getData();
});
}
ko.applyBindings(ilia.models.inst, document.getElementById("grid_area"));
//ilia.models.inst.getData();
});
Would perhaps be useful to see the HTML binding here as well.
Are there any console errors? Are you sure the new data received isn't the old data, due to some server-side caching etc?
Anyhow, if not any of those:
Are you using deferred updates? If the array size doesn't change, I've seen KO not being able to track the properties of a nested viewmodel, meaning that if the array size haven't changed then it may very well be that it ignores notifying subscribers. You could solve that with
self.items.removeAll();
ko.tasks.runEarly();
//here's the loop
If the solution above doesn't work, could perhaps observable.valueHasMutated() be of use? https://forums.asp.net/t/2056128.aspx?What+is+the+use+of+valueHasMutated+in+Knockout+js
I am trying to get a better understanding on javacsript. And I am not sure why this code is not working. I am trying to create functions that will call another function. And return the results of the called function.
When I call the below, I get fully logged in and presented with the screen I desire. But jsDidLogin Always returns undefined. Is there a better way to implement my methods?
var jsDidLogin = beginLogin()
console.log(jsDidLogin)
function waitUntilElementFound(element, time, callFunction) //Wait for the element to be found on the page
{
if (document.querySelector(element) != null) {
return callFunction();
}
else {
if (!checkForFailedLogin()) {
setTimeout(function () {
waitUntilElementFound(element, time, callFunction);
}, time);
}
else {
return false;
}
}
}
function checkForFailedLogin() {
if (document.querySelector("div[class='modal-body ng-scope'] h1") != null) {
if(document.querySelector("div[class='modal-body ng-scope'] h1").innerHTML == "Login Error")
{
return true;
}
}
else {
return false;
}
}
function initialTabSelect() //Load the bank page once login is completed
{
document.querySelectorAll("li[class='Tab'] a")[0].click();
return "Fully Logged In";
}
function initialDoNotAsk() {
document.querySelectorAll("a[ng-click='modalCancel()']")[0].click();
return waitUntilElementFound("li[class='Tab'] a", 1000, initialTabSelect);
}
function initialLogin() {
var accountName = document.getElementById("username");
var accountPassword = document.getElementById("password");
var evt = document.createEvent("Events");
evt.initEvent("change", true, true);
accountName.value = "USERNAME";
accountPassword.value = "PASSWORD";
accountName.dispatchEvent(evt);
accountPassword.dispatchEvent(evt);
document.querySelectorAll("form[name='loginForm'] button.icon-login")[0].click();
return waitUntilElementFound("a[ng-click='modalCancel()']", 2000, initialDoNotAsk);
}
function beginLogin() {
return waitUntilElementFound("form[name='loginForm'] button.icon-login", 1000, initialLogin);
}
Changing to this alerts me when Fully Logged in, but if I change it to return status. I still get no returns.
My head is starting to hurt :(
function waitUntilElementFound(element, time, callFunction, callBack) //Wait for the element to be found on the page
{
if (document.querySelector(element) != null) {
callBack(callFunction());
}
else {
if (!checkForFailedLogin()) {
setTimeout(function () {
callBack(waitUntilElementFound(element, time, callFunction, function(status){alert(status);}));
}, time);
}
else {
return false;
}
}
}
function checkForFailedLogin() {
if (document.querySelector("div[class='modal-body ng-scope'] h1") != null) {
if(document.querySelector("div[class='modal-body ng-scope'] h1").innerHTML == "Login Error")
{
return true;
}
}
else {
return false;
}
}
function initialTabSelect() //Load the bank page once login is completed
{
document.querySelectorAll("li[class='Tab'] a")[0].click();
return "Fully Logged In";
}
function initialDoNotAsk() {
document.querySelectorAll("a[ng-click='modalCancel()']")[0].click();
return waitUntilElementFound("li[class='Tab'] a", 1000, initialTabSelect, function(status){alert(status)};);
}
function initialLogin() {
var accountName = document.getElementById("username");
var accountPassword = document.getElementById("password");
var evt = document.createEvent("Events");
evt.initEvent("change", true, true);
accountName.value = "USERNAME";
accountPassword.value = "PASSWORD";
accountName.dispatchEvent(evt);
accountPassword.dispatchEvent(evt);
document.querySelectorAll("form[name='loginForm'] button.icon-login")[0].click();
return waitUntilElementFound("a[ng-click='modalCancel()']", 2000, initialDoNotAsk, function(status){alert(status)};);
}
function beginLogin() {
return waitUntilElementFound("form[name='loginForm'] button.icon-login", 1000, initialLogin, function(status){alert(status)};);
}
I am making an app on visual studio 2012. I am navigating from home page to levelOne. On a button click on level one i'm doing some animation,during animation if i get back to home page using windows back button, and then again come back to level one i get the animation running ,i want this animation to get stopped.
This is my first page where the animation will occur:
(function () {
"use strict";
WinJS.UI.Pages.define("/pages/levelOne/levelOne.html", {
// This function is called whenever a user navigates to this page. It
// populates the page elements with the app's data.
ready: function initiazlize(element, options) {
document.getElementById("play").addEventListener("click",function initial(){
roundCount++;
if (flag1 == false) {
//if flag1 is false that means its time for next storyboard
j = 0;
var r3 = Math.random();
if (r3 <= 0.333) {
$(Left).animate({ marginTop: '-=100px' }, 500);
$(Left).animate({ marginTop: '+=100px' }, 500, animateAsync);
//document.getElementById("play").disabled = false;
}
else if (r3 <= 0.666) {
$(midle).animate({ marginTop: '-=100px' }, 500);
$(midle).animate({ marginTop: '+=100px' }, 500, animateAsync);
//document.getElementById("play").disabled = false;
}
else {
$("#bowlThree").animate({ marginTop: '-=100px' }, 500);
$("#bowlThree").animate({ marginTop: '+=100px' }, 500, animateAsync);
//document.getElementById("play").disabled = false;
}
//inside animate Async(), some more animation on capOne,capTwo,capThree and on object, I want to stop animation on these
//There is a counter j, when j reachs 100 animation is stopped
}
});
document.onbeforeunload = function () {
j = 100;
flag1 = true;
$("#capOne").stop(true, false);
$("#capTwo").stop(true, false);
$("#capThree").stop(true, false);
clearTimeout(variableTimer);
window.cancelAnimationFrame(variableTimer);
Debug;
};
},
unload: function () {
// TODO: Respond to navigations away from this page.
$("#capOne").stop();
$("#capOne").css({ "margin-top": "360px", "margin-left": "250px" })
$("#capTwo").css({ "margin-top": "360px", "margin-left": "580px" })
$("#capThree").css({ "margin-top": "360px", "margin-left": "910px" })
////return false;
$("#capOne").fadeOut(100);
WinJS.UI.disableAnimations();
flag1 = true;//if flag1=false then animation is stopped
$("#capOne").stop(true, false);
$("#capTwo").stop(true, false);
$("#capThree").stop(true, false);
clearTimeout(document.variableTimer);
window.cancelAnimationFrame(document.variableTimer);
debugger;
},
updateLayout: function (element, viewState, lastViewState) {
/// <param name="element" domElement="true" />
// TODO: Respond to changes in viewState.
}
});
})();
Here is the navigator.js file
(function () {
"use strict";
var appView = Windows.UI.ViewManagement.ApplicationView;
var nav = WinJS.Navigation;
WinJS.Namespace.define("Application", {
PageControlNavigator: WinJS.Class.define(
// Define the constructor function for the PageControlNavigator.
function PageControlNavigator(element, options) {
this._element = element || document.createElement("div");
this._element.appendChild(this._createPageElement());
this.home = options.home;
this._lastViewstate = appView.value;
nav.onnavigated = this._navigated.bind(this);
window.onresize = this._resized.bind(this);
document.body.onkeyup = this._keyupHandler.bind(this);
document.body.onkeypress = this._keypressHandler.bind(this);
document.body.onmspointerup = this._mspointerupHandler.bind(this);
Application.navigator = this;
}, {
home: "",
/// <field domElement="true" />
_element: null,
_lastNavigationPromise: WinJS.Promise.as(),
_lastViewstate: 0,
// This is the currently loaded Page object.
pageControl: {
get: function () { return this.pageElement && this.pageElement.winControl; }
},
// This is the root element of the current page.
pageElement: {
get: function () { return this._element.firstElementChild; }
},
// Creates a container for a new page to be loaded into.
_createPageElement: function () {
var element = document.createElement("div");
element.style.width = "100%";
element.style.height = "100%";
return element;
},
// Retrieves a list of animation elements for the current page.
// If the page does not define a list, animate the entire page.
_getAnimationElements: function () { //IHDAr
if (this.pageControl && this.pageControl.getAnimationElements) {
return this.pageControl.getAnimationElements();
}
return this.pageElement;
},
// Navigates back whenever the backspace key is pressed and
// not captured by an input field.
_keypressHandler: function (args) {
if (args.key === "Backspace") {
nav.back();
}
},
// Navigates back or forward when alt + left or alt + right
// key combinations are pressed.
_keyupHandler: function (args) {
if ((args.key === "Left" && args.altKey) || (args.key === "BrowserBack")) {
nav.back();
} else if ((args.key === "Right" && args.altKey) || (args.key === "BrowserForward")) {
nav.forward();
}
},
// This function responds to clicks to enable navigation using
// back and forward mouse buttons.
_mspointerupHandler: function (args) {
if (args.button === 3) {
nav.back();
} else if (args.button === 4) {
nav.forward();
}
},
// Responds to navigation by adding new pages to the DOM.
_navigated: function (args) {
var newElement = this._createPageElement();
var parentedComplete;
var parented = new WinJS.Promise(function (c) { parentedComplete = c; });
this._lastNavigationPromise.cancel();
this._lastNavigationPromise = WinJS.Promise.timeout().then(function () {
return WinJS.UI.Pages.render(args.detail.location, newElement,args.detail.state, parented);
}).then(function parentElement(control) {
var oldElement = this.pageElement;
if (oldElement.winControl && oldElement.winControl.unload) {
oldElement.winControl.unload();
}
this._element.appendChild(newElement);
this._element.removeChild(oldElement);
oldElement.innerText = "";
this._updateBackButton();
parentedComplete();
var history = args.detail.state;
WinJS.UI.Animation.enterPage(this._getAnimationElements()).done(
function () {
}
);//IDHAR
}.bind(this));
args.detail.setPromise(this._lastNavigationPromise);//IDHAR BHI
},
// Responds to resize events and call the updateLayout function
// on the currently loaded page.
_resized: function (args) {
if (this.pageControl && this.pageControl.updateLayout) {
this.pageControl.updateLayout.call(this.pageControl, this.pageElement, appView.value, this._lastViewstate);
}
this._lastViewstate = appView.value;
},
// Updates the back button state. Called after navigation has
// completed.
_updateBackButton: function () {
var backButton = this.pageElement.querySelector("header[role=banner] .win-backbutton");
if (backButton) {
backButton.onclick = function () { nav.back(); };
if (nav.canGoBack) {
backButton.removeAttribute("disabled");
} else {
backButton.setAttribute("disabled", "disabled");
}
}
},
}
)
});
})();
So, I've searched for this high and low and maybe I'm just having trouble understanding jQuery's deferred function or I'm completely on the wrong track. So any help would be appreciated folks!
I basically have a custom jQuery function messager that displays a message with a fadeOut and fadeIn.
(function ( $ ) {
$.fn.messager = function(message, effect, speed) {
$(this).fadeOut(speed).delay(speed).text(message).fadeIn(speed);
return this;
};
}( jQuery ));
So, I have a div called $elem and when $elem.messager gets called multiple times (with different messages), I would like the messager function to wait till its last call has finished. As in the last FadeIn has finished. Because currently what's happening is that the second call of the function is overwriting the animation effect of the first call of the function.
Any ideas?
jQuery Deferred Way
jQuery Deferred object (roughly compromising CommonJS Promises API) can help us managing queued operations. Here is my implementation of queuing messages. You can pass through multiple messages as an array in one call, or synchronize different message boards easily because #messager() returns jQuery object itself but also wrapped as a promise object which will be resolved just when message(s) being displayed.
(function ($) {
function awaits($el) {
var awaits = $el.data('_awaits');
awaits || $el.data('_awaits', awaits = []);
return awaits;
}
function resolveNext(curr /*, ignored */) {
var head = awaits(this).shift();
if (head === curr) {
resolveNext.call(this, 'not await');
} else {
head && head.resolve();
}
}
function display(message, speed) {
var $self = this, await = $.Deferred(), willDone = $.Deferred();
awaits($self).push(await) > 1 || await.resolve();
await.done(function() {
function reveal() {
$self.text(message).fadeIn(speed, function() {
resolveNext.call($self, await);
willDone.resolve();
});
}
$self.fadeOut(speed/2, reveal);
});
return willDone.promise(this);
};
$.fn.messager = function(message, speed) {
speed = speed || 500;
if ($.isArray(message)) {
var arr = [];
message.forEach(function(m) {
arr.push(display.call(this, m, speed));
}, this);
return $.when.apply(this, arr);
} else {
return display.call(this, message, speed);
}
}
}( jQuery ));
function play() {
$('#msgbox1').messager(['A demo of', 'queued messages'], 1000);
for (var i = 3; i > 0; i--) $('#msgbox1').messager(i);
$('#msgbox1').messager(['Ready to sing...', 'Singing...']);
for (var i = 8; i > 0; i--) $('#msgbox2').messager('***');
for (i = 1; i < 8; i++) $('#msgbox2').messager(String.fromCharCode(64 + i));
$('#msgbox2')
.messager('')
.done(function() {
$('#msgbox1')
.messager(['End of demo.', 'Thank you.', 'Run again?'], 1000)
.done(function() {
$('#msgbox1, #msgbox2').one('click', play);
$('#msgbox2').messager('>');
});
});
}
play();
html {
background: rgba(0, 0, 0, 0.25);
}
#msgbox1, #msgbox2 {
color: #FFF;
padding: 0.3em 0.5em;
font-size: 36pt;
text-align: center;
height: 1.8em;
cursor: default;
}
#msgbox2 {
color: yellow;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Queuing Messages with jQuery Deferred Object</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
</head>
<body>
<div id="msgbox1"></div>
<div id="msgbox2"></div>
</body>
</html>
Edit, Updated
Try
(function ($) {
$.fn.messager = messager;
function messager(message, speed, callback) {
var that = $(this);
if (that.data("queue") === undefined) {
$.fx.interval = 0;
that.data("queue", []);
that.data("msg", []);
};
var q = that.data("queue"),
msgs = that.data("msg");
q.push([message, speed, callback]);
msgs.push(message);
var fn = function (m, s, cb) {
return that.fadeOut(s, function () {
that.text(m)
})
.delay(s)
.fadeIn(s, cb)
.promise("fx")
.done(function (el) {
console.log("callback", q.length);
if (q.length > 1) {
q.splice(0, 1);
fn.apply(el, q[0])
} else {
el.data("queue", []);
console.log("done", el.data("queue").length);
always(promise, ["complete", msgs])
.then(complete);
}
return el.promise("fx");
})
}
, promise = $.when(!that.queue("fx").length
? fn.apply(that, q[0])
: that.promise("fx"))
, always = function (elem, args) {
if (elem.state() === "pending") {
console.log(elem.state(), args)
} else {
if (elem.state() === "resolved") {
elem.done(function (elem) {
console.log(msgs.length + " messages complete");
})
};
};
return elem.promise("fx")
};
always(promise, ["start", message, q.length]);
return that
};
}(jQuery));
See .promise()
(function ($) {
$.fn.messager = messager;
function messager(message, speed, callback) {
var that = $(this);
if (that.data("queue") === undefined) {
$.fx.interval = 0;
that.data("queue", []);
that.data("msg", []);
};
var q = that.data("queue"),
msgs = that.data("msg");
q.push([message, speed, callback]);
msgs.push(message);
var fn = function (m, s, cb) {
return that.fadeOut(s, function () {
that.text(m)
})
.delay(s)
.fadeIn(s, cb)
.promise("fx")
.done(function (el) {
console.log("callback", q.length);
if (q.length > 1) {
q.splice(0, 1);
fn.apply(el, q[0])
} else {
el.data("queue", []);
console.log("done", el.data("queue").length);
always(promise, ["complete", msgs])
.then(complete);
}
return el.promise("fx");
})
}
, promise = $.when(!that.queue("fx").length
? fn.apply(that, q[0])
: that.promise("fx"))
, always = function (elem, args) {
if (elem.state() === "pending") {
console.log(elem.state(), args)
} else {
if (elem.state() === "resolved") {
elem.done(function (elem) {
console.log(msgs.length + " messages complete");
})
};
};
return elem.promise("fx")
};
always(promise, ["start", message, q.length]);
return that
};
}(jQuery));
var complete = function() {
if (!$("pre").is("*")) {
$("body").append("<pre>" + JSON.stringify($(this).data("msg"), null, 4))
} else {
$("pre")
.text(JSON.stringify($(this).data("msg"), null, 4));
$("label[for=messages]").text("messages updated")
.show(0).delay(350).hide(0)
};
};
var fx = function() {
$(this).css("color", "purple").animate({
fontSize: "72"
}, 100, function() {
$(this).animate({
fontSize: "36"
}, 100, function() {
$(this).css("color", "inherit")
})
})
};
var input = $("input");
var $elem = $("#messages");
$elem.messager("0", 1000)
.messager("1", 100)
.messager("2", 200)
.messager("3", 300)
.messager("4", 400)
.messager("5", 500)
.messager("6", 600)
.messager("7", 700)
.messager("8", 800)
.messager("9", 900);
$.each("abcdefghijklmnopqrstuvwxyz".split(""), function(key, val) {
$elem.messager(val, 200, fx);
});
$("button").on("click", function() {
$elem.messager(input.val().length > 0 ? input.val() : $.now(), 200);
input.val("")
});
#messages {
display:block;
height:38px;
font-size:36px;
position : absolute;
}
label[for=messages] {
color:blue;
}
pre {
position:relative;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<label for="button">add messages</label> <label for="messages"></label><br>
<input type="text" /><button>click</button>
<br />
<div id="messages">messages</div>
<br><br>
<script>
(function ( $ ) {
$.fn.messager = function(message, effect, speed, gothru) {
if (!$(this).data('message'))
{
$(this).data('message', Array());
}
$(this).data('message').push({messageContent: message, messageEffect: effect, messageSpeed: speed});
if ($(this).data('message').length > 1 && gothru != true)
{
return;
}
$(this).fadeOut(speed).delay(speed).text(message).fadeIn(speed, function(){
$(this).data("message").shift();
if ($(this).data('message').length > 0)
{
var arrMessage = $(this).data('message');
var messageContent = arrMessage[0].messageContent;
var messageEffect= arrMessage[0].messageEffect;
var messageSpeed= arrMessage[0].messageSpeed;
$(this).data("message").shift();
$(this).messager(messageContent , messageEffect, messageSpeed, true);
}
});
return this;
};
}( jQuery ));
</script>
It's good now.
The naive way of doing it recursively:
Make a global variable(boolean), in this case called queue. If queue is false, set it to true and begin executing the code you want to run. When that code finishes, set queue back to false. Otherwise, if queue was true, just recursively call _this.messager() until queue is set back to false, which would mean that the code is finished running.
fadeIn() and fadeOut() can take callbacks as the final argument, so I'm utilizing that here.
HTML:
<div id="messageBox"></div>
javaScript:
(function ( $ ) {
var queue = false;
$.fn.messager = function(message, effect, speed) {
var _this = $(this);
if (!queue) {
queue = true;
_this.fadeOut(speed, function() {
_this.text(message);
_this.fadeIn(speed, function() {
queue = false;
});
});
} else {
_this.messager(message, effect, speed);
}
return this;
};
}( jQuery ));
$('#messageBox').messager('One', 300);
$('#messageBox').messager('Two', 300);
$('#messageBox').messager('Three', 300);
This typically results in:
Uncaught RangeError: Maximum call stack size exceeded
A more advanced example:
Here we create a second variable called counter to keep track of how many times 'messager' is called recursively and doesn't exceed the limit specified in the options. I set a default of 50, which can be overwritten by the options parameter.
In addition, we've separated out the code that you want to run. This could even be multiple functions that call each other, the important bit is making sure that when your code is finished running, you set queue to false rather than returning false and setting queue to the result of the function. Setting it to the result of the function just makes it undefined until the function finishes returning. We want it to remain as true until the code is finished executing.
This example also throttles the recursive calling so that it's only called once every 100 milliseconds, although that too can be overwritten with whatever value you like (in milliseconds) via the options parameter.
HTML:
<div id="messageBox"></div>
javaScript:
(function( $ ) {
var queue = false;
var counter = 0;
$.fn.messager = function(message, effect, speed, options) {
var _S = $.extend({
throttle: 100,
counter: 50
}, options);
var _this = $(this);
counter += 1;
function codeToRun() {
_this.fadeOut(speed, function() {
_this.text(message);
_this.fadeIn(speed, function() {
queue = false;
});
});
}
if (!queue) {
queue = true;
codeToRun();
counter = 0;
} else {
if (counter < _S.counter) {
setTimeout(function() {
_this.messager(message, effect, speed);
}, _S.throttle);
}
}
return this;
};
})( jQuery );
$('#messageBox').messager('One', 300);
$('#messageBox').messager('Two', 300);
$('#messageBox').messager('Three', 300);
For some reason, calling methods on $(this) directly gives me:
[Window, jquery: "1.11.0", constructor: function, selector: "", toArray: function, get: function…]
But storing $(this) in a variable and calling methods on that variable gives me the correct element:
[div#messageBox, selector: "#messageBox", context: document, jquery: "1.11.0", constructor: function, toArray: function…]
I hope that somebody can help me.
I want to redeclare js function by extension.
For example, there is the basic js function on website:
function foo(){
..something here..
}
i want to redeclare it by own function with the same name. how it will be easiest to do?
edit 1. i'll try to explain better.
there is a native code in website:
Notifier = {
debug: false,
init: function (options) {
curNotifier = extend({
q_events: [],
q_shown: [],
q_closed: [],
q_max: 3,
q_idle_max: 5,
done_events: {},
addQueues: curNotifier.addQueues || {},
recvClbks: curNotifier.recvClbks || {},
error_timeout: 1,
sound: new Sound('mp3/bb1'),
sound_im: new Sound('mp3/bb2')
}, options);
if (!this.initFrameTransport() && !this.initFlashTransport(options)) {
return false;
}
this.initIdleMan();
if (!(curNotifier.cont = ge('notifiers_wrap'))) {
bodyNode.insertBefore(curNotifier.cont = ce('div', {id: 'notifiers_wrap', className: 'fixed'}), ge('page_wrap'));
}
},
destroy: function () {
Notifier.hideAllEvents();
curNotifier.idle_manager.stop();
curNotifier = {};
re('notifiers_wrap');
re('queue_transport_wrap');
},
reinit: function () {
ajax.post('notifier.php?act=a_get_params', {}, {
onDone: function (options) {
if (options) {
curNotifier.error_timeout = 1;
this.init(options);
} else {
curNotifier.error_timeout = curNotifier.error_timeout || 1;
setTimeout(this.reinit.bind(this), curNotifier.error_timeout * 1000);
if (curNotifier.error_timeout < 256) {
curNotifier.error_timeout *= 2;
}
}
}.bind(this),
onFail: function () {
curNotifier.error_timeout = curNotifier.error_timeout || 1;
setTimeout(this.reinit.bind(this), curNotifier.error_timeout * 1000);
if (curNotifier.error_timeout < 256) {
curNotifier.error_timeout *= 2;
}
return true;
}.bind(this)
});
}
}
and function Sound
function Sound(filename) {
var audioObjSupport = false, audioTagSupport = false, self = this, ext;
if (!filename) throw 'Undefined filename';
try {
var audioObj = ce('audio');
audioObjSupport = !!(audioObj.canPlayType);
if (('no' != audioObj.canPlayType('audio/mpeg')) && ('' != audioObj.canPlayType('audio/mpeg')))
ext = '.mp3?1';
else if (('no' != audioObj.canPlayType('audio/ogg; codecs="vorbis"')) && ('' != audioObj.canPlayType('audio/ogg; codecs="vorbis"')))
ext = '.ogg?1';
else
audioObjSupport = false;
} catch (e) {}
// audioObjSupport = false;
if (audioObjSupport) {
audioObj.src = filename + ext;
var ended = false;
audioObj.addEventListener('ended', function(){ended = true;}, true);
audioObj.load();
this.playSound = function() {
if (ended) {
audioObj.load();
}
audioObj.play();
ended = false;
};
this.pauseSound = function() {
audioObj.pause();
};
} else {
cur.__sound_guid = cur.__sound_guid || 0;
var wrap = ge('flash_sounds_wrap') || utilsNode.appendChild(ce('span', {id: 'flash_sounds_wrap'})),
guid = 'flash_sound_' + (cur.__sound_guid++);
var opts = {
url: '/swf/audio_lite.swf?4',
id: guid
}
var params = {
swliveconnect: 'true',
allowscriptaccess: 'always',
wmode: 'opaque'
}
if (renderFlash(wrap, opts, params, {})) {
var swfObj = browser.msie ? window[guid] : document[guid],
inited = false,
checkLoadInt = setInterval(function () {
if (swfObj && swfObj.paused) {
try {
swfObj.setVolume(1);
swfObj.loadAudio(filename + ext);
swfObj.pauseAudio();
} catch (e) {debugLog(e);}
}
inited = true;
clearInterval(checkLoadInt);
}, 300);
self.playSound = function() {
if (!inited) return;
swfObj.playAudio(0);
};
self.pauseSound = function() {
if (!inited) return;
swfObj.pauseAudio();
};
}
}
}
Sound.prototype = {
play: function() {
try {this.playSound();} catch(e){}
},
pause: function() {
try {this.pauseSound();} catch(e){}
}
};
when i try to add injection with redeclaration function Sound it doesn't work.
if i create my own function, for example, xSound and сall it this way:
cur.sound = new xSound('mp3/bb1');
it's working.
You can do it like this, for example:
foo = function(args) {
// method body...
}
JavaScript is a programming language where functions are first-class citizens so you can manipulate them like other types.
UPDATE:
Make sure that this piece of code actually does the redefinition and not the first definition. (thanks to #jmort253)
function foo(){
// ..something else here..
}
Remember that an extension's Content Script code and the webpage code run in different execution contexts.
So if you want to redefine a function that exists in the webpage context, you'll have to inject your code into the webpage. Take a look at this answer by Rob W for different methods of doing that:
Insert code into the page context using a content script