0

Been pulling my hair out over a certain issue. I can't seem to find out why my pushState router is not working on our development server while it works perfectly on 2 other real servers (staging and client's live domain) and it also works for my local MAMP server.

Here's my router:

    jQuery(function(){
        // define routes
        var Router = Backbone.Router.extend({
           routes: {
                '': 'chrono',
                ':number': 'chrono',
                ':number/plus': 'chrono'
           },

          // load views
          initialize: function() {
              Backbone.history.start({
                  pushState: true,
                  hashChange: false     // use html5 pushState with hashChange set to false
                                        // to handle navigation of hash anchors
            });
          },

          chrono: function(number) {
                url = window.location.hash.split("/");

                this.reset();
                this.content = new chronoView();

                if(number == undefined){
                    number = 0;
                }
                if(url[1]=="plus") {
                    this.content.showMiddleBox();
                    jQuery('#overlay2').show();
                }
          },

          reset: function() {
                if (this.content != undefined){
                    this.content.hide();
                 }
          }
      });

      var Router = new Router;
    });

My .htaccess file on the non-working server does have quite a few engine re-write rules already, and the file itself is 200+ lines but here's what I deem to be the relevant parts:

    # Block access to "hidden" directories whose names begin with a period. This
    # includes directories used by version control systems such as Subversion or Git.
    <IfModule mod_rewrite.c>
      RewriteCond %{SCRIPT_FILENAME} -d
      RewriteCond %{SCRIPT_FILENAME} -f
      RewriteRule "(^|/)\." - [F]
    </IfModule>

    <IfModule !mod_rewrite.c>
        # If we don't have mod_rewrite installed, all 404's
        # can be sent to index.php, and everything works as normal.
        # Submitted by: ElliotHaughin

        ErrorDocument 404 index.php
    </IfModule>

    # URL and hash rewrite for backbone
    # Used for HTML 5 pushState support *NOT WORKING*
    <ifModule mod_rewrite.c>
        RewriteEngine On
        RewriteCond %{REQUEST_FILENAME} !-f
        RewriteCond %{REQUEST_FILENAME} !-d
        RewriteCond %{REQUEST_URI} !index
        RewriteRule (.*) index.html [L]
    </ifModule>

/* EDIT */

After comparing the .htaccess file of the non-working server against the working server.htaccess I've not had any success, as both files were essentially the same except for some accounting for subfolder paths. I.e.:

Working server: RewriteRule ^(.*)$ index.php?/$1 [L] Non-working server: RewriteRule ^(.*)$ dj24/index.php?/$1 [L]

And that's the only difference between the two files, but this makes sense because the client's version (working .htaccess) is hosted on subdomain with no subfolder, i.e. http://subdomain.ClientsDomain.com/ whereas our development server version hosts the project on a subfolder path to a subdomain, i.e. http://dev.Our_Domain.com/OurApplication

I'm starting to believe it's the subdomain + subfolder combo that's causing the issue. Any help with this would be appreciated. Thanks!

DrewT
  • 4,983
  • 2
  • 40
  • 53
  • 1
    The problem isn't your front-end code, it's the server. The server needs to support pushState. If you don't have control over the server of your clients, why don't you just use the hashchange urls? – Cory Danielson Jun 27 '14 at 17:02
  • 1
    http://readystate4.com/2012/05/17/nginx-and-apache-rewrite-to-support-html5-pushstate/ http://stackoverflow.com/questions/8936695/account-for-backbone-js-pushstate-routes-with-node-js-express-server configuration for every server is different – Cory Danielson Jun 27 '14 at 17:03
  • @CoryDanielson ah yes thanks for the links. I was searching for something about server support for pushState and couldn't find anything. I assumed it would have to be something like this. – DrewT Jun 27 '14 at 17:35
  • The .htaccess for your non-working server is different from the others?, if you use the .htaccess from any of those on this server does that solve the issue? or it persist? – Lu Roman Jul 04 '14 at 16:43
  • @Zagen nope, the .htaccess files are the same between the two server except for an added sub path. See my updated question for details on the differences – DrewT Jul 06 '14 at 00:40
  • You managed to pull it out using hashes, but i got a question, how exactly is not working the pushstate version?, it doesn't update the url, or the handlers associated with the routes won't fire when the route changes? – Lu Roman Jul 07 '14 at 16:11
  • @Zagen - When it's not working backbone does not append chrono view at all. Here's 2 screenshots to show the issue - not working version (pushState left in): http://screencast.com/t/ibMWKrdL0tZ - working version (pushState removed): http://screencast.com/t/4cbaIcfN – DrewT Jul 07 '14 at 16:52
  • On the first screenshot, is supposed to be pushstate but the route is the same as the other one, if you use the url structure proposed in your question ':number' what happens?, does the crhono function gets called? Try placing a debugger inside that method or a console.log to check if it is being called and let me know. – Lu Roman Jul 07 '14 at 19:34

2 Answers2

2

Since the main difference between the working project and the one that doesn't is the folder structure and using another configuration for your httaccess does not solve this issue, try setting a folder root to your router, i had a similar issue a few months ago but i can't recall exactly what i was(but i do remember it was related to using a subfolder inside my domain and backbone), so this is just a suggestion, might or might not help, but in my case it did.

First get the base folder

var baseFolder =  window.location.pathname.replace('/','').split('/')[0];

Then use it as the router root

routes: {
    '(:number)(/plus)': 'chrono'
},

initialize: function() {
    Backbone.history.start({
        pushState: true,
        hashChange: false,
        root: baseFolder                                       
    });
 },

Let me know if it helps =)

Lu Roman
  • 2,220
  • 3
  • 25
  • 40
0

Here's how I managed to fix the issue.

On the problematic server I configured the backbone.js router to accept hash uri changes without using pushState, then I removed pushState conditions.

Here's what the route looks like now:

jQuery(function(){
    // define routes
    var Router = Backbone.Router.extend({
        routes: {
            '': 'chrono',
            ':number/': 'chrono', /* NOTE: ':number/' instead of the previous  ':number' */
            ':number/plus/': 'chrono' /* NOTE: ':number/plus/' instead of the previous ':number/plus' */
    },

        // load views
        initialize: function() {
            Backbone.history.start(); // pushState settings removed
    },

    // ... other router functions
    // reset: etc.

    var Router = new Router;
});

Using this configuration I am now able to handle all of the hash tag anchor routes of my application which I had previously handled using pushState:

  • http://dev.OurDomain.com/OurAplication/chrono
  • http://dev.OurDomain.com/OurAplication/chrono#0 - http://dev.OurDomain.com/OurAplication/chrono#6 (i.e. there are 7 of these numbered routes)
  • http://dev.OurDomain.com/OurAplication/chrono#0/plus - http://dev.OurDomain.com/OurAplication/chrono#6/plus (i.e. each numbered hash route has a modal info pop-up box called by adding /plus to the current number route

I will also mention each of these 14 links has Facebook and Twitter sharing buttons assoicoated to it. This is why we are explicitly writing their hashes to the url bar so we can control how open graph information is getting scraped from our server. Depending on what section of the page the user is sharing/tweeting from, different OG data is served. In other words, it's arbitrarily easy to click and navigate the page sections for #0-6 and /plus but the purpose of writing their clicks directly to the url bar is to register information and thumbnails with Facebook and Twitter's APIs. These query strings are able to register with the Facebook API because of secondary backend PHP architecture which doesn't need to be discussed here :D

A NICE TO HAVE THOUGH:

So now that everything works again!

I still WOULD want to revert to the pushState version we use on our client's live server. (This is so the animations of each chrono#0 (#0-6) don't fire a second time when closing the lightbox overlay opened by the /plus routes.) If anyone can figure out how to do this (I assume it's a .htaccess subfolder RewriteCondition or flag I am missing) I will gladly re-assign / award the accepted answer and associated bounty to you.

EDIT: Was able to re-integrate pushState on the non-working server by adding the root param to Backbone.history.start() as per Zagen's answer.

DrewT
  • 4,983
  • 2
  • 40
  • 53
  • Maybe I'm misunderstanding your answer, but as far as I know, Facebook does not register dynamic changes (JavaScript) in the OG tags. – steveax Jul 06 '14 at 03:32
  • @steveax That is correct, and because this is a backbone.js app normally the Facebook API wouldn't recognize any of the routes such as /chrono or /plus because these routes are coming from the backbone router extend and not from the static index.php at the project root. To get around this we have php files which serve up a model and view in php based on the url scheme sent to the url bar – DrewT Jul 06 '14 at 05:04
  • I seem to have fixed the problem of the chrono#1-6 animations from firing a second time when closing the /plus box. I just changed `':number': 'chrono'` to `':number/': 'chrono'` in my router – DrewT Jul 07 '14 at 18:45