[2nd Screen] live.js delayed if POS screen is idle

I’m glad that SambaPOS is extremely customizable! I’m done setting up with overpriced dumb pole display, and right now I’m setting up an affordable 7" second screen display (half the price of the pole display I bought) with 800*480px resolution (horizontal orientation).

Everything works fine except the live.js. The live.js is delayed everytime when POS screen is idle for less than 2 min (or more) - this kind of delay won’t happen with pole display OR creating new ticket right after ticket closed. I already modified the interval from 1000 to 50 in the live.js. Another thing is, how to avoid the blinking screen when items are being added? Is there any similar alternative to live.js?

Below is the video of my current situation:

By the way, I followed this instruction to setup my second screen (with some modification):

my live.js script:

 /*
  Live.js - One script closer to Designing in the Browser
  Written for Handcraft.com by Martin Kool (@mrtnkl).

  Version 4.
  Recent change: Made stylesheet and mimetype checks case insensitive.

  http://livejs.com
  http://livejs.com/license (MIT)  
  @livejs

  Include live.js#css to monitor css changes only.
  Include live.js#js to monitor js changes only.
  Include live.js#html to monitor html changes only.
  Mix and match to monitor a preferred combination such as live.js#html,css  

  By default, just include live.js to monitor all css, js and html changes.
  
  Live.js can also be loaded as a bookmarklet. It is best to only use it for CSS then,
  as a page reload due to a change in html or css would not re-include the bookmarklet.
  To monitor CSS and be notified that it has loaded, include it as: live.js#css,notify
*/
(function () {

  var headers = { "Etag": 1, "Last-Modified": 1, "Content-Length": 1, "Content-Type": 1 },
      resources = {},
      pendingRequests = {},
      currentLinkElements = {},
      oldLinkElements = {},
      interval = 50,
      loaded = false,
      active = { "html": 1, "css": 1, "js": 1 };

  var Live = {

    // performs a cycle per interval
    heartbeat: function () {      
      if (document.body) {        
        // make sure all resources are loaded on first activation
        if (!loaded) Live.loadresources();
        Live.checkForChanges();
      }
      setTimeout(Live.heartbeat, interval);
    },

    // loads all local css and js resources upon first activation
    loadresources: function () {

      // helper method to assert if a given url is local
      function isLocal(url) {
        var loc = document.location,
            reg = new RegExp("^\\.|^\/(?!\/)|^[\\w]((?!://).)*$|" + loc.protocol + "//" + loc.host);
        return url.match(reg);
      }

      // gather all resources
      var scripts = document.getElementsByTagName("script"),
          links = document.getElementsByTagName("link"),
          uris = [];

      // track local js urls
      for (var i = 0; i < scripts.length; i++) {
        var script = scripts[i], src = script.getAttribute("src");
        if (src && isLocal(src))
          uris.push(src);
        if (src && src.match(/\blive.js#/)) {
          for (var type in active)
            active[type] = src.match("[#,|]" + type) != null
          if (src.match("notify")) 
            alert("Live.js is loaded.");
        }
      }
      if (!active.js) uris = [];
      if (active.html) uris.push(document.location.href);

      // track local css urls
      for (var i = 0; i < links.length && active.css; i++) {
        var link = links[i], rel = link.getAttribute("rel"), href = link.getAttribute("href", 2);
        if (href && rel && rel.match(new RegExp("stylesheet", "i")) && isLocal(href)) {
          uris.push(href);
          currentLinkElements[href] = link;
        }
      }

      // initialize the resources info
      for (var i = 0; i < uris.length; i++) {
        var url = uris[i];
        Live.getHead(url, function (url, info) {
          resources[url] = info;
        });
      }

      // add rule for morphing between old and new css files
      var head = document.getElementsByTagName("head")[0],
          style = document.createElement("style"),
          rule = "transition: all .3s ease-out;"
      css = [".livejs-loading * { ", rule, " -webkit-", rule, "-moz-", rule, "-o-", rule, "}"].join('');
      style.setAttribute("type", "text/css");
      head.appendChild(style);
      style.styleSheet ? style.styleSheet.cssText = css : style.appendChild(document.createTextNode(css));

      // yep
      loaded = true;
    },

    // check all tracking resources for changes
    checkForChanges: function () {
      for (var url in resources) {
        if (pendingRequests[url])
          continue;

        Live.getHead(url, function (url, newInfo) {
          var oldInfo = resources[url],
              hasChanged = false;
          resources[url] = newInfo;
          for (var header in oldInfo) {
            // do verification based on the header type
            var oldValue = oldInfo[header],
                newValue = newInfo[header],
                contentType = newInfo["Content-Type"];
            switch (header.toLowerCase()) {
              case "etag":
                if (!newValue) break;
                // fall through to default
              default:
                hasChanged = oldValue != newValue;
                break;
            }
            // if changed, act
            if (hasChanged) {
              Live.refreshResource(url, contentType);
              break;
            }
          }
        });
      }
    },

    // act upon a changed url of certain content type
    refreshResource: function (url, type) {
      switch (type.toLowerCase()) {
        // css files can be reloaded dynamically by replacing the link element                               
        case "text/css":
          var link = currentLinkElements[url],
              html = document.body.parentNode,
              head = link.parentNode,
              next = link.nextSibling,
              newLink = document.createElement("link");

          html.className = html.className.replace(/\s*livejs\-loading/gi, '') + ' livejs-loading';
          newLink.setAttribute("type", "text/css");
          newLink.setAttribute("rel", "stylesheet");
          newLink.setAttribute("href", url + "?now=" + new Date() * 1);
          next ? head.insertBefore(newLink, next) : head.appendChild(newLink);
          currentLinkElements[url] = newLink;
          oldLinkElements[url] = link;

          // schedule removal of the old link
          Live.removeoldLinkElements();
          break;

        // check if an html resource is our current url, then reload                               
        case "text/html":
          if (url != document.location.href)
            return;

          // local javascript changes cause a reload as well
        case "text/javascript":
        case "application/javascript":
        case "application/x-javascript":
          document.location.reload();
      }
    },

    // removes the old stylesheet rules only once the new one has finished loading
    removeoldLinkElements: function () {
      var pending = 0;
      for (var url in oldLinkElements) {
        // if this sheet has any cssRules, delete the old link
        try {
          var link = currentLinkElements[url],
              oldLink = oldLinkElements[url],
              html = document.body.parentNode,
              sheet = link.sheet || link.styleSheet,
              rules = sheet.rules || sheet.cssRules;
          if (rules.length >= 0) {
            oldLink.parentNode.removeChild(oldLink);
            delete oldLinkElements[url];
            setTimeout(function () {
              html.className = html.className.replace(/\s*livejs\-loading/gi, '');
            }, 100);
          }
        } catch (e) {
          pending++;
        }
        if (pending) setTimeout(Live.removeoldLinkElements, 50);
      }
    },

    // performs a HEAD request and passes the header info to the given callback
    getHead: function (url, callback) {
      pendingRequests[url] = true;
      var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XmlHttp");
      xhr.open("HEAD", url, true);
      xhr.onreadystatechange = function () {
        delete pendingRequests[url];
        if (xhr.readyState == 4 && xhr.status != 304) {
          xhr.getAllResponseHeaders();
          var info = {};
          for (var h in headers) {
            var value = xhr.getResponseHeader(h);
            // adjust the simple Etag variant to match on its significant part
            if (h.toLowerCase() == "etag" && value) value = value.replace(/^W\//, '');
            if (h.toLowerCase() == "content-type" && value) value = value.replace(/^(.*?);.*?$/i, "$1");
            info[h] = value;
          }
          callback(url, info);
        }
      }
      xhr.send();
    }
  };

  // start listening
  if (document.location.protocol != "file:") {
    if (!window.liveJsLoaded)
      Live.heartbeat();

    window.liveJsLoaded = true;
  }
  else if (window.console)
    console.log("Live.js doesn't support the file protocol. It needs http.");    
})();
1 Like

As it is in V4 I dont thing you will be able to not have the blinking.
You might be able to change and use frames and only have ticket side blink but as its a page refresh it will do that.

V5 has an alternative using the upcoming API and from what I have seen it is a dynamic page so just adds orders without refreshing page.

2 Likes

Not the question, but that customer display looks nice, care sharing the css/html for it

Actually, I’m fine with the blinking screen when item is added to ticket. I’m more concern about the live.js delay, is there anyway to work out the live.js to avoid the delay (without having to manually refresh the browser) when item is added to new ticket after the POS screen is idle for a while (about a minute or two)?

But anyway, it will be nice to read a V5 tutorial for setting up second screen using the upcoming API.

@the133448 thanks for the compliment, sure I will share the css/html for my second screen setup. Lookup for the attached file (SECONDSCREEN.zip) in this post, all setup is using this tutorial Customer Display with Advertisement. Use the printer template in the attached file, instead from the given tutorial to setup the actions and rules.

Anyway, you can customize the typeface by using cufon.js.

SECONDSCREEN.zip (143.9 KB)

1 Like

Which Browser are you using?

The thing about live.js is that the code runs solely in the Browser client, and is set to “poll the server” to detect changes in the HTML/JS/CSS content. So what is probably happening is the polling interval is being deactivated for some reason (ie. the Browser is going into some kind of “sleep” mode), or the Browser is somehow being stubborn about caching so the code is being “tricked” into believing nothing has been changed/updated.

Do you experience the same results in different Browsers?

P.S. the Customer Display using GQL (V5) works fairly flawlessly, perhaps since it does not “poll” for changes; rather, it is “notified” that something has happened in SambaPOS, and the notification contains updated Ticket data… at least, that is the way I implemented it.

2 Likes

I’m using Google Chrome, never come to my mind that this issue is related to the browser, not the live.js. You’re right, Google Chrome is caching the page, so the live.js won’t be working as it’s supposed to be. I can tell from the status bar when the page is being reloaded that shows “Loading cache” message.

Thanks for your advise by using different browser. I just tried using Safari browser, the live.js working with no delay when the POS screen is idle for a while. Another problem is coming up, I could not start Safari on full screen by command line switch -fullscreen (which useful on system startup). My .bat file as follows:

@echo off
start /MAX "Safari" safari --new-window --start -fullscreen
"C:\Program Files (x86)\IIS Express\iisexpress.exe" /path:C:\IIS\ /port:9980

GQL sounds good to me, but I never use any of GQL function before. Have anyone done a tutorial for Second Screen Customer Display using GQL in V5?

EDIT

I decided to use Google Chrome back again in order to gain the full screen on startup, but with different .bat settings:

@echo off
start /MAX "Chrome" chrome --new-window --start-fullscreen --disk-cache-size=1 --media-cache-size=1
"C:\Program Files (x86)\IIS Express\iisexpress.exe" /path:C:\IIS\ /port:9980

The addition of --disk-cache-size=1 --media-cache-size=1 will minimize the cache size, so the page will not be enough to cache. Therefore the live.js will not be delayed on the second screen.

2 Likes

Is there no option relating to cache in crome?
Maybe firefox as another option?

Im having an issue with order tags. i added a rule to excute print action on order taging.
What happens, on HTML file, all other order lines disappear, execpt for the tagged order along with its tags.
Adding a new item (order line) refreshes the content and displays everything. so in short Print action on Order Tagged rule, seems to only print the order associated with current tag.

Any one came acreoss this? Thanks

This type of screen is really pointless now. With v5 we have much better methods and much easier methods to create a customer screen. I suggest you look into them.

1 Like

@Jesse you are right, but this has been setup and woring for a community center, except for that tag glitch.
and i would like to know wether im doing something wrong or this is how it is meant to be.