browser-sync for SPA: how to sync document fragments: #/contact - single-page-application

I'm developing a SPA (Single Page Application) and use grunt-browsers-sync.
All browser-sync features seem to work: CSS injection, scrolling and form synchronization.
It's a SPA so no navigation to other pages in done. Navigation is done via routes in the document fragment (I use the SammyJs library for this)
mysite.com/#/home
mysite.com/#/contact
mysite.com/#/...
It seems BrowserSync doesn't synchronizes document fragments.
I think it's because document fragments are handled by the browser and not requested at the BrowserSync server/proxy.
Is there some way to make the scenario work?
PS: I have a javascript callback when navigating, which I can use to send the new url to BrowserSync at development (if BrowserSync supports something like that)

I also tried using browser-sync for a single-page backbone application.
Routes changes are basically triggered on clicking anchors. Unfortunately, browser-sync doesn't play well with events having stopPropagation & hence, the click wasn't triggered in other browsers and routes were synced.
Since then I've forked and fixed this plus other issues namely event syncing for mouseup, mousedown, keyup, keydown and contenteditable div.
The pull-request is still pending though, so meanwhile you can use browser-sync-client from https://github.com/nitinsurana/browser-sync-client
You'll need to have configuration as follows for the fixes to take effect . Notice the capture, contenteditable, mouseup and other config options not present in browser-sync
var bs = require("browser-sync").create();
var client = require("./");
client["plugin:name"] = "client:script";
bs.use(client);
bs.init({
//server: {
// baseDir: ["test/fixtures"]
//},
proxy: 'http://localhost:8080',
open: false,
minify: false,
snippetOptions: {
rule: {
//match: /SHNAE/,
match: /<\/head>/i,
fn: function (snippet) {
return snippet + "\n</head>";
}
}
},
clientEvents: [
"scroll",
"input:text",
"input:toggles",
"input:keydown",
"input:keypress",
"form:submit",
"form:reset",
"click",
"contenteditable:input",
"mouseup",
"mousedown",
"select:change"
],
ghostMode: {
clicks: true,
scroll: true,
forms: {
submit: true,
inputs: true,
toggles: true,
keypress: true,
keydown: true,
contenteditable: true,
change: true
},
mouseup: true,
mousedown: true
},
capture:true
});

Related

Firebase dynamic link acting differently if generated on iOS (React Native) vs REST API

We have a functionality to generate dynamic links to our app on iOS using React Native, which works perfectly (identifiable data redacted by replacing them with "our app"):
dynamicLinks().buildShortLink({
link: `https://our_app?referral=${referralCode}`,
domainUriPrefix: 'https://ourapp.page.link',
android: {
packageName: 'app.ourapp.mobile',
},
ios: {
appStoreId: 'XXX',
bundleId: 'app.ourapp.client',
},
navigation: {
forcedRedirectEnabled: true,
},
});
It correctly opens the app if installed, and App Store if not installed.
I need to implement the same functionality on web site, here's my code:
const payload = {
dynamicLinkInfo: {
link: `https://our_app.app?referral=${referralCode}`,
domainUriPrefix: 'https://ourapp.page.link',
androidInfo: {
androidPackageName: 'app.ourapp.mobile',
},
iosInfo: {
iosBundleId: 'XXX',
iosBundleId: 'app.ourapp.client',
},
navigationInfo: {
enableForcedRedirect: true,
},
}
};
// generate page link and redirect there
const result = await fetch('https://firebasedynamiclinks.googleapis.com/v1/shortLinks?key=OUR_API_KEY', {
method: 'post',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
});
It's the same (with parameter names from React Native changed to REST parameter names respectively, as documented at https://firebase.google.com/docs/reference/dynamic-links/link-shortener)
A link is generated, however when clicked, if the app isn't installed, the page link redirects our website instead of App Store.
When I debug both links using ?d=1 query string parameter, I can indeed notice the difference:
(left: in-app generated, correct. right: REST-generated, incorrect)
Why are these links, generated with the exact same parameters, behaving differently and how can I make the second one work exactly like the first one (redirect to App Store instead of our website)?
After examining closely I've found a typo:
While generating the link in the second example, I was mistakenly using the same key twice:
iosInfo: {
iosBundleId: 'XXX',
iosBundleId: 'app.ourapp.client',
},
Changed the first one to iosAppStoreId and it worked.

gatsby-plugin-google-analytics + Shopify - Conversions not working

I have a Gatsby site sourcing from Shopify, and I'm having a hard time getting the Acquisition Conversions working.
My guess is that when they go to the Checkout page, since it's on Shopify's domain, it sees that as Direct traffic.
My current configuration is:
{
resolve: `gatsby-plugin-google-analytics`,
options: {
trackingId: "...",
head: true,
anonymize: false,
respectDNT: false,
allowLinker: true,
},
},
I just added that allowLinker in to test today. Is there anything else I'm missing? I'm not too familiar with analytics I'm just a humble javascript farmer.
Thank you
Google Analytics recently changed their API to version 4 and upon other things, they changed the way the tracking identifier is set to the page. I suppose that the plugins will migrate soon to that changes but in the meantime, this is the only plugin available that works with that new API version: gatsby-plugin-google-gtag. So:
// In your gatsby-config.js
module.exports = {
plugins: [
{
resolve: `gatsby-plugin-google-gtag`,
options: {
// You can add multiple tracking ids and a pageview event will be fired for all of them.
trackingIds: [
"GA-TRACKING_ID", // Google Analytics / GA
"AW-CONVERSION_ID", // Google Ads / Adwords / AW
"DC-FLOODIGHT_ID", // Marketing Platform advertising products (Display & Video 360, Search Ads 360, and Campaign Manager)
],
// This object gets passed directly to the gtag config command
// This config will be shared across all trackingIds
gtagConfig: {
optimize_id: "OPT_CONTAINER_ID",
anonymize_ip: true,
cookie_expires: 0,
},
// This object is used for configuration specific to this plugin
pluginConfig: {
// Puts tracking script in the head instead of the body
head: false,
// Setting this parameter is also optional
respectDNT: true,
// Avoids sending pageview hits from custom paths
exclude: ["/preview/**", "/do-not-track/me/too/"],
},
},
},
],
}
All the above parameters are optional, just omit them and replace the GA-TRACKING_ID for your real identifier.
I'm not sure if you ever solved it #cYberSport91, but in the year 2021 AD I am trying to do the same. This is what I found:
Place this snippet in your gatsby-ssr.js or gatsby-browser.js:
exports.onRenderBody = ({ setPostBodyComponents }) => {
const attachCode = `
if (ga) {
ga('require', 'linker');
ga('linker:autoLink', ['destination1.com', 'destination2.com']);
}`
setPostBodyComponents([<script dangerouslySetInnerHTML={{
__html: attachCode
}}/>])
}
Source: Gatsby Git Issues
(Still waiting to confirm with my client whether this solution works)

Impossible to load an iframe inside the background page (status=canceled)

I want to dynamicaly inject and load an iframe inside the background page. But every time, the request is canceled.
http://i.imgur.com/Puto33c.png
That used to work a week ago. I don't know where I'm wrong.
To reproduce this issue, I created a small extension :
manifest.js :
{
"name": "iframe background",
"version": "1.0.0",
"manifest_version": 2,
"browser_action": {
"default_title": "iframe"
},
"background": {
"persistent": false,
"scripts": ["background.js"]
}
}
background.js :
chrome.browserAction.onClicked.addListener(function() {
var iframe = document.createElement('iframe');
iframe.src = 'http://localhost:3000/';
iframe.onload = function() {
console.log(iframe.contentDocument); // return null
};
document.body.appendChild(iframe);
});
The page to load is not blocked by X-Frame-Options SAMEORIGIN.
I tried to put the iframe directly within a HTML background page with no luck.
I also tried to add an content_security_policy :
"content_security_policy": "script-src 'self'; object-src 'self'; frame-src 'self' http://localhost:3000/"
But the iframe still doesn't load.
Does someone has a workaround or a solution to this problem?
Thanks !
Chrome 58.0.3014.0 enables Site Isolation for extensions by default that makes the iframe load in a different renderer process handled by a separate chrome.exe OS process.
The 'canceled' message means that the extension's chrome.exe process canceled the request and it was handled by a different hidden chrome.exe process.
The correct approach is to declare a content script that will automatically run on the iframe URL and communicate to the background page. Note: only JSON-fiable data may be passed, in other words, you can pass innerHTML but not DOM elements. This is easy to handle though via DOMParser.
manifest.json additions:
"content_scripts": [{
"matches": ["http://localhost:3000/*"],
"js": ["iframe.js"],
"run_at": "document_end",
"all_frames": true
}],
iframe.js:
var port = chrome.runtime.connect();
// send something immediately
port.postMessage({html: document.documentElement.innerHTML});
// process any further messages from the background page
port.onMessage.addListener(msg => {
..............
// reply
port.postMessage(anyJSONfiableObject); // not DOM elements!
});
background.js:
var iframePort;
chrome.browserAction.onClicked.addListener(() => {
document.body.insertAdjacentHTML('beforeend',
'<iframe src="http://localhost:3000/"></iframe>');
});
chrome.runtime.onConnect.addListener(port => {
// save in a global variable to access it later from other functions
iframePort = port;
port.onMessage.addListener(msg => {
if (msg.html) {
const doc = new DOMParser().parseFromString(msg.html, 'text/html');
console.log(doc);
alert('Received HTML from the iframe, see the console');
}
});
});
See also a similar QA: content.js in iframe from chrome-extension popup

plupload can't show well and maybe it's style conflict

I use pluploadQueue to upload files which all the function could work well except the showing, I think maybe it's due to style confict. I want to know, does anyone have meet this issue before that?
using MVC + bootstrap modal + bootstrap dialog + plupload Queue
I can't upload picture, how to get reputaton?
var uploader1 = $("#uploader1").pluploadQueue({
// General settings
runtimes: 'html5,silverlight,flash,html4',
url: '#Url.Action("PLUpload", "Document")' + '?hasModification=true',
chunk_size: '2mb',
//Allow multiple files
max_file_count: 10,
// Specify what files to browse for
filters: {
// Maximum file size
max_file_size: '20mb',
// Specify what files to browse for
mime_types: [
{ title: "Allowed files", extensions: "#Model.PLUploadAllowedExtensions" }
]
},
// Rename files by clicking on their titles
rename: true,
// Sort files
sortable: true,
// Enable ability to drag'n'drop files onto the widget (currently only HTML5 supports that)
dragdrop: true,
//// Views to activate
views: {
list: true,
thumbs: false, // Show thumbs
active: 'list'
},
init: {
PostInit: function () {
// Called after initialization is finished and internal event handlers bound. http://www.plupload.com/examples/events
// The uploader must in html4 only mode be initialized fully before it's hidden: http://stackoverflow.com/questions/11898082/plupload-html4-add-files-dialog-not-triggering-in-ie
// Prepare uploader
if ('#Model.HasUploadFiles' == 'True') {
$("#hasChanged").attr('value', true);
$("#uploader1").show();
} else {
$("#uploader1").hide();
}
//// Force resize function is not working. this is the fix for window size
//$('#formID').css("overflow", "hidden");
},
},
// Flash settings
flash_swf_url: '#Url.Content("~/Scripts/plupload/Moxie.swf")',
// Silverlight settings
silverlight_xap_url: '#Url.Content("~/Scripts/plupload/Moxie.xap")'
});

ASP.NET Route config for Backbone Routes with PushState

I have run into an issue recently where we have been told to remove the hash symbols from our Backbone applications. This presents two problems: (a) the ASP.NET routes need to handle any remotely linked URL (currently this is no problem with the hash symbols) so that we're not hitting a 404 error and (b) the proper route needs to be preserved and passed on to the client side (Backbone) application. We're currently using ASP.NET MVC5 and Web API 2 for our backend.
The setup
For an example (and test project), I've created a test project with Backbone - a simple C# ASP.NET MVC5 Web Application. It is pretty simple (here is a copy of the index.cshtml file, please ignore what is commented out as they'll be explained next):
<script type="text/javascript">
$(document).ready(function(event) {
Backbone.history.start({
//pushState: true,
//root: "/Home/Index/"
});
var Route = Backbone.Router.extend({
routes: {
"test/:id": function (event) {
$(".row").html("Hello, " + event);
},
"help": function () {
alert("help!");
}
}
});
var appRouter = new Route();
//appRouter.navigate("/test/sometext", { trigger: true });
//appRouter.navigate("/help", { trigger: true });
});
</script>
<div class="jumbotron">
<h3>Backbone PushState Test</h3>
</div>
<div class="row"></div>
Now, without pushState enabled I have no issue remote linking to this route, ie http://localhost/Home/Index#test/sometext
The result of which is that the div with a class of .row is now "Hello, sometext".
The problem
Enabling pushState will allow us to replace that pesky # in the URL with a /, ie: http://localhost/Home/Index/test/sometext. We can use the Backbone method of router.navigate("url", true); (as well as other methods) to use adjust the URL manually. However, this does not solve the problem of remote linking. So, when trying to access http://localhost/Home/Index/test/sample you just end up with the typical 404.0 error served by IIS. so, I assume that it is handled in in the RouteConfig.cs file - inside, I add a "CatchAll" route:
routes.MapRoute(
name: "CatchAll",
url: "{*clientRoute}",
defaults: new { controller = "Home", action = "Index" }
);
I also uncomment out the pushState and root attributes in the Backbone.history.start(); method:
Backbone.history.start({
pushState: true,
root: "/Home/Index/"
});
var Route = Backbone.Router.extend({
routes: {
"test/:id": function (event) {
$(".row").html("Hello, " + event);
},
"help": function () {
alert("help!");
}
}
});
var appRouter = new Route();
//appRouter.navigate("/test/sometext", { trigger: true });
//appRouter.navigate("/help", { trigger: true });
This allows me to at least let get past the 404.0 page when linking to these routes - which is good. However, none of the routes actually "trigger" when I head to them. After attempting to debug them in Chrome, Firefox, and IE11 I notice that none of the events fire. However, if I manually navigate to them using appRouter.navigate("/help", { trigger: true }); the routes are caught and events fired.
I'm at a loss at this point as to where I should start troubleshooting next. I've placed my Javascript inside of the $(document).ready() event as well as the window.onload event also (as well as not inside of an event); none of these correct the issue. Can anyone offer advice on where to look next?
You simply have to move Backbone.history.start after the "new Route" line.
var Route = Backbone.Router.extend({
routes: {
"test/:id": function (event) {
$(".row").html("Hello, " + event);
},
"help": function () {
alert("help!");
}
}
});
var appRouter = new Route();
Backbone.history.start({
pushState: true,
root: "/Home/Index/"
});
Make sure you go to ".../Home/Index/help". If it doesn't work, try temporarily removing the root and go to ".../help" to see if the root is the problem.
If you still have troubles, set a js breakpoint in Backbone.History.loadUrl on the "return" line. It is called from the final line of History.start to execute the current browser url on page load. "this.matchRoot()" must pass then, "fragment" is matched against each "route" or regexp string in "this.handlers". You can see why or why not the browser url matches the route regexps.
To set to the js breakpoint, press F12 in the browser to open the dev console, press Ctrl-O or Ctrl-P to open a js file, then type the name of the backbone js file. Then search for "loadUrl:". You can also search for "Router =" to find the start of the router class definition (same as for "View =" and "Model =" to find the backbone view/model implementation code). I find it quite useful to look at the backbone code when I have a question like this. It is surprisingly readable and what better place to get answers?
If your js files happen to be minified/compressed, preferably turn this off. Alternately you can try the browser unminify option. In Chrome this is the "{}" button or "pretty print". Then the js code is not all on 1 line and you can set breakpoints. But the function and variable names may still be mangled.
I have solved my own problem using what feels to be "hackish", via the following. If anyone can submit a better response it would be appreciated!
My Solution:
I globally override the default Backbone.Router.intilaize method (it is empty) with the following:
$(document).ready(function (event) {
var _root = "/Home/Index/";
_.extend(Backbone.Router.prototype, {
initialize: function () {
/* check for route & navigate to it */
var pathName = window.location.pathname;
var route = pathName.split(_root)[1];
if (route != undefined && route != "") {
route = "/" + route;
this.navigate("", { trigger: false });
this.navigate(route, { trigger: true });
}
}
});
});

Resources