Fotorama - deep linking and browser history - fotorama

I love the fact that Fotorama updates the URL with a hash specific to each slide, but when I use the browser back button the slideshow doesn't update to the previously viewed slide.
Is there a way to have Fotorama record the deep linking in the browser history, so that using the browsers back/forward buttons update the slide show accordingly?
Thanks

There is no built-in way to do that. Write your own solution, using 'fotorama:show' event and fotorama.show()method:
<script>
$(function () {
window.onpopstate = function (e) {
var hash = location.hash.replace(/^#/, '');
if (fotorama && hash) {
fotorama.show(hash);
}
};
var fotorama = $('.fotorama')
.on('fotorama:show', function (e, fotorama) {
var hash = fotorama.activeFrame.id || fotorama.activeIndex;
console.log('fotorama:show', hash);
if (window.history && location.hash.replace(/^#/, '') != hash) {
history.pushState(null, null, '#' + hash);
}
})
.fotorama({
startindex: location.hash.replace(/^#/, '')
})
.data('fotorama');
});
</script>
<!-- Fotorama -->
<div class="fotorama"
data-auto="false">
</div>
Attribute data-auto="false" is important!
Demo: http://jsfiddle.net/artpolikarpov/2Enwa/show/
Original fiddle: http://jsfiddle.net/artpolikarpov/2Enwa/ (not working because of iframe)

Related

ng-style background-image url not working for Firebase Storage reference

I'm trying to load background images in a div. I tried several options but my page is blank, nothing is displayed. Below are the different styles I tried:
<div ng-style="{'background-image': 'url(' + profilepic + ')'}"></div>
<div ng-style="{'background-image': 'url('{{profilepic}}')'}"></div>
Where, in my controller:
storageRef.child('images/pic.png').getDownloadURL().then(function(url) {
// Get the download URL for 'images/stars.jpg'
// This can be inserted into an <img> tag
// This can also be downloaded directly
$scope.profilepic = url;
}).catch(function(error) {
// Handle any errors
});
There is no error in the console log. I get the actual url.
This works works, te image is properly displayed, but it's not what I'm trying to accomplish:
<div style="{'background-image': 'url('img/pic.png')'}"></div>
I went through similar posts and tried their solutions, but none of them seem to work.
I finally see what was the problem. I have to share this, it may help others. Everything was fine with my HTML below:
<ion-content>
<div ng-style="{'background-image': 'url('+pic+')'}">
<div class="content">
<div class="avatar" ng-style="{'background-image': 'url('+profilepic+')'}"></div>
<h3>{{fname.txt}} {{lname.txt}}</h3>
</div>
</div>
</ion-content>
The issue was within my controller. This is what I had:
auth.onAuthStateChanged(function(user) {
var storage = firebase.storage();
var storageRef = storage.ref();
var storageRefPic = '';
storageRef.child('images/pic.jpg').getDownloadURL().then(function(url) {
storageRefDefltPic = url;
}).catch(function(error) {
// Handle any errors
});
$scope.pic = storageRefDefltPic;
var storageRefProfilePic = '';
storageRef.child('images/profilepic.jpg').getDownloadURL().then(function(url) {
storageRefProfilePic = url;
}).catch(function(error) {
// Handle any errors
});
$scope.profilepic = storageRefProfilePic;
fbRef.child(userID).once('value').then(function(snapshot) {
$scope.fname.txt = snapshot.val().firstName;
$scope.lname.txt = snapshot.val().lastName;
$scope.pic = storageRefProfilePic;
});
});
My variables and code were completely mixed up. In the console, I came to realize that the firebase reference was getting called first, before the storage reference, causing my scope variables to come out empty, consequently the profile images to be blank. So I removed unnecessary variables and moved the storage references within the callback, before the data snapshot, as below:
auth.onAuthStateChanged(function(user) {
fbRef.child(userID).once('value').then(function(snapshot) {
var storage = firebase.storage();
var storageRef = storage.ref();
var storageRefPic = '';
storageRef.child('images/pic.jpg').getDownloadURL().then(function(url) {
$scope.pic = url;
}).catch(function(error) {
// Handle any errors
});
storageRef.child('images/profilepic.jpg').getDownloadURL().then(function(url) {
$scope.profilepic = url;
}).catch(function(error) {
// Handle any errors
});
$scope.fname.txt = snapshot.val().firstName;
$scope.lname.txt = snapshot.val().lastName;
});
});
Now everything is working fine! I thank you all guys for your help! I sure do appreciate you!
If I understood you correct - the problem is solved fairly easy.
You need to set the size for the div or put some content inside as background-image does not count as content.
Which leads to an empty div with height =0;
here is a working Fiddle
It should be:
<div ng-style="{'background-image': 'url({{profilepic}})'}"></div>
In your case you should use this:
<div ng-style="{'background-image': 'url(' + profilepic + ')'}"></div>
Updated JSFiddle

wikipedia api - relative urls | remove or redirect links

Currently I've been using the following request to display the Wikipedia content on my AngularJS app:
https://en.wikipedia.org/w/api.php?action=parse&format=json&callback=JSON_CALLBACK&page=little%20tinamou
Using the following, I display all the text on the page:
var lowercaseBirdname = $filter('lowercase')($scope.birdname);
console.log(lowercaseBirdname);
birdApi.getWikipedia(lowercaseBirdname)
.then(function(res) {
console.log(res.data.parse.text['*']);
var toHtml = res.data.parse.text['*'];
document.getElementById("extract").innerHTML = toHtml;
});
All the images, external links are showing, though in the html you can see that the page has alot of '/wiki/' links which redirects me to my own url.
How do I bypass this, do give a redirect to the wikipage on a new tab, or can I simply remove all the links while keeping the layout/images?
You can manipulate the html using angular.element() and adjust the href of resultant dom nodes then return to string before passing to the view:
var url = 'https://en.wikipedia.org/w/api.php?action=parse&format=json&callback=JSON_CALLBACK&page=little%20tinamou',
wikiBaseUrl = "http://en.wikipedia.org";
$http.jsonp(url).then(function(resp){
var html = resp.data.parse.text['*'];
// create a div and append html data
var div = angular.element('<div>').append(html),
// create collection of the `<a>` elements
links = div.find('a');
// loop over `<a>` elements and adjust href
for(var i =0; i<links.length; i++ ){
var el = links[i];
var $link =angular.element(el) , href = $link.attr('href');
if(href[0] ==='/'){
// set absolute URL.
$link.attr('href', wikiBaseUrl + href)
}
}
// return the modified html string from the div element
$scope.html = div.html();
});
Note that many of the href are hashes for in-page ID. Not sure what you want to do with those
Should use ng-bind-html along with ngSanitze and not do any dom manipulation in controllers
<div ng-bind-html="html">
DEMO
Since angular has embedded jquery functionality as angular.element, you can do and wrap all the html manipulation in a custom directive.
This directive will get the html string from wikipedia api response, load them into an element, lookup and replace for relative urls and with wikipedia base url and will store the result on the directive's element.
Online demo - https://plnkr.co/edit/0wtFVOhxw0NfRw43x8K6?p=preview
html:
<button ng-click="reload()">Reload</button>
<hr>
<div wikipedia-content="getWikipediaContent()"></div>
javascript:
var app = angular.module('plunker', []);
var WIKIPEDIA_BASE_URL = 'http://en.wikipedia.org';
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
$scope.reload = function() {
// hard coded for testing purposes
$scope.response = {
"parse": {
"title": "Little tinamou",
"pageid": 805527,
"revid": 697345219,
"text": {
"*": "<div>\n<table cl ... "
}
}
};
};
$scope.getWikipediaContent = function getWikipediaContent() {
if (!$scope.response) {
return '';
}
return $scope.response.parse.text['*']
};
});
app.directive("wikipediaContent", function() {
return {
scope: {
wikipediaContent: '='
},
link: function(scope, directiveElement) {
scope.$watch('wikipediaContent', function() {
if (!scope.wikipediaContent) {
return;
}
var wikipediaElement = angular.element(scope.wikipediaContent);
wikipediaElement.find('a').each(function() {
var element = angular.element(this);
var href = element.attr('href');
if (href.match(/^http/)) {
return;
}
element.attr('href', WIKIPEDIA_BASE_URL + href);
});
directiveElement.replaceWith(wikipediaElement);
});
}
}
});
https://plnkr.co/edit/0wtFVOhxw0NfRw43x8K6?p=preview
Replace all the relative urls with absoluteUrls,
example: replace /wiki/File:Crypturellus_soui.jpg with https://en.wikipedia.org/wiki/File:Crypturellus_soui.jpg
NOTE: This should open the image in wikipedia page in current browser tab.

Angularjs + Phonegap anchor tag does not open web page

I am using AngularJS and PhoneGap to create a mobile app. I've a HTML string with anchor tag, which I display on screen using ng-bind-html. But when I touch the link on the phone it does not open the web site at all. My intention is to open the web site in new browser window. The string that I am passing into ng-bind-html is :
"www.google.com";
Also, Im using InAppBrowser Phonegap plugin for this. But this is still not working.
Please advice.
You can use :
www.google.com
More... https://docs.angularjs.org/api/ng/directive/a
OR
www.google.com
$( "#a_id" ).click(function() {
var url = $(this).attr('href')
var ref = window.open(url, 'random_string', 'location=no');
ref.addEventListener('loadstart', function(event) {
console.log(event.type + ' - ' + event.url);
} );
ref.addEventListener('loadstop', function(event) {
console.log(event.type + ' - ' + event.url);
} );
ref.addEventListener('exit', function(event) {
//console.log(event.type + ' - ' + event.url);
} );
});
I came across this problem to enable links in a twitter list which I was showing in my app.
I ended up using the following with a little help of jQuery:
1) use a filter to add a class to your links
html:
<span ng-bind-html="myLinks | externalLinks"></span>
filter in controller.js
.filter('externalLinks', function() {
return function(text) {
return String(text).replace(/href=/gm, "class=\"ex-link\" href=");
}
});
2) in your controller you can use jQuery to add a click event to the links (I am using a timeout to be sure the links are loaded into the DOM and don't forget to inject $timeout in your controller)
$timeout(function(){
jQuery('.ex-link').click(function (e) {
var url = jQuery(this).attr('href');
e.preventDefault();
var system = "";
if(device.platform == "iOS"){
system = "_blank";
}
else {
system = "_system"
}
window.open(url, system, "location=yes");
})
},1000);
Hope this helps.

Capture "updater" action from AngularUI's Bootstrap Typeahead

How can I capture the "updater" event from AngularUI's UI Bootstrap directive?
I've defined my HTML:
<input type="text" pattern="[0-9]*" class="span6" placeholder="8675309" ng-model="empid" typeahead="entry for entry in httpEmpIdSearch($viewValue, 8)">
... and my associated function:
$scope.httpEmpIdSearch = function(query, limit)
{
return $http.get(
'/visitorLog/api/v1/employee/?limit=' + limit + '&empid__startswith=' + query
).then(function(response)
{
output = [];
angular.forEach(response.data.objects, function(value, key) {
this.push(value.bems.toString());
}, output);
return output;
});
}
I would like to take additional action (autocomplete a form) when the user clicks on an ID in the popup. If raw Bootstrap I would use the "updater":
$('#sampleElement').typeahead({
minLength: 3,
source: function (query, process)
{
// get the data
},
updater: function (item)
{
// user clicked on an item, do something more!
},
});
I've tried various listeners like:
$scope.$on('typeahead-updated', function(event)
{ ... }
But I've not way to let me capture such an event. Is there some way I can take additional action once a typeahead option is selected?
Also, in version 0.4.0 of the ui-bootstrap library the typeahead-on-select directive is now supported. This should provide your desired event.
See:
https://github.com/angular-ui/bootstrap/commit/91ac17c9ed691a99647b66b3f464e3585398be19
It does not appear that there is a way to hook into that event in the directive. You could however put a watch on the model value and simulate this behavior.
Check out this plunker example
Since you are pulling the list of data from the server on the fly this solution may not work for you. But I would definitely have a look at watch it seems like the best way to achieve the desired outcome.
Something along these lines could work.
$scope.output = [];
$scope.httpEmpIdSearch = function(query, limit)
{
return $http.get(
'/visitorLog/api/v1/employee/?limit=' + limit + '&empid__startswith=' + query
).then(function(response)
{
$scope.output.length = 0;
angular.forEach(response.data.objects, function(value, key) {
this.push(value.bems.toString());
}, $scope.output);
return $scope.output;
});
}
$scope.$watch('empid', function(newValue, oldValue){
if(newValue !== oldValue && $scope.output.indexOf(newValue) !== -1){
//do work here
}
});
The full list of typeahead related directives is here:
https://github.com/angular-ui/bootstrap/tree/master/src/typeahead/docs
typeahead-editable, typeahead-input-formatter, typeahead-loading, typeahead-min-length, typeahead-on-select, typeahead-template-url, typeahead-wait-ms

Tracking Site Activity (Google Analytics) using Backbone

I am looking the best way to track the Site Activity in Google Analytics for a web app made with Backbone and Requires.
Looking At Google's Page, I found this drop-in plugin - Backbone.Analytics.
My questions are:
1) using Backbone.Analytics, should I change backbone.analytics.js in order to add _gaq.push(['_setAccount', 'UA-XXXXX-X']);?
2) Are there other possible solutions/plugins?
I prefer "do it yourself" style :) It's really simple:
var Router = Backbone.Router.extend({
initialize: function()
{
//track every route change as a page view in google analytics
this.bind('route', this.trackPageview);
},
trackPageview: function ()
{
var url = Backbone.history.getFragment();
//prepend slash
if (!/^\//.test(url) && url != "")
{
url = "/" + url;
}
_gaq.push(['_trackPageview', url]);
}
}
And you add google analytics script to your page as usual.
You shouldn't have to change anything. Just add your Google Analytics code snippet, like normal, and include Backbone.Analytics as you would any other Javascript library.
Just figured i'd share how i'm doing it. This might not work for larger apps but I like manually telling GA when to track page views or other events. I tried binding to "all" or "route" but couldn't quite get it to record all the actions that I need automajically.
App.Router = BB.Router.extend({
//...
track: function(){
var url = Backbone.history.getFragment();
// Add a slash if neccesary
if (!/^\//.test(url)) url = '/' + url;
// Record page view
ga('send', {
'hitType': 'pageview',
'page': url
});
}
});
So i just call App.Router.Main.track(); after I navigate or do anything i want to track.
Do note that I use the new Analytics.js tracking snippet which is currently in public beta but has an API so intuitive that it eliminates the need for a plugin to abstract any complexity what so ever. For example: I keep track of how many people scroll to end of an infinite scroll view like this:
onEnd: function(){
ga('send', 'event', 'scrollEvents', 'Scrolled to end');
}
Good luck.
I wrote a small post on this, hope it helps someone:
http://sizeableidea.com/adding-google-analytics-to-your-backbone-js-app/
var appRouter = Backbone.Router.extend({
initialize: function() {
this.bind('route', this.pageView);
},
routes: {
'dashboard': 'dashboardPageHandler'
},
dashboardPageHandler: function() {
// add your page-level logic here...
},
pageView : function(){
var url = Backbone.history.getFragment();
if (!/^\//.test(url) && url != ""){
url = "/" + url;
}
if(! _.isUndefined(_gaq)){
_gaq.push(['_trackPageview', url]);
}
}
});
var router = new appRouter();
Backbone.history.start();
Regarding other possible solutions/plugins, I've used https://github.com/aterris/backbone.analytics in a few projects and it works quite well as well. It also has options for a few more things like event tracking which can be handy at some point in your analytics integration.
If you use the new universal analytics.js, you can do that like this:
var Router = Backbone.Router.extend({
routes: {
"*path": "page",
},
initialize: function(){
// Track every route and call trackPage
this.bind('route', this.trackPage);
},
trackPage: function(){
var url = Backbone.history.getFragment();
// Add a slash if neccesary
if (!/^\//.test(url)) url = '/' + url;
// Analytics.js code to track pageview
ga('send', {
'hitType': 'pageview',
'page': url
});
},
// If you have a method that render pages in your application and
// call navigate to change the url, you can call trackPage after
// this.navigate()
pageview: function(path){
this.navigate(path);
pageView = new PageView;
pageView.render();
// It's better call trackPage after render because by default
// analytics.js passes the meta tag title to Google Analytics
this.trackPage();
}
}
All answers seem to be almost good, but out-of-date (Sept. 2015). Following this Google devs guide: https://developers.google.com/analytics/devguides/collection/analyticsjs/single-page-applications
Here's my version of the solution (I've added the suggested call to ga('set'...) ):
MyRouter = Backbone.Router.extend
...
initialize: () ->
# Track every route and call trackPage
#bind 'route', #trackPage
trackPage: () ->
url = Backbone.history.getFragment()
# Add a slash if neccesary
if not /^\//.test(url) then url = '/' + url
# Analytics.js code to track pageview
global.ga('set', {page: url})
global.ga('send', 'pageview')
...
Just posting an update to this question as it seems to be one I get a lot from backbone.js developers I know or work with who seem to fall at the last hurdle.
The Javascript:
App.trackPage = function() {
var url;
if (typeof ga !== "undefined" && ga !== null) {
url = Backbone.history.getFragment();
return ga('send', 'pageview', '/' + url);
}
};
Backbone.history.on("route", function() {
return App.trackPage();
});
The Tracking Snippet:
<head>
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||
function(){(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();
a=s.createElement(o),m=s.getElementsByTagName(o)[0];a.async=1;
a.src=g;m.parentNode.insertBefore(a,m)})(window,document,'script',
'//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-XXXXXXXX-X', 'auto');
</script>
</head>
The Tracking Snippet should be available on any page you wish to track activity. This could be your index.html where all your content is injected, but some sites may have multiple static pages or a mix. You can include the ga('send') function if you wish, but it will only fire on a page load.
I wrote a blog post that goes in to more detail, explaining rather than showing, the full process which you can find here: http://v9solutions.co.uk/tech/2016/02/15/how-to-add-google-analytics-to-backbone.js.html

Resources