Backbone router working differently with utf-8 characters on Safari and Chrome - backbone.js

I have this example, using a route with an utf-8 non-ascii character:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Backbone Test</title>
</head>
<body>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script src="http://ajax.cdnjs.com/ajax/libs/underscore.js/1.3.1/underscore-min.js"></script>
<script src="http://ajax.cdnjs.com/ajax/libs/backbone.js/0.9.2/backbone-min.js"></script>
<script>
(function(){
window.App = {
Models: {},
Collections: {},
Views: {},
Router: {}
};
})();
App.Router = Backbone.Router.extend({
routes: {
'charñ': 'charChrome',
'char%C3%B1': 'charSafari'
},
charChrome: function(){
$(document.body).append("Chrome-compatible route triggered.<br/>");
},
charSafari: function(){
$(document.body).append("Safari-compatible route triggered.<br/>");
},
});
new App.Router;
Backbone.history.start();
</script>
<h1>HELLO THERE</h1>
</body>
</html>
When the page got called with something like:
file://localhost/whatever.html#charñ
...it trigger a different function on Safari and Chrome.
It's REALLY not easy for me to change backbone version.
Is there a way to avoid that difference?
Note: interesting enough, removing the meta tag breaks Chrome routing.

This shouldn't be seen as an answer to that makes every browsers works the same but prevent this from happening. As far as I know, there is no much control on what backone will trigger. It's not like you can really filter the url before it gets handled by the Router.
That said, you can create routes for both style. To make this easier, you can create a special object that will get a utf8 string and create a urlencoded version of the route. Both routes will have the same callback.
On other possibility is to avoid utf-8 symbols in the url and report a bug to backbonejs. Having both routes created with urlencoded/urldecoded will make the site work for possibly every browsers supporting javascript. The downside is that you'll have to create n*2 routes.

Have you tried decoding the URL when it's passed to your function? It may be that Chrome decodes the URL for you while Safari does not. 'char%C3%B1' will decode to 'charñ' just fine when using a URI decoder, and it should have no effect on already-decoded strings (assuming the encoding used was the correct one, of course).
decodeURIComponent('char%C3%B1')
/*
charñ
*/
decodeURIComponent('charñ')
/*
charñ
*/
Going by http://backbonejs.org/docs/backbone.html#section-156, you may be able to substitute _extractParameters with a version that calls decodeURI or decodeURIComponent on fragment before executing the regular expression.
Another possibility to try, as I just noticed that routes can be regular expressions (see http://backbonejs.org/docs/backbone.html#section-152): if you don't have many of them, you could use something like /char(ñ|%C3%B1)/g, or write a function to produce such a regular expression for the unencoded value, such that makeRegex('charñ') would produce /char(ñ|%C3%B1)/g, or whatever the regular expression would be.

I solved it by adding an additional line in the while loop, inside _bindRoutes, to bind the encoded routes as well:
_bindRoutes: function() {
if (!this.routes) return;
this.routes = _.result(this, 'routes');
var route, routes = _.keys(this.routes);
while ((route = routes.pop()) != null) {
this.route(route, this.routes[route]);
//add this line:
this.route(encodeURIComponent(route).replace(/%2F/g,'/').replace(/%3A/g,':'), this.routes[route]);
}
}

Related

how to resolve Javascript issues when using 2sxc IRenderService to embed an App in a DNN Theme Layout

Im trying to embed my 2sxc App into a Theme layout page.
The App view html does render
<%-- This namespace provides this.GetScopedService<T>() --%>
<%# Import Namespace="ToSic.Sxc.Dnn" %>
<%-- This namespace provides all the common 2sxc services --%>
<%# Import Namespace="ToSic.Sxc.Services" %>
<%= this.GetScopedService<IRenderService>().Module(1041,3421) %>
, but its Javascript crashes.
The App uses its own API for searching.
1. // get the sxc-controller for this module
2. var sxc = $2sxc(3421);
3. // now get the data in the promise
4. sxc.webApi.get('app/auto/api/Forms/SearchForm')
5. .then(data => {
6. console.log(data)
7. });
originally line 2 crashed saying $2sxc is not recognised.
we resolved that by adding this script reference to our layout page
<script src="/desktopmodules/tosic_sexycontent/js/2sxc.api.min.js" type="text/javascript"></script>
And now it crashes on line 4 when trying to use sxc.webApi.get
Uncaught Can't find page - something went wrong, pls contact 2sxc.org
It seems I need to include another JS script.
I tried to also include
<script src="/desktopmodules/tosic_sexycontent/dist/inpage/inpage.min.js" type="text/javascript"></script>
but that made it worse
I resolved this by adding this
var page = GetService<ToSic.Sxc.Services.IPageService>();
page.Activate("2sxc.JsCore");
Update: to use the JS better in the theme, I've just pushed an update to 14.07.04.
Basically you can force a different context than the automatic one, using
var sxc = $2sxc({ pageId: 27, moduleId: 42, zoneId: 3, appId: 8});
sxc.webApi.fetchJson(...).then(...);
this uses the context identifier https://docs.2sxc.org/api/js/ContextIdentifier.html#Api_Js_SxcJs_ContextIdentifier which has existed for a few versions now, but there was a bug that was fixed in 14.07.04
See also https://docs.2sxc.org/api/js/SxcGlobal.html#Api_Js_SxcJs_SxcGlobal_get_3

How To Lock Code With password in reactjs

I Want To Add a Password System To My Code To Prevent My Code From Being Run By Someone else, is There Any Way How Can i do so?
Interface can be like asking for password before starting development server in reactjs
I have put together an example of what I meant in the comments:
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title>SourceCode Encryption example</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/sjcl/1.0.8/sjcl.min.js" integrity="sha512-s0r9TyYSs5CusSDL5s/QEZQZ4DNYLDXx0S+imS+SHR9mw0Yd0Z5KNS9dw7levSp7GpjOZu/bndds3FEDrKd8dg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
</head>
<body>
<script type="text/javascript">
// Hide this part from the end user
var password = "MySourceCodePassword";
var sourceCode = 'alert(\'Ur name is: \' + prompt("What is ur name?"))';
var encryptedSourceCode = sjcl.encrypt(password, sourceCode);
// User endpoint code
// You would have the encryptedSourceCode stored into an string and only
// is the propper key is used then it would be decrypted and evaluated.
try {
var userPassword = prompt("Whats the sourceCode password?");
eval(sjcl.decrypt(userPassword, encryptedSourceCode));
} catch (error) {
alert(error.message);
}
</script>
</body>
</html>
In your case you would have to provide your encoded Reactjs code and the required html dom element, I have simplied this example wrapping it in an html page and prompting the user for a password but u could use a form or whatever you might require.
Be aware that once the know the key to decrypt the will be able to see your javascript source code, and so far it is the way it works, since the browser need to read javascript you cannot hide it from it. If you wanna make it harded you can always minify so it is harder to read.

Implementing google custom search in angularjs

I am trying to implement google custom search in an angular js website.
When I click on the search button it does not display me anything, but the url is updated to the url.
I have followed the steps mentioned in the documentation by google.
I am not sure what I am doing wrong?
My search bar is located on the home page as -
<gcse:searchbox-only enableAutoComplete="true" resultsUrl="#/searchresult" lr="lang_en" queryParameterName="search"></gcse:searchbox-only>
my search result has -
<gcse:searchresults-only lr="lang_en"></gcse:searchresults-only>
Any input is much appreciated.
Thanks,
You may have more than one problem happening at the same time...
1. Query Parameter mismatch
Your searchresults-only does not match the queryParameterName specified on gcse:searchbox-only.
Index.html
<gcse:searchresults-only queryParameterName="search"></gcse:searchresults-only>
Search.html
<gcse:searchresults-only queryParameterName="search"></gcse:searchresults-only>
2. Angular.js is blocking the flow of Google CSE
Under normal circumstances, Google Search Element will trigger an HTTP GET with the search parameter. However, since you are dealing with a one-page application, you may not see the query parameter. If that suspicion is true when you target resultsUrl="#/searchresult", then you have two options:
Force a HTTP GET on resultsUrl="http://YOURWEBSITE/searchresult". You may have to match routes, or something along those lines in order to catch the REST request (Ember.js is really easy to do so, but I haven't done in Angular.js yet.)
Use JQuery alongside Angular.js to get the input from the user on Index.html and manually trigger a search on search.html. How would you do it? For the index.html you would do something like below and for the results you would implement something like I answered in another post.
Index.html
<div>GSC SEARCH BUTTON HOOK: <strong><div id="search_button_hook">NOT ACTIVATED.</div></strong></div>
<div>GSC SEARCH TEXT: <strong><div id="search_text_hook"></div></strong></div>
<gcse:search ></gcse:search>
Index.js
//Hook a callback into the rendered Google Search. From my understanding, this is possible because the outermost rendered div has id of "___gcse_0".
window.__gcse = {
callback: googleCSELoaded
};
//When it renders, their initial customized function cseLoaded() is triggered which adds more hooks. I added comments to what each one does:
function googleCSELoaded() {
$(".gsc-search-button").click(function() {
$("#search_button_hook").text('HOOK ACTIVATED');
});
$("#gsc-i-id1").keydown(function(e) {
if (e.which == 13) {
$("#enter_keyboard_hook").text('HOOK ACTIVATED');
}
else{
$("#search_text_hook").text($("#gsc-i-id1").val());
}
});
}
(function() {
var cx = '001386805071419863133:cb1vfab8b4y';
var gcse = document.createElement('script');
gcse.type = 'text/javascript';
gcse.async = true;
gcse.src = 'https://cse.google.com/cse.js?cx=' + cx;
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(gcse, s);
})();
I have a live version of the index.html code, but I don't make promises that will be permanently live since it is hosted in my NDSU FTP.

How do I adapt a Google AdWords tracking pixel for use in an AngularJS app?

How do I adapt an AdWords tracking pixel to function as intended within an AngularJS application?
The typical tracking code looks like this:
<script type="text/javascript">
/* <![CDATA[ */
var google_conversion_id = 123456789;
var google_conversion_language = "en";
var google_conversion_format = "2";
var google_conversion_color = "ffffff";
var google_conversion_label = "AAAAAAAAAAAAAAAAAAA";
var google_conversion_value = 0;
/* ]]> */
</script>
<script type="text/javascript"
src="//www.googleadservices.com/pagead/conversion.js">
</script>
(I've omitted the standard <noscript> fallback, as it's obviously irrelevant in the context of an AngularJS app.)
The tracking code works by setting a bunch of variables in the global namespace, then fetching an external script, on every page load. In an Angular context, this doesn't work because the HTML source isn't retrieved anew from the server on each page load.
My initial (and possibly non-functional) attempt to adapt this to Angular looks like this (in Coffeescript):
SpiffyApp.run ($rootScope, $location, $window, session, flash) ->
# Other initialization stuff
$rootScope.$on '$routeChangeSuccess', (event, data) ->
# Other route-change callback stuff
$window.google_conversion_id = 123456789
$window.google_conversion_language = "en"
$window.google_conversion_format = "2"
$window.google_conversion_color = "ffffff"
$window.google_conversion_label = "AAAAAAAAAAAAAAAAAAA"
$window.google_conversion_value = 0
jQuery.ajax
type: "GET",
url: "//www.googleadservices.com/pagead/conversion.js",
dataType: "script",
cache: true
This doesn't appear to be working. At least, the marketing consultants are claiming such. I recognize there's a pretty decent chance of PEBKAC here, so my questions:
Should the above work?
If not, what would work?
Thanks in advance!
PS: I've inherited this app from another developer, and I'm not (yet) well-versed in the platform. Feel free to point out (in the comments) any grievously bad code/practices above. Thanks!
I am not an expert on AngularJS, but this might be something that can be resolved by using the asynchronous version of the AdWords tracking pixel as the conversions for that can just be called with a standard javascript function call and does not rely on the page load.
You can include the asynchronous version of the AdWords tracking pixel like this (make sure you use the https version):
<script type="text/javascript" src="https://www.googleadservices.com/pagead/conversion_async.js" charset="utf-8">
Then once you have done that, you'll get a "google_trackConversion" function added to window which you can then just call whenever you need it, e.g.
window.google_trackConversion({
google_conversion_id: 123456789,
google_conversion_label: 'AAAAAAAAAAAAAAAAAAA',
google_conversion_language: "en",
google_conversion_format: "2",
google_conversion_color: "ffffff",
google_conversion_value: 0
});
HTH

Google Analytics don't seem to be firing

I've tried to look around many threads here around GA, I feel like I've done what's required. My site consist of HTML5 and angular.js, the page is structured roughly as follow (through ng-include)
index.html
|-header
--|-navigation
|-content
|-footer
when a link from navigation bar is clicked, the content will change (partials page in angular), header, footer stays the same.
The last script tag in my < head > is as follow (inside index.html)
<script type="text/javascript">
var globalLanguage = 'en';
// GA tracking variable
var _gaq = [['_setAccount', 'UA-XXXXXXXX-X'], ['_trackPageview']];
</script>
and at the bottom part of index.html, before the closing < / body > tag:
<script type="text/javascript">
(function(d, t) {
var g = d.createElement(t), s = d.getElementsByTagName(t)[0];
g.async = 1;
g.src = '//www.google-analytics.com/ga.js';
g.type = 'text/javascript';
s.parentNode.insertBefore(g, s);
}(document, 'script'));
</script>
All my angular controller function, call a common function as described in Tracking Google Analytics Page Views with Angular.js
function gaqPageView($scope, $location, $window) {
console.log('triggering google analytics');
$scope.$on('$viewContentLoaded', function(event) {
console.log('event triggered, tracking: ' + $location.path());
$window._gaq.push([ '_trackPageview', $location.path() ]);
});
}
I do see the console log statements, there's no error in the console either.
When I print out the content of _gaq, I do get an array that grows as I navigate around the page (which mean my _gaq.push call is working just fine).
However, in my the network call (in chrome dev tool), I don't see any _utm.gif call to Google Analytics. (Basics of Debugging Google Analytics Code: GA Chrome Debugger and other tools).
What am I missing here? seems like the google analytics is not firing off the event and reporting it?
Edit: I am pretty sure I am silly here, the _gaq variable itself is just a normal javascript array, so of course _gaq.push work just fine. But what am I missing to get Google Analytics to kick in and start sending the content of that _gaq?
Are you currently running your server on localhost, or an intranet name without a "." -- the tracking GIF request doesn't get made for localhost servers by default.
See Google Analytics GIF request not sent.
Another idea: Usually _gaq is defined as an array only if it's not already defined. If ga.js has already executed, you might be overwriting the _gaq object. It doesn't seem likely from your code organization, but...
Try replacing
var _gaq = [['_setAccount', 'UA-XXXXXXXX-X'], ['_trackPageview']];
with
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-XXXXXXXX-X'], ['_trackPageview']);

Resources