view events are set twice when I instantiate view second time - requirejs

This is my project architecture issue - its my first backbone project and I probably did something wrong.
In all my project in route callbacks I have:
myroute: function() {
this.currentView = new MyCustomView();
},
mysecondroute: function() {
this.currentView = new MySecondView()
},
//...
So in all route callbacks I instantiate some view. This view has initialze method which calls render method. It works except that all view events (declared in events: {}) are 'binded' every time same view is instantiated. so when I visit same route twice events for view corresponding to this route are fired twice...
Probably I shouldn instantiate new view on every route call - but how I can do this ? I mean what are the standards? Maybe I should just unload current view somehow - is there any method to do this?

I think you have to add a method to unbind all the events at the time to close the view
like this
close : function () {
//your code to clean everything before closing the view
this.remove();
this.unbind();
}
so the next time the view is called the events will be added during the initialization of the view, thats why you had events being called twice. the initialize method binds the events to the .el element. you need to make sure you unbind those at some point.

Related

What is the order of execution of the same-type hooks in fastify?

In Fastify.js you have at least to ways to register hooks: globally (via fastify.addHook()) or as a property inside the route declaration. In the example below I'm trying to use fastfy-multer to handle file uploading but the maximum amount of files must be limited by a setting associated with a "room". As the app has many rooms, most of the requests contain a reference to a room, and every time the request is being augmented with room settings by the preHandler hook.
import fastify from 'fastify'
import multer from 'fastify-multer'
const server = fastify()
server.register(multer.contentParser)
// For all requests containing the room ID, fetch the room options from the database
fastify.addHook('preHandler', async (request, reply) => {
if (request.body.roomID) {
const roomOptions = await getRoomOptions(request.body.roomID)
if (roomOptions) {
reuqest.body.room = roomOptions
}
else {
// handle an error if the room doesn't exist
}
}
})
server.post('/post', {
// Limit the maximum amount of files to be uploaded based on room options
preHandler: upload.array(files, request.body.room.maxFiles)
})
In order for this setup to work, the global hook must always be executed before the file upload hook. How can I guarantee that?
Summary: As #Manuel Spigolon said:
How can I guarantee that? The framework does it
Now we can take Manuel's word for it (SPOILER ALERT: they are absolutely correct), or we can prove how this works by looking in the source code on GitHub.
The first thing to keep in mind is that arrays in JavaScript are remain ordered by the way objects are pushed into them, but don’t take my word for it. That is all explained here if you want to dive a little deeper into the evidence. If that was not true, everything below doesn't matter and you could just stop reading now.
How addHook works
Now that we have established that arrays maintain their order, let look at how the addHook code is executed. We can start by looking at the default export of fastify in the fastify.js file located in the root directory. In this object if scoll down you'll see the addHook property defined. When we look into the addHook function implementation we can see that in that add hook call we are calling this[kHooks].add.
When we go back to see what the kHooks property is we see that it is a new Hooks(). When we go to take a look at the add method on the Hooks object, we can see that it just validates the hook that is being add and then [pushes] it to the array property on the Hooks object with the matching hook name. This shows that hooks will always be in the order which add was called for them.
How fastify.route adds hooks
I hope you're following to this point because that only proves the order of the addHook calls in the respective array on the Hooks object. The next question is how these interact with the calls of fastify.(get | post | route | ...) functions. We can walk through the fastify.get function, but they are all pretty much the same (you can do the same exercise with any of them). Looking at the get function, we see that the implementation is just calling the router.prepareRoute function. When you look into the prepareRoute implementation, you see that this function returns a call to the route function. In the route function there is a section where the hooks are set up. It looks like this:
for (const hook of lifecycleHooks) {
const toSet = this[kHooks][hook]
.concat(opts[hook] || [])
.map(h => h.bind(this))
context[hook] = toSet.length ? toSet : null
}
What this does is go through every lifecycle hook and turn it into a set of all the hooks from the Fastify instance (this) and the hooks in the options (opts[hook]) for that given hook and binds them to the fastify instance (this). This shows that the hooks in the options for the routes are always added after the addHook handlers.
How Fastify executes hooks
This is not everything we need though. Now we know the order in which the hooks are stored. But how exactly are they executed? For that we can look at the hookRunner function in the hooks.js file. We see this function acts as a sort of recursive loop that continues running as long as the handlers do not error. It first creates a variable i to keep track of the handler function it is currently on and then tries to execute it and increments the function tracker (i).
If the handler fails (handleReject), it runs a callback function and does not call the next function to continue. If the handler succeeds (handleResolve), it just runs the next function to try the same process on the following handler (functions[i++]) in the functions set.
Why does this matter
This proves that the hook handlers are called in the order that they were pushed into the ordered collection. In other words:
How can I guarantee that? The framework does it

How to initialize Chrome extension context menus under Manifest V3 service workers? [duplicate]

I'm working on a simple link sharing extension (pinboard, readability, delicious, etc), and have a question about how to properly deal with a context menu item. In my non-persistent background page I call chrome.contextMenus.create and chrome.contextMenus.onClicked.addListener to setup/respond to the context menu.
The context menu entry works as expected. But the background page is showing the following error (right after it starts and before I've used the entry) :
contextMenus.create: Cannot create item with duplicate id id_share_link at chrome-extension://.../share.js:52:30 lastError:29 set
This made me realize that at no point do I remove the item or the listener. Knowing little about javascript and extensions, I'm left wondering if I'm doing everything correctly. I'm assuming this top-level code is going to re-execute every time the background page is invoked. So there are going to be redundant calls to create and addListener (and hence the error I see being logged).
I clearly can't do cleanup in response to suspend, as these calls need to be present to wake up the background script.
Should I be handling things differently?
If you want to use an event page, ie a non-persistent background page, as you call it, you should register a context menu via contextMenus.create in the event handler of runtime.onInstalled, as these context menu registrations ”persist“ anyways.
You have to add the listener-function for the contextMenus.onClicked event every time the event page gets reloaded, though, as the registration of your wish to listen on that event persists, while the handler callback itself does not. So generally don't call contextMenus.onClicked.addListener from runtime.onInstalled, but from top level or other code, that is guaranteed to be executed each time the event page loads.[1]
You can handle it one of two ways:
You can add the context menu and the listeners on install using:
chrome.runtime.onInstalled.addListener(function() {
/* Add context menu and listener */
});
You can remove the context menu and listener, and then re-add it each time the file is called.
[solution may no longer be the case, read comment]
runtime.onInstalled is not triggered if you disable/enable your extension.
My solution is to always add menu items and swallow errors:
'use strict';
{
let seqId = 0;
const createMenuLinkEntry = (title, tab2url) => {
const id = (++seqId).toString();
chrome.contextMenus.create({
id: id,
title: title,
contexts: ['browser_action'],
}, () => {
const err = chrome.runtime.lastError;
if(err) {
console.warn('Context menu error ignored:', err);
}
});
};
createMenuLinkEntry('Go to Google', (tab) => 'https://google.com');
createMenuLinkEntry('Go to GitHub', (tab) => 'https://github.com');
} // namespace

attaching Jquery on partially downloaded DOM [duplicate]

Essentially I want to have a script execute when the contents of a DIV change. Since the scripts are separate (content script in the Chrome extension & webpage script), I need a way simply observe changes in DOM state. I could set up polling but that seems sloppy.
For a long time, DOM3 mutation events were the best available solution, but they have been deprecated for performance reasons. DOM4 Mutation Observers are the replacement for deprecated DOM3 mutation events. They are currently implemented in modern browsers as MutationObserver (or as the vendor-prefixed WebKitMutationObserver in old versions of Chrome):
MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
var observer = new MutationObserver(function(mutations, observer) {
// fired when a mutation occurs
console.log(mutations, observer);
// ...
});
// define what element should be observed by the observer
// and what types of mutations trigger the callback
observer.observe(document, {
subtree: true,
attributes: true
//...
});
This example listens for DOM changes on document and its entire subtree, and it will fire on changes to element attributes as well as structural changes. The draft spec has a full list of valid mutation listener properties:
childList
Set to true if mutations to target's children are to be observed.
attributes
Set to true if mutations to target's attributes are to be observed.
characterData
Set to true if mutations to target's data are to be observed.
subtree
Set to true if mutations to not just target, but also target's descendants are to be observed.
attributeOldValue
Set to true if attributes is set to true and target's attribute value before the mutation needs to be recorded.
characterDataOldValue
Set to true if characterData is set to true and target's data before the mutation needs to be recorded.
attributeFilter
Set to a list of attribute local names (without namespace) if not all attribute mutations need to be observed.
(This list is current as of April 2014; you may check the specification for any changes.)
Edit
This answer is now deprecated. See the answer by apsillers.
Since this is for a Chrome extension, you might as well use the standard DOM event - DOMSubtreeModified. See the support for this event across browsers. It has been supported in Chrome since 1.0.
$("#someDiv").bind("DOMSubtreeModified", function() {
alert("tree changed");
});
See a working example here.
Many sites use AJAX/XHR/fetch to add, show, modify content dynamically and window.history API instead of in-site navigation so current URL is changed programmatically. Such sites are called SPA, short for Single Page Application.
Usual JS methods of detecting page changes
MutationObserver (docs) to literally detect DOM changes.
Info/examples:
How to change the HTML content as it's loading on the page
Performance of MutationObserver to detect nodes in entire DOM.
Lightweight observer to react to a change only if URL also changed:
let lastUrl = location.href;
new MutationObserver(() => {
const url = location.href;
if (url !== lastUrl) {
lastUrl = url;
onUrlChange();
}
}).observe(document, {subtree: true, childList: true});
function onUrlChange() {
console.log('URL changed!', location.href);
}
Event listener for sites that signal content change by sending a DOM event:
pjax:end on document used by many pjax-based sites e.g. GitHub,
see How to run jQuery before and after a pjax load?
message on window used by e.g. Google search in Chrome browser,
see Chrome extension detect Google search refresh
yt-navigate-finish used by Youtube,
see How to detect page navigation on YouTube and modify its appearance seamlessly?
Periodic checking of DOM via setInterval:
Obviously this will work only in cases when you wait for a specific element identified by its id/selector to appear, and it won't let you universally detect new dynamically added content unless you invent some kind of fingerprinting the existing contents.
Cloaking History API:
let _pushState = History.prototype.pushState;
History.prototype.pushState = function (state, title, url) {
_pushState.call(this, state, title, url);
console.log('URL changed', url)
};
Listening to hashchange, popstate events:
window.addEventListener('hashchange', e => {
console.log('URL hash changed', e);
doSomething();
});
window.addEventListener('popstate', e => {
console.log('State changed', e);
doSomething();
});
P.S. All these methods can be used in a WebExtension's content script. It's because the case we're looking at is where the URL was changed via history.pushState or replaceState so the page itself remained the same with the same content script environment.
Another approach depending on how you are changing the div.
If you are using JQuery to change a div's contents with its html() method, you can extend that method and call a registration function each time you put html into a div.
(function( $, oldHtmlMethod ){
// Override the core html method in the jQuery object.
$.fn.html = function(){
// Execute the original HTML method using the
// augmented arguments collection.
var results = oldHtmlMethod.apply( this, arguments );
com.invisibility.elements.findAndRegisterElements(this);
return results;
};
})( jQuery, jQuery.fn.html );
We just intercept the calls to html(), call a registration function with this, which in the context refers to the target element getting new content, then we pass on the call to the original jquery.html() function. Remember to return the results of the original html() method, because JQuery expects it for method chaining.
For more info on method overriding and extension, check out http://www.bennadel.com/blog/2009-Using-Self-Executing-Function-Arguments-To-Override-Core-jQuery-Methods.htm, which is where I cribbed the closure function. Also check out the plugins tutorial at JQuery's site.
In addition to the "raw" tools provided by MutationObserver API, there exist "convenience" libraries to work with DOM mutations.
Consider: MutationObserver represents each DOM change in terms of subtrees. So if you're, for instance, waiting for a certain element to be inserted, it may be deep inside the children of mutations.mutation[i].addedNodes[j].
Another problem is when your own code, in reaction to mutations, changes DOM - you often want to filter it out.
A good convenience library that solves such problems is mutation-summary (disclaimer: I'm not the author, just a satisfied user), which enables you to specify queries of what you're interested in, and get exactly that.
Basic usage example from the docs:
var observer = new MutationSummary({
callback: updateWidgets,
queries: [{
element: '[data-widget]'
}]
});
function updateWidgets(summaries) {
var widgetSummary = summaries[0];
widgetSummary.added.forEach(buildNewWidget);
widgetSummary.removed.forEach(cleanupExistingWidget);
}

Call fabricjs event continuously

I'm calling custom event in this way:
How can I fire custom events on canvas in Fabric JS?
Is there a way to call it continuously like the object:scaling/moving events?
SOLUTION:
I solved this problem using the object:moving event:
canvas.on({'object:moving': handleMovement});
...
var handleMovement = function (event) {
//only when a specific corner was dragged
if (event.target.__corner == 'mb') {
//reset original position
event.target.top = event.target.originalState.top;
event.target.left = event.target.originalState.left;
//do other stuff
}
}
If you need to call the event continuously, you could wrap it in a while loop, or use some sort of timeout.
while (shouldFire) {
canvas.trigger(event);
}
or
setTimeout(triggerEvent, 100);
function triggerEvent() {
canvas.trigger(event);
setTimeout(triggerEvent, 100);
}
However, that might not be ideal for the user (especially the while).
While the object:scaling/moving events may appear to be called continuously, I believe they are simply being called incredibly rapidly in response to user interaction. There's a good event demo on the fabricjs website for looking at this. If you select an object and simply hold it stationary, there are no events fired by the canvas. Instead, the events are only fired in response to user movement, such as dragging the shape around the canvas. So, instead of trying to make the events continuously fired, you could simply listen for small changes in the user's input.

Implementing listeners in Pusher when all there is to work with is bindings?

I'm trying to create a modular application in javascript using pusher. Different modules need to bind to the same pusher event and sometimes that event is nested in another event. Furthermore, these modules get loaded at different times depending on DOM events triggered by the user.
So, if one module has some code like
env.pusher.connection.bind('connected', function() {
env.my_channel.bind('private_message',function(data){ ... }
}
And another module comes along and wants to listen to the same private_message event. What happens if I write the same code is that the first bind gets overwritten.
What I'm looking for is a way to implement some kind of listeners, possibly with the option of removing a listener from a channel event.
I've thought of a solution myself. It comprises of the following steps:
keep a dictionary of pusher events
every module that wants to make use of a pusher event should search the dictionary first to see if that event exists and if not, write the code that creates the bind for the first time and add it to the dictionary
when a module creates the bind for the first time, it should also trigger a custom event and pass to it the data that pusher sends at the completion of the pusher event
every module that wants to make use of a pusher event should add a handler to the custom event that is triggered when the pusher event is triggered
If that looks hard to follow, here's some code inside a module that is a rewrite of the code in my question(I've used jQuery because jQuery is succint and has custom events already implemented):
if (typeof(env.pusher_events['my_channel']['private_message']) == 'undefined'){
env.pusher_events['my_channel']['private_message'] = true;
// 'pusher-connected' is defined in another module
// this module depends on that event but for brevity
// I'm not defining the 'connected' event here
$(document).on('pusher-connected', 'body', function(){
env.my_channel.bind('private_message', function(data){
$('body').trigger('pusher-my_channel-private_message', data);
})
})
}
$(document).on('pusher-my_channel-private_message', 'body', function(data){
// do something useful with the data
}
Would love to get some feedback on this (drawbacks etc.)

Resources