<commit_before>
// ------------------------------------------
// Rellax.js - v1.0.0
// Buttery smooth parallax library
// Copyright (c) 2016 Moe Amaya (@moeamaya)
// MIT license
//
// Thanks to Paraxify.js and Jaime Cabllero
// for parallax concepts
// ------------------------------------------

(function (root, factory) {
  if (typeof define === 'function' && define.amd) {
    // AMD. Register as an anonymous module.
    define([], factory);
  } else if (typeof module === 'object' && module.exports) {
    // Node. Does not work with strict CommonJS, but
    // only CommonJS-like environments that support module.exports,
    // like Node.
    module.exports = factory();
  } else {
    // Browser globals (root is window)
    root.Rellax = factory();
  }
}(this, function () {
  var Rellax = function(el, options){
    "use strict";

    var self = Object.create(Rellax.prototype);

    var posY = 0; // set it to -1 so the animate function gets called at least once
    var screenY = 0;
    var blocks = [];
    var pause = false;

    // check what requestAnimationFrame to use, and if
    // it's not supported, use the onscroll event
    var loop = window.requestAnimationFrame ||
      window.webkitRequestAnimationFrame ||
      window.mozRequestAnimationFrame ||
      window.msRequestAnimationFrame ||
      window.oRequestAnimationFrame ||
      function(callback){ setTimeout(callback, 1000 / 60); };

    // Default Settings
    self.options = {
      speed: -2,
      center: false
    };

    // User defined options (might have more in the future)
    if (options){
      Object.keys(options).forEach(function(key){
        self.options[key] = options[key];
      });
    }

    // If some clown tries to crank speed, limit them to +-10
    if (self.options.speed < -10) {
      self.options.speed = -10;
    } else if (self.options.speed > 10) {
      self.options.speed = 10;
    }

    // By default, rellax class
    if (!el) {
      el = '.rellax';
    }

    // Classes
    if (document.getElementsByClassName(el.replace('.',''))){
      self.elems = document.getElementsByClassName(el.replace('.',''));
    }

    // Now query selector
    else if (document.querySelector(el) !== false) {
      self.elems = querySelector(el);
    }

    // The elements don't exist
    else {
      throw new Error("The elements you're trying to select don't exist.");
    }

    // Let's kick this script off
    // Build array for cached element values
    // Bind scroll and resize to animate method
    var init = function() {
      screenY = window.innerHeight;
      setPosition();

      // Get and cache initial position of all elements
      for (var i = 0; i < self.elems.length; i++){
        var block = createBlock(self.elems[i]);
        blocks.push(block);
      }

      window.addEventListener('resize', function(){
        animate();
      });

      // Start the loop
      update();

      // The loop does nothing if the scrollPosition did not change
      // so call animate to make sure every element has their transforms
      animate();
    };

    // We want to cache the parallax blocks'
    // values: base, top, height, speed
    // el: is dom object, return: el cache values
    var createBlock = function(el) {
      var dataPercenage = el.getAttribute('data-rellax-percentage');
      var dataSpeed = el.getAttribute('data-rellax-speed');

      // initializing at scrollY = 0 (top of browser)
      // ensures elements are positioned based on HTML layout.
      //
      // If the element has the percentage attribute, the posY needs to be
      // the current scroll position's value, so that the elements are still positioned based on HTML layout
      var posY = dataPercenage || self.options.center ? document.body.scrollTop : 0;

      var blockTop = posY + el.getBoundingClientRect().top;
      var blockHeight = el.clientHeight || el.offsetHeight || el.scrollHeight;

      // apparently parallax equation everyone uses
      var percentage = dataPercenage ? dataPercenage : (posY - blockTop + screenY) / (blockHeight + screenY);
      if(self.options.center){ percentage = 0.5; }

      // Optional individual block speed as data attr, otherwise global speed
      // Check if has percentage attr, and limit speed to 5, else limit it to 10
      var speed = dataSpeed ? limitSpeed(dataSpeed, 10) : self.options.speed;
      if (dataPercenage || self.options.center) {
        speed = dataSpeed ? limitSpeed(dataSpeed, 5) : limitSpeed(self.options.speed, 5);
      }

      var base = updatePosition(percentage, speed);

      // ~~Store non-translate3d transforms~~
      // Store inline styles and extract transforms
      var style = el.style.cssText;
      var transform = '';

      // Check if there's an inline styled transform
      if (style.indexOf('transform') >= 0) {
        // Get the index of the transform
        var index = style.indexOf('transform');

        // Trim the style to the transform point and get the following semi-colon index
        var trimmedStyle = style.slice(index);
        var delimiter = trimmedStyle.indexOf(';');

        // Remove "transform" string and save the attribute
        if (delimiter) {
          transform = " " + trimmedStyle.slice(11, delimiter).replace(/\s/g,'');
        } else {
          transform = " " + trimmedStyle.slice(11).replace(/\s/g,'');
        }
      }

      return {
        base: base,
        top: blockTop,
        height: blockHeight,
        speed: speed,
        style: style,
        transform: transform
      };
    };

    // Check if current speed is < or > than max/-max
    // If so, return max
    var limitSpeed = function(current, max) {
      if (current < -max) {
        return -max;
      } else if (current > max) {
        return max;
      } else {
        return current;
      }
    };

    // set scroll position (posY)
    // side effect method is not ideal, but okay for now
    // returns true if the scroll changed, false if nothing happened
    var setPosition = function() {
      var oldY = posY;

      if (window.pageYOffset !== undefined) {
        posY = window.pageYOffset;
      } else {
        posY = (document.documentElement || document.body.parentNode || document.body).scrollTop;
      }

      if (oldY != posY) {
        // scroll changed, return true
        return true;
      }

      // scroll did not change
      return false;
    };

    // Ahh a pure function, gets new transform value
    // based on scrollPostion and speed
    var updatePosition = function(percentage, speed) {
      var value = (speed * (100 * (1 - percentage)));
      return Math.round(value);
    };

    //
    var update = function() {
      if (setPosition() && pause === false) {
        animate();
      }

      // loop again
      loop(update);
    };

    // Transform3d on parallax element
    var animate = function() {
      for (var i = 0; i < self.elems.length; i++){
        var percentage = ((posY - blocks[i].top + screenY) / (blocks[i].height + screenY));

        // Subtracting initialize value, so element stays in same spot as HTML
        var position = updatePosition(percentage, blocks[i].speed) - blocks[i].base;

        // Move that element
        // (Prepare the new transform and append initial inline transforms. Set the new, and preppend previous inline styles)
        var translate = ' translate3d(0,' + position + 'px' + ',0)' + blocks[i].transform;
        self.elems[i].style.cssText = blocks[i].style+'-webkit-transform:'+translate+';-moz-transform:'+translate+';transform:'+translate+';';
      }
    };

    self.destroy = function() {
      for (var i = 0; i < self.elems.length; i++){
        self.elems[i].style.cssText = blocks[i].style;
      }
      pause = true;
    };

    init();
    return self;
  };
  return Rellax;
}));
<commit_msg>using number clamp<commit_after>
// ------------------------------------------
// Rellax.js - v1.0.0
// Buttery smooth parallax library
// Copyright (c) 2016 Moe Amaya (@moeamaya)
// MIT license
//
// Thanks to Paraxify.js and Jaime Cabllero
// for parallax concepts
// ------------------------------------------

(function (root, factory) {
  if (typeof define === 'function' && define.amd) {
    // AMD. Register as an anonymous module.
    define([], factory);
  } else if (typeof module === 'object' && module.exports) {
    // Node. Does not work with strict CommonJS, but
    // only CommonJS-like environments that support module.exports,
    // like Node.
    module.exports = factory();
  } else {
    // Browser globals (root is window)
    root.Rellax = factory();
  }
}(this, function () {
  var Rellax = function(el, options){
    "use strict";

    var self = Object.create(Rellax.prototype);

    var posY = 0; // set it to -1 so the animate function gets called at least once
    var screenY = 0;
    var blocks = [];
    var pause = false;

    // check what requestAnimationFrame to use, and if
    // it's not supported, use the onscroll event
    var loop = window.requestAnimationFrame ||
      window.webkitRequestAnimationFrame ||
      window.mozRequestAnimationFrame ||
      window.msRequestAnimationFrame ||
      window.oRequestAnimationFrame ||
      function(callback){ setTimeout(callback, 1000 / 60); };

    // limit the given number in the range [min, max]
    var clamp = function(num, min, max) {
      return (num <= min) ? min : ((num >= max) ? max : num);
    };

    // Default Settings
    self.options = {
      speed: -2,
      center: false
    };

    // User defined options (might have more in the future)
    if (options){
      Object.keys(options).forEach(function(key){
        self.options[key] = options[key];
      });
    }

    // If some clown tries to crank speed, limit them to +-10
    self.options.speed = clamp(self.options.speed, -10, 10);

    // By default, rellax class
    if (!el) {
      el = '.rellax';
    }

    // Classes
    if (document.getElementsByClassName(el.replace('.',''))){
      self.elems = document.getElementsByClassName(el.replace('.',''));
    }

    // Now query selector
    else if (document.querySelector(el) !== false) {
      self.elems = querySelector(el);
    }

    // The elements don't exist
    else {
      throw new Error("The elements you're trying to select don't exist.");
    }

    // Let's kick this script off
    // Build array for cached element values
    // Bind scroll and resize to animate method
    var init = function() {
      screenY = window.innerHeight;
      setPosition();

      // Get and cache initial position of all elements
      for (var i = 0; i < self.elems.length; i++){
        var block = createBlock(self.elems[i]);
        blocks.push(block);
      }

      window.addEventListener('resize', function(){
        animate();
      });

      // Start the loop
      update();

      // The loop does nothing if the scrollPosition did not change
      // so call animate to make sure every element has their transforms
      animate();
    };

    // We want to cache the parallax blocks'
    // values: base, top, height, speed
    // el: is dom object, return: el cache values
    var createBlock = function(el) {
      var dataPercenage = el.getAttribute('data-rellax-percentage');
      var dataSpeed = el.getAttribute('data-rellax-speed');

      // initializing at scrollY = 0 (top of browser)
      // ensures elements are positioned based on HTML layout.
      //
      // If the element has the percentage attribute, the posY needs to be
      // the current scroll position's value, so that the elements are still positioned based on HTML layout
      var posY = dataPercenage || self.options.center ? document.body.scrollTop : 0;

      var blockTop = posY + el.getBoundingClientRect().top;
      var blockHeight = el.clientHeight || el.offsetHeight || el.scrollHeight;

      // apparently parallax equation everyone uses
      var percentage = dataPercenage ? dataPercenage : (posY - blockTop + screenY) / (blockHeight + screenY);
      if(self.options.center){ percentage = 0.5; }

      // Optional individual block speed as data attr, otherwise global speed
      // Check if has percentage attr, and limit speed to 5, else limit it to 10
      var speed = dataSpeed ? clamp(dataSpeed, -10, 10) : self.options.speed;
      if (dataPercenage || self.options.center) {
        speed = clamp(dataSpeed || self.options.speed, -5, 5);
      }

      var base = updatePosition(percentage, speed);

      // ~~Store non-translate3d transforms~~
      // Store inline styles and extract transforms
      var style = el.style.cssText;
      var transform = '';

      // Check if there's an inline styled transform
      if (style.indexOf('transform') >= 0) {
        // Get the index of the transform
        var index = style.indexOf('transform');

        // Trim the style to the transform point and get the following semi-colon index
        var trimmedStyle = style.slice(index);
        var delimiter = trimmedStyle.indexOf(';');

        // Remove "transform" string and save the attribute
        if (delimiter) {
          transform = " " + trimmedStyle.slice(11, delimiter).replace(/\s/g,'');
        } else {
          transform = " " + trimmedStyle.slice(11).replace(/\s/g,'');
        }
      }

      return {
        base: base,
        top: blockTop,
        height: blockHeight,
        speed: speed,
        style: style,
        transform: transform
      };
    };

    // set scroll position (posY)
    // side effect method is not ideal, but okay for now
    // returns true if the scroll changed, false if nothing happened
    var setPosition = function() {
      var oldY = posY;

      if (window.pageYOffset !== undefined) {
        posY = window.pageYOffset;
      } else {
        posY = (document.documentElement || document.body.parentNode || document.body).scrollTop;
      }

      if (oldY != posY) {
        // scroll changed, return true
        return true;
      }

      // scroll did not change
      return false;
    };

    // Ahh a pure function, gets new transform value
    // based on scrollPostion and speed
    var updatePosition = function(percentage, speed) {
      var value = (speed * (100 * (1 - percentage)));
      return Math.round(value);
    };

    //
    var update = function() {
      if (setPosition() && pause === false) {
        animate();
      }

      // loop again
      loop(update);
    };

    // Transform3d on parallax element
    var animate = function() {
      for (var i = 0; i < self.elems.length; i++){
        var percentage = ((posY - blocks[i].top + screenY) / (blocks[i].height + screenY));

        // Subtracting initialize value, so element stays in same spot as HTML
        var position = updatePosition(percentage, blocks[i].speed) - blocks[i].base;

        // Move that element
        // (Prepare the new transform and append initial inline transforms. Set the new, and preppend previous inline styles)
        var translate = ' translate3d(0,' + position + 'px' + ',0)' + blocks[i].transform;
        self.elems[i].style.cssText = blocks[i].style+'-webkit-transform:'+translate+';-moz-transform:'+translate+';transform:'+translate+';';
      }
    };

    self.destroy = function() {
      for (var i = 0; i < self.elems.length; i++){
        self.elems[i].style.cssText = blocks[i].style;
      }
      pause = true;
    };

    init();
    return self;
  };
  return Rellax;
}));
<|endoftext|>


<commit_before>var initialized = false;
var interval;
var lat1 = 0;
var lon1 = 0;
var lat2;
var lon2;
var speed = 0;
var accuracy = 0;
var phoneHead = 0;
var prevHead = 0;
var prevDist = 0;
var head = 0;
var dist = 0;
var token = '-';
var units = "metric";
var sens = 5;
var R = 6371000; // m
var locationWatcher;
var locationInterval;
var locationOptions = {timeout: 15000, maximumAge: 1000, enableHighAccuracy: true };
var serverAddress = 'http://getback.pebbletime.io/';
// var geocoder = 'http://nominatim.openstreetmap.org/reverse?format=json&zoom=18';
// var geocoder = 'http://api.geonames.org/findNearestAddressJSON?formatted=false&style=full';
var geocoder = 'http://services.gisgraphy.com/street/search?format=json&from=0&to=1';

Pebble.addEventListener("ready", function(e) {
  lat2 = parseFloat(localStorage.getItem("lat2")) || null;
  lon2 = parseFloat(localStorage.getItem("lon2")) || null;
  interval = parseInt(localStorage.getItem("interval")) || 0;
  units = localStorage.getItem("units") || "metric";
  sens = parseInt(localStorage.getItem("sens")) || 5;
  token = localStorage.getItem("token") || token;
  if ((lat2 === null) || (lon2 === null)) {
    storeCurrentPosition();
  }
  else {
    // console.log("Target location known:" + lon2 + ',' + lat2);
  }
  startWatcher();
  if (Pebble.getTimelineToken) {
    Pebble.getTimelineToken(
      function (timelineToken) {
        token = timelineToken;
        console.log('Got timeline token ' + token);
        localStorage.setItem("token", token);
        // sending id from phone to watch is just a signal that
        // the phone is ready to accept app messages (e.g. id)
        var msg = {"id": -1};
        sendMessage(msg);
      },
      function (error) {
        console.log('Error getting timeline token: ' + error);
      }
    );
  }
  console.log("JavaScript app ready and running!");
  initialized = true;
});

Pebble.addEventListener("appmessage",
  function(e) {
    if (e && e.payload && e.payload.cmd) {
      console.log("Got command: " + e.payload.cmd);
      switch (e.payload.cmd) {
        case 'set':
          if (!e.payload.id || (e.payload.id < 0)) {
            storeCurrentPosition();
          }
          else {
            getPositionFromServer(e.payload.id);
          }
          break;
        case 'clear':
          lat2 = null;
          lon2 = null;
          break;
        case 'quit':
          navigator.geolocation.clearWatch(locationWatcher);
          break;
        default:
          console.log("Unknown command!");
      }
    }
  }
);

Pebble.addEventListener("showConfiguration",
  function() {
    var conf = {
      'units': units,
      'interval': interval,
      'sens': sens};
    if (token != '-') {
      conf.token = token;
    }
    var uri = serverAddress + token + '/configure?conf=' +
      encodeURIComponent(JSON.stringify(conf));
    console.log("Configuration url: " + uri);
    Pebble.openURL(uri);
  }
);

Pebble.addEventListener("webviewclosed",
  function(e) {
    // console.log(e.response);
    var options = JSON.parse(decodeURIComponent(e.response));
    console.log("Webview window returned: " + JSON.stringify(options));
    units = (options.units == 'imperial') ? 'imperial' : 'metric';
    localStorage.setItem("units", units);
    console.log("Units set to: " + units);
    interval = parseInt(options.interval) || 0;
    localStorage.setItem("interval", interval);
    console.log("Interval set to: " + interval);
    sens = parseInt(options.sens) || 5;
    localStorage.setItem("sens", sens);
    console.log("Sentitivity set to: " + sens);
    var msg = {"units": units,
               "sens": parseInt(sens)};
    sendMessage(msg);
    calculate();
    startWatcher();
  }
);

function sendMessage(dict) {
  Pebble.sendAppMessage(dict, appMessageAck, appMessageNack);
  console.log("Sent message to Pebble! " + JSON.stringify(dict));
}

function appMessageAck(e) {
  console.log("Message accepted by Pebble!");
}

function appMessageNack(e) {
  console.log("Message rejected by Pebble! ");
}

function locationSuccess(position) {
  // console.log("Got location " +
  // position.coords.latitude + ',' +
  // position.coords.longitude + ', heading at ' +
  // position.coords.heading);
  lat1 = position.coords.latitude;
  lon1 = position.coords.longitude;
  speed = position.coords.speed;
  accuracy = position.coords.accuracy;
  phoneHead = position.coords.heading;
  calculate();
}

function addLocation(position) {
  storeLocation(position);
  if (token && (token != '-')) {
    // get address
    var addr = position.coords.latitude + ',' + position.coords.longitude;
    var url = geocoder + '&username=' + token + '&lat=' +
      position.coords.latitude + '&lng=' +
      position.coords.longitude;
    var rgc = new XMLHttpRequest(); // xhr for reverse geocoding, only one instance!
    rgc.open("get", url, true);
    rgc.setRequestHeader('User-Agent', 'Get Back in Time/1.7');
    // rgc.setRequestHeader('X-Forwarded-For', token);
    rgc.onerror = rgc.ontimeout = function(e) {
      console.log("Reverse geocoding error: " + this.statusText);
    }
    rgc.onload = function(res) {
      var json = this.responseText;
      console.log("Got: " + json + " from reverse geocoder");
      var latlon = Math.round(position.coords.latitude*1000)/1000 + ',' +
                   Math.round(position.coords.longitude*1000)/1000;
      var obj = {
        "position": position,
        "pin": {
          "time": new Date().toISOString(),
          "layout": {
            "type": "genericPin",
            "tinyIcon": "system://images/NOTIFICATION_FLAG_TINY",
            "title": latlon,
            "subtitle": "Get Back in Time",
            "body": "Set from watch."
          },
        }
      }
      var res = JSON.parse(json);
      if (res && res.result && res.result[0]) {
        var placeName = res.result[0].name || '';
        if (!placeName && res.result[0].isIn) {
          placeName = res.result[0].isIn;
        }
        console.log("Reverse geocoded address: " + placeName);
        obj.pin.layout.title = placeName;
      };
      // add place to server
      var url = serverAddress + token + '/place/new';
      var req = new XMLHttpRequest();
      req.onload = function(res) {
        var json = this.responseText;
        console.log('Got ' + json);
        if (!json || (json.substr(0, 1) != '{')) {
          console.log("Error sending place to server: " + json);
          return;
        }
        var obj = JSON.parse(json);
        console.log("Sent place to server: " + obj._id);
      };
      req.open("post", url, true);
      req.setRequestHeader('Content-Type', 'application/json');
      req.send(JSON.stringify(obj));
    };
    rgc.send(null);
    console.log("Trying to reverse geocode: " + url);
  }
}

function storeLocation(position) {
  lat2 = position.coords.latitude;
  lon2 = position.coords.longitude;
  localStorage.setItem("lat2", lat2);
  localStorage.setItem("lon2", lon2);
  // console.log("Stored location " +
  // position.coords.latitude + ',' +
  // position.coords.longitude);
  calculate();
}

function calculate() {
  var msg;
  if (lat2 || lon2) {
    if (!lat2) {
      lat2 = 0;
    }
    if (!lon2) {
      lon2 = 0;
    }
    var dLat = toRad(lat2-lat1);
    var dLon = toRad(lon2-lon1);
    var l1 = toRad(lat1);
    var l2 = toRad(lat2);
    var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
            Math.sin(dLon/2) * Math.sin(dLon/2) * Math.cos(l1) * Math.cos(l2);
    var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
    dist = Math.round(R * c);
    var y = Math.sin(dLon) * Math.cos(l2);
    var x = Math.cos(l1)*Math.sin(l2) -
            Math.sin(l1)*Math.cos(l2)*Math.cos(dLon);
    head = toDeg(Math.round(Math.atan2(y, x)));
    if (head < 360) {
      head = (head % 360) + 360;
    }
    if ((dist != prevDist) || (head != prevHead)) {
      msg = {"dist": dist,
             "head": head,
             "accuracy": accuracy,
             "phonehead": -1,
             "speed": 0,
             "units": units,
             "sens": parseInt(sens)};
      if ((speed > 0) && (!isNan(phoneHead))) {
        // enough movement to calculate speed and heading
        msg["phonehead"] = phoneHead;
        msg["speed"] = speed;
      }
      sendMessage(msg);
      prevDist = dist;
      prevHead = head;
    }
  }
}

function locationError(error) {
  console.warn('location error (' + error.code + '): ' + error.message);
}

function storeCurrentPosition() {
  // console.log("Attempting to store current position.");
  var msg = {"dist": 0,
             "head": 0};
  sendMessage(msg);
  navigator.geolocation.getCurrentPosition(addLocation, locationError, locationOptions);
}

function getPositionFromServer(id) {
  var url = serverAddress + token + '/place/' + id;
  var req = new XMLHttpRequest();
  req.onload = parseServerResponse;
  req.open("get", url, true);
  req.send();
}

function parseServerResponse() {
  var res = this.responseText;
  if (!res) {
    console.warn('No response from server');
    return false;
  }
  if ((this.status != 200) || (this.responseText.substr(0,1) != '{')) {
    console.warn('Server did not return a JSON object: ' + this.responseText);
    return false;
  }
  var obj = JSON.parse(res);
  if (!obj || !obj.position) {
    console.warn('No position from server: ' + this.responseText);
    return false;
  }
  console.log('Got position from server: ' +
    obj.position.coords.latitude + ', ' +
    obj.position.coords.longitude);
  storeLocation(obj.position);
}

function startWatcher() {
  if (locationInterval) {
    clearInterval(locationInterval);
  }
  if (locationWatcher) {
    navigator.geolocation.clearWatch(locationWatcher);
  }
  if (interval > 0) {
    console.log('Interval is ' + interval + ', using getCurrentPosition and setInterval');
    navigator.geolocation.getCurrentPosition(locationSuccess, locationError, locationOptions);
    locationInterval = setInterval(function() {
      navigator.geolocation.getCurrentPosition(locationSuccess, locationError, locationOptions);
    }, interval * 1000);
  }
  else {
    console.log('Interval not set, using watchPosition');
    navigator.geolocation.getCurrentPosition(locationSuccess, locationError, locationOptions);
    locationWatcher = navigator.geolocation.watchPosition(locationSuccess, locationError, locationOptions);
    console.log("Started location watcher: " + locationWatcher);
  }
  // for testing: randomize movement!
  /*
  window.navigator.geolocation.getCurrentPosition(locationSuccess, locationError, locationOptions);
  if (!interval) {
    interval = 10;
  }
  locationWatcher = setInterval(function() {
    lat1 = lat1 + Math.random()/100 * ((Math.random() > 0.5) || -1);
    lon1 = lon1 - Math.random()/100 * ((Math.random() > 0.5) || -1);
    calculate();
  }, interval * 1000);
  console.log("Started fake location watcher: " + locationWatcher);
  */
}
function toRad(num) {
  return num * Math.PI / 180;
}
function toDeg(num) {
  return num * 180 / Math.PI;
}
<commit_msg>fixed a very strange behaviour on position object<commit_after>var initialized = false;
var interval;
var lat1 = 0;
var lon1 = 0;
var lat2;
var lon2;
var speed = 0;
var accuracy = 0;
var phoneHead = 0;
var prevHead = 0;
var prevDist = 0;
var head = 0;
var dist = 0;
var token = '-';
var units = "metric";
var sens = 5;
var R = 6371000; // m
var locationWatcher;
var locationInterval;
var locationOptions = {timeout: 15000, maximumAge: 1000, enableHighAccuracy: true };
var serverAddress = 'http://getback.pebbletime.io/';
// var geocoder = 'http://nominatim.openstreetmap.org/reverse?format=json&zoom=18';
// var geocoder = 'http://api.geonames.org/findNearestAddressJSON?formatted=false&style=full';
var geocoder = 'http://services.gisgraphy.com/street/search?format=json&from=0&to=1';

Pebble.addEventListener("ready", function(e) {
  lat2 = parseFloat(localStorage.getItem("lat2")) || null;
  lon2 = parseFloat(localStorage.getItem("lon2")) || null;
  interval = parseInt(localStorage.getItem("interval")) || 0;
  units = localStorage.getItem("units") || "metric";
  sens = parseInt(localStorage.getItem("sens")) || 5;
  token = localStorage.getItem("token") || token;
  if ((lat2 === null) || (lon2 === null)) {
    storeCurrentPosition();
  }
  else {
    // console.log("Target location known:" + lon2 + ',' + lat2);
  }
  startWatcher();
  if (Pebble.getTimelineToken) {
    Pebble.getTimelineToken(
      function (timelineToken) {
        token = timelineToken;
        console.log('Got timeline token ' + token);
        localStorage.setItem("token", token);
        // sending id from phone to watch is just a signal that
        // the phone is ready to accept app messages (e.g. id)
        var msg = {"id": -1};
        sendMessage(msg);
      },
      function (error) {
        console.log('Error getting timeline token: ' + error);
      }
    );
  }
  console.log("JavaScript app ready and running!");
  initialized = true;
});

Pebble.addEventListener("appmessage",
  function(e) {
    if (e && e.payload && e.payload.cmd) {
      console.log("Got command: " + e.payload.cmd);
      switch (e.payload.cmd) {
        case 'set':
          if (!e.payload.id || (e.payload.id < 0)) {
            storeCurrentPosition();
          }
          else {
            getPositionFromServer(e.payload.id);
          }
          break;
        case 'clear':
          lat2 = null;
          lon2 = null;
          break;
        case 'quit':
          navigator.geolocation.clearWatch(locationWatcher);
          break;
        default:
          console.log("Unknown command!");
      }
    }
  }
);

Pebble.addEventListener("showConfiguration",
  function() {
    var conf = {
      'units': units,
      'interval': interval,
      'sens': sens};
    if (token != '-') {
      conf.token = token;
    }
    var uri = serverAddress + token + '/configure?conf=' +
      encodeURIComponent(JSON.stringify(conf));
    console.log("Configuration url: " + uri);
    Pebble.openURL(uri);
  }
);

Pebble.addEventListener("webviewclosed",
  function(e) {
    // console.log(e.response);
    var options = JSON.parse(decodeURIComponent(e.response));
    console.log("Webview window returned: " + JSON.stringify(options));
    units = (options.units == 'imperial') ? 'imperial' : 'metric';
    localStorage.setItem("units", units);
    console.log("Units set to: " + units);
    interval = parseInt(options.interval) || 0;
    localStorage.setItem("interval", interval);
    console.log("Interval set to: " + interval);
    sens = parseInt(options.sens) || 5;
    localStorage.setItem("sens", sens);
    console.log("Sentitivity set to: " + sens);
    var msg = {"units": units,
               "sens": parseInt(sens)};
    sendMessage(msg);
    calculate();
    startWatcher();
  }
);

function sendMessage(dict) {
  Pebble.sendAppMessage(dict, appMessageAck, appMessageNack);
  console.log("Sent message to Pebble! " + JSON.stringify(dict));
}

function appMessageAck(e) {
  console.log("Message accepted by Pebble!");
}

function appMessageNack(e) {
  console.log("Message rejected by Pebble! ");
}

function locationSuccess(position) {
  // console.log("Got location " +
  // position.coords.latitude + ',' +
  // position.coords.longitude + ', heading at ' +
  // position.coords.heading);
  lat1 = position.coords.latitude;
  lon1 = position.coords.longitude;
  speed = position.coords.speed;
  accuracy = position.coords.accuracy;
  phoneHead = position.coords.heading;
  calculate();
}

function addLocation(position) {
  var pos = position;
  if (JSON.stringify(pos) == "{}") {
    // WTF: JSON.stringify(position) returns {}
    // but position.coords.latitude and position.coords.longitude exist
    pos = {
      'coords': {
        'latitude': position.coords.latitude,
        'longitude': position.coords.longitude
      }
    };
  }
  storeLocation(position);
  if (token && (token != '-')) {
    // get address
    var addr = position.coords.latitude + ',' + position.coords.longitude;
    var url = geocoder + '&username=' + token + '&lat=' +
      position.coords.latitude + '&lng=' +
      position.coords.longitude;
    var rgc = new XMLHttpRequest(); // xhr for reverse geocoding, only one instance!
    rgc.open("get", url, true);
    rgc.setRequestHeader('User-Agent', 'Get Back in Time/1.7');
    // rgc.setRequestHeader('X-Forwarded-For', token);
    rgc.onerror = rgc.ontimeout = function(e) {
      console.log("Reverse geocoding error: " + this.statusText);
    }
    rgc.onload = function(res) {
      var json = this.responseText;
      console.log("Got: " + json + " from reverse geocoder");
      var latlon = Math.round(position.coords.latitude*1000)/1000 + ',' +
                   Math.round(position.coords.longitude*1000)/1000;
      var obj = {
        "position": pos,
        "pin": {
          "time": new Date().toISOString(),
          "layout": {
            "type": "genericPin",
            "tinyIcon": "system://images/NOTIFICATION_FLAG_TINY",
            "title": latlon,
            "subtitle": "Get Back in Time",
            "body": "Set from watch."
          },
        }
      }
      var res = JSON.parse(json);
      if (res && res.result && res.result[0]) {
        var placeName = res.result[0].name || '';
        if (!placeName && res.result[0].isIn) {
          placeName = res.result[0].isIn;
        }
        console.log("Reverse geocoded address: " + placeName);
        obj.pin.layout.title = placeName;
      };
      // add place to server
      var url = serverAddress + token + '/place/new';
      var req = new XMLHttpRequest();
      req.onload = function(res) {
        var json = this.responseText;
        console.log('Got ' + json);
        if (!json || (json.substr(0, 1) != '{')) {
          console.log("Error sending place to server: " + json);
          return;
        }
        var obj = JSON.parse(json);
        console.log("Sent place to server: " + obj._id);
      };
      req.open("post", url, true);
      req.setRequestHeader('Content-Type', 'application/json');
      req.send(JSON.stringify(obj));
    };
    rgc.send(null);
    console.log("Trying to reverse geocode: " + url);
  }
}

function storeLocation(position) {
  lat2 = position.coords.latitude;
  lon2 = position.coords.longitude;
  localStorage.setItem("lat2", lat2);
  localStorage.setItem("lon2", lon2);
  // console.log("Stored location " +
  // position.coords.latitude + ',' +
  // position.coords.longitude);
  calculate();
}

function calculate() {
  var msg;
  if (lat2 || lon2) {
    if (!lat2) {
      lat2 = 0;
    }
    if (!lon2) {
      lon2 = 0;
    }
    var dLat = toRad(lat2-lat1);
    var dLon = toRad(lon2-lon1);
    var l1 = toRad(lat1);
    var l2 = toRad(lat2);
    var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
            Math.sin(dLon/2) * Math.sin(dLon/2) * Math.cos(l1) * Math.cos(l2);
    var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
    dist = Math.round(R * c);
    var y = Math.sin(dLon) * Math.cos(l2);
    var x = Math.cos(l1)*Math.sin(l2) -
            Math.sin(l1)*Math.cos(l2)*Math.cos(dLon);
    head = toDeg(Math.round(Math.atan2(y, x)));
    if (head < 360) {
      head = (head % 360) + 360;
    }
    if ((dist != prevDist) || (head != prevHead)) {
      msg = {"dist": dist,
             "head": head,
             "accuracy": accuracy,
             "phonehead": -1,
             "speed": 0,
             "units": units,
             "sens": parseInt(sens)};
      if ((speed > 0) && (!isNan(phoneHead))) {
        // enough movement to calculate speed and heading
        msg["phonehead"] = phoneHead;
        msg["speed"] = speed;
      }
      sendMessage(msg);
      prevDist = dist;
      prevHead = head;
    }
  }
}

function locationError(error) {
  console.warn('location error (' + error.code + '): ' + error.message);
}

function storeCurrentPosition() {
  // console.log("Attempting to store current position.");
  var msg = {"dist": 0,
             "head": 0};
  sendMessage(msg);
  navigator.geolocation.getCurrentPosition(addLocation, locationError, locationOptions);
}

function getPositionFromServer(id) {
  var url = serverAddress + token + '/place/' + id;
  var req = new XMLHttpRequest();
  req.onload = parseServerResponse;
  req.open("get", url, true);
  req.send();
}

function parseServerResponse() {
  var res = this.responseText;
  if (!res) {
    console.warn('No response from server');
    return false;
  }
  if ((this.status != 200) || (this.responseText.substr(0,1) != '{')) {
    console.warn('Server did not return a JSON object: ' + this.responseText);
    return false;
  }
  var obj = JSON.parse(res);
  if (!obj || !obj.position) {
    console.warn('No position from server: ' + this.responseText);
    return false;
  }
  console.log('Got position from server: ' +
    obj.position.coords.latitude + ', ' +
    obj.position.coords.longitude);
  storeLocation(obj.position);
}

function startWatcher() {
  if (locationInterval) {
    clearInterval(locationInterval);
  }
  if (locationWatcher) {
    navigator.geolocation.clearWatch(locationWatcher);
  }
  if (interval > 0) {
    console.log('Interval is ' + interval + ', using getCurrentPosition and setInterval');
    navigator.geolocation.getCurrentPosition(locationSuccess, locationError, locationOptions);
    locationInterval = setInterval(function() {
      navigator.geolocation.getCurrentPosition(locationSuccess, locationError, locationOptions);
    }, interval * 1000);
  }
  else {
    console.log('Interval not set, using watchPosition');
    navigator.geolocation.getCurrentPosition(locationSuccess, locationError, locationOptions);
    locationWatcher = navigator.geolocation.watchPosition(locationSuccess, locationError, locationOptions);
    console.log("Started location watcher: " + locationWatcher);
  }
  // for testing: randomize movement!
  /*
  window.navigator.geolocation.getCurrentPosition(locationSuccess, locationError, locationOptions);
  if (!interval) {
    interval = 10;
  }
  locationWatcher = setInterval(function() {
    lat1 = lat1 + Math.random()/100 * ((Math.random() > 0.5) || -1);
    lon1 = lon1 - Math.random()/100 * ((Math.random() > 0.5) || -1);
    calculate();
  }, interval * 1000);
  console.log("Started fake location watcher: " + locationWatcher);
  */
}
function toRad(num) {
  return num * Math.PI / 180;
}
function toDeg(num) {
  return num * 180 / Math.PI;
}
<|endoftext|>


<commit_before>'use strict';
var alexa = require('alexa-app'),
    HarmonyUtils = require('harmony-hub-util'),
    harmony_clients = {},
    conf = require('./remote_conf.js'),
    Q = require('q'),
    hub_ip = conf.hub_ip,
    app_id = conf.app_id,
    MAX_ACTIVITY_WAIT_TIME_MS = 15000;

// Define an alexa-app
var app = new alexa.app('remote');

app.launch(function(req, res) {
    console.log("Launching the application");
});

function execCmdDF(hutils, is_device, dev_or_act, cmd, cnt, fn, res) {
    console.log("execCmd called with cnt = " + cnt + " is_dev " + is_device +
                " dev/act " + dev_or_act + " cmd = " + cmd);
    if (cnt === 0) {
        fn(res);
        hutils.end();
        return;
    }
    hutils.executeCommand(is_device, dev_or_act, cmd).then(function (res) {
        console.log(cnt + ". Command " + cmd + " to device/activity " +
                    dev_or_act + " was executed with result : " + res);
        if (res) {
            setTimeout(function () {
                execCmdDF(hutils, is_device, dev_or_act, cmd, cnt - 1, fn, res);
            }, 100);
        }
    }, function(err) {
        console.log("ERROR Occured " + err);
        console.log("      stack " + err.stack);
    });
}

function execCmd(dev, cmd, cnt, fn, res) {
    new HarmonyUtils(hub_ip).then(function (hutil) {
        execCmdDF(hutil, true, dev, cmd, cnt, fn, res);
    });
}

function execCmdCurrentActivity(cmd, cnt, fn, res) {
    new HarmonyUtils(hub_ip).then(function (hutils) {
        hutils.readCurrentActivity().then(function (current_activity) {
            execCmdDF(hutils, false, current_activity, cmd, cnt, fn, res);
        });
    });
}

/**
 * Waits for a specific activity to be the current activity
 * (assumes the activity has already been executed).
 *
 * @param {string} hutils - The hutils to use
 * @param {string} act - The activity to wait for
 * @param {number} max_wait_timestamp - The timestamp to give up on waiting
 * @returns deferred promise
 */
function waitForActivity(hutils, act, max_wait_timestamp) {
   var deferred = Q.defer(),
      wait_interval = 3000;

   hutils.readCurrentActivity().then(function (current_activity) {
      if (current_activity != act) {
         if (Date.now() > max_wait_timestamp) {
            deferred.reject('Max wait time exceeded waiting for ' + act);
            return;
         }
         console.log(act + ' is not the current activity yet, waiting another ' + wait_interval + 'ms ...');
         setTimeout(function () {
            waitForActivity(hutils, act, max_wait_timestamp).then(function (res) {
               deferred.resolve(res);
            }, function (err) {
               deferred.reject(err);
            });
         }, wait_interval);
      } else {
         console.log(act + ' is now the current activity');
         deferred.resolve(true);
      }
   }, function (err) {
      deferred.reject(err);
   });

   return deferred.promise;
}

/**
 * Executes a command for a specific activity, executing and waiting
 * for that activity if needed.
 *
 * @param {string} act - The activity the command should be executed under
 * @param {string} cmd - The command to execute
 * @param {string} cnt - The count
 */
function execActivityCmd(act, cmd, cnt) {
   new HarmonyUtils(hub_ip).then(function (hutils) {
       hutils.readCurrentActivity().then(function (current_activity) {
          if (current_activity != act) {
             // Need to switch activities and wait
             execActivity(act, function (res) {
                waitForActivity(hutils, act, Date.now() + MAX_ACTIVITY_WAIT_TIME_MS).then(function (res) {
                   execCmdCurrentActivity(cmd, 1, function (res) {
                      hutils.end();
                      console.log('Command executed with result : ' + res);
                   });
                }, function (err) {
                   console.error(err);
                   hutils.end();
                });
             });
          } else {
             console.log(act + ' is already the current activity, executing command');
             execCmdCurrentActivity(cmd, 1, function (res) {
                console.log('Command executed with result : ' + res);
                hutils.end();
             });
          }
       });
   });
}

function execActivity(act, fn) {
    new HarmonyUtils(hub_ip).then(function (hutils) {
        hutils.executeActivity(act).then(function (res) {
            fn(res);
        });
    });
}

app.pre = function(req, res, type) {
    if (req.applicationId !== app_id) {
        console.log(" Received and invalid applicaiton ID " + req.applicationId);
        res.fail("Invalid applicationId");
    }
};

app.intent('IncreaseVolume',
    {
        "slots" : {'AMOUNT' : 'NUMBER'},
        "utterances" : ["{increase|} volume {by|} {1-9|AMOUNT}"]
    },
    function (req, res) {
        var amt = parseInt(req.slot('AMOUNT'), 10);
        if (isNaN(amt)) {
            amt = 1;
        }
        res.say('Increasing volume by ' + amt);
        console.log('Increasing volume by ' + amt);
        execCmdCurrentActivity('Volume,Volume Up', amt, function (res) {
            console.log("Command Volume UP was executed with result : " + res);
        });
    });
app.intent('DecreaseVolume',
    {
        "slots" : {'AMOUNT' : 'NUMBER'},
        "utterances" : ["{decrease volume|reduce volume|down volume|volume down} {by|} {1-9|AMOUNT}"]
    },
    function (req, res) {
        var amt = parseInt(req.slot('AMOUNT'), 10);
        if (isNaN(amt)) {
            amt = 1;
        }
        res.say('Decreasing volume by ' + amt);
        console.log('Decreasing volume by ' + amt);
        execCmdCurrentActivity('Volume,Volume Down', amt, function (res) {
            console.log("Command Volume Down was executed with result : " + res);
        });
    });

app.intent('MuteVolume',
    {
        "slots" : {},
        "utterances" : ["{mute|quiet|shut up|unmute}"]
    },
    function (req, res) {
        res.say('Muting!');
        console.log('Muting!');
        execCmdCurrentActivity('Volume,Mute', 1, function (res) {
            console.log("Command Mute executed with result : " + res);
        });
    });

app.intent('IncreaseTVVolume',
    {
        "slots" : {'AMOUNT' : 'NUMBER'},
        "utterances" : ["{increase|} TV volume by {1-9|AMOUNT}"]
    },
    function (req, res) {
        var amt = parseInt(req.slot('AMOUNT'), 10);
        if (isNaN(amt)) {
            amt = 1;
        }
        res.say('Increasing TV volume by ' + amt);
        console.log('Increasing volume by ' + amt);
        execCmd('TV', 'VolumeUp', amt, function (res) {
            console.log("Command Volume UP was executed with result : " + res);
        });
    });
app.intent('DecreaseTVVolume',
    {
        "slots" : {'AMOUNT' : 'NUMBER'},
        "utterances" : ["{decrease TV volume|reduce TV volume} by {1-9|AMOUNT}"]
    },
    function (req, res) {
        var amt = parseInt(req.slot('AMOUNT'), 10);
        if (isNaN(amt)) {
            amt = 1;
        }
        res.say('Decreasing TV volume by ' + amt);
        console.log('Decreasing volume by ' + amt);
        execCmd('TV', 'VolumeDown', amt, function (res) {
            console.log("Command Volume Down was executed with result : " + res);
        });
    });

app.intent('MuteTVVolume',
    {
        "slots" : {},
        "utterances" : ["{mute|unmute} {TV|telivision}"]
    },
    function (req, res) {
        res.say('Muting TV!');
        console.log('Muting!');
        execCmd('TV', 'Mute', 1, function (res) {
            console.log("Command Mute executed with result : " + res);
        });
    });

app.intent('TurnOffTV',
    {
        "slots" : {},
        "utterances" : ["{turn the TV off|turn TV off}"]
    },
    function (req, res) {
        res.say('Turning TV off!');
        console.log('Turning TV off!');
        execCmd('TV', 'PowerOff', 1, function (res) {
            console.log("Command TV PowerOff executed with result : " + res);
        });
    });

app.intent('TurnOnTV',
    {
        "slots" : {},
        "utterances" : ["{turn on the TV|turn the TV on|turn on TV|turn TV on}"]
    },
    function (req, res) {
        res.say('Turning TV on!');
        console.log('Turning TV on!');
        execCmd('TV', 'PowerOn', 1, function (res) {
            console.log("Command TV PowerOn executed with result : " + res);
        });
    });

app.intent('TurnOffAmplifier',
    {
        "slots" : {},
        "utterances" : ["{turn the amplifer off|turn amplifier off}"]
    },
    function (req, res) {
        res.say('Turning amplifer off!');
        console.log('Turning amplifier off!');
        execCmd('Amplifier', 'PowerToggle', 1, function (res) {
            console.log("Command for amplifer PowerToggle executed with result : " + res);
        });
    });

app.intent('TurnOnAmplifier',
    {
        "slots" : {},
        "utterances" : ["{turn on the amplifier|turn the amplifier on}"]
    },
    function (req, res) {
        res.say('Toggle power on the amplifier!');
        console.log('Turning amplifier on!');
        execCmd('Amplifier', 'PowerToggle', 1, function (res) {
            console.log("Command Amplifier PowerToggle executed with result : " + res);
        });
    });

app.intent('AmplifierInputNext',
    {
        "slots" : {},
        "utterances" : ["{select next amplifier input}"]
    },
    function (req, res) {
        res.say('selecting next input on amplifier!');
        console.log('Selecting next amplifier input!');
        execCmd('Amplifier', 'InputNext', 1, function (res) {
            console.log("Command Amplifier InputNext executed with result : " + res);
        });
    });

app.intent('SelectChromeCast',
    {
        "slots" : {},
        "utterances" : ["{to|} {select|} {chrome cast|chromecast}"]
    },
    function (req, res) {
        res.say('Selecting Chromecast!');
        console.log('Selecting Chromecast!');
        execCmd('TV', 'InputHdmi3', 1, function (res) {
            console.log("Command TV InputHdmi3 executed with result : " + res);
        });
    });

app.intent('SelectTivo',
    {
        "slots" : {},
        "utterances" : ["{to|} select tivo"]
    },
    function (req, res) {
        res.say('Selecting tivo!');
        console.log('Selecting tivo!');
        execCmd('TV', 'InputHdmi2', 1, function (res) {
            console.log("Command TV InputHdmi2 executed with result : " + res);
        });
    });

app.intent('SelectPlaystation',
    {
        "slots" : {},
        "utterances" : ["{select|} {playstation}"]
    },
    function (req, res) {
        res.say('Selecting ps4!');
        console.log('Selecting ps4!');
        execCmd('TV', 'InputHdmi1', 1, function (res) {
            console.log("Command TV InputHdmi1 executed with result : " + res);
        });
    });

app.intent('TurnOff',
    {
        "slots" : {},
        "utterances" : ["{shutdown|good night|power everything off|power off everything|turn everything off|turn off everything|shut down}"]
    },
    function (req, res) {
        res.say('Turning off everything!');
        console.log('Turning off everythign!');
        execActivity('PowerOff', function (res) {
            console.log("Command to PowerOff executed with result : " + res);
        });
    });

app.intent('Movie',
    {
        "slots" : {},
        "utterances" : ["{movie|start movie|watch movie}"]
    },
    function (req, res) {
        res.say('Turning on Movie Mode!');
        console.log('Turning on Movie Mode!');
        execActivity('Watch a Movie', function (res) {
            console.log("Command to Watch a Movie executed with result : " + res);
        });
    });

app.intent('TIVO',
    {
        "slots" : {},
        "utterances" : ["{tivo|start tivo|watch tivo}"]
    },
    function (req, res) {
        res.say('Turning on Tivo Mode!');
        console.log('Turning on Tivo Mode!');
        execActivity('Watch Tivo', function (res) {
            console.log("Command to Watch Tivo executed with result : " + res);
        });
    });

app.intent('Music',
    {
        "slots" : {},
        "utterances" : ["{music|start music}"]
    },
    function (req, res) {
        res.say('Turning on Music Mode!');
        console.log('Turning on Music Mode!');
        execActivity('Listen to Digital Music', function (res) {
            console.log("Command to Music executed with result : " + res);
        });
    });

/**
 * Creates an intent function for a specific channel configuration
 *
 * @param {object} channel - The channel configuration to create the function for
 * @returns {function} The channel intent function
 */
function getChannelFunction(channel) {
   return function (req, res) {
      res.say('Starting to ' + channel.utterance_name + '!');
      console.log('Starting to ' + channel.utterance_name + '!');
      var cmd = [], channel_chars = channel.channel.split(""), j;
      for (j = 0; j < channel_chars.length; j++) {
         cmd[j] = 'NumericBasic,' + channel_chars[j];
      }
      execActivityCmd(channel.activity, cmd, 1);
   }
}

if (conf.channels) {
   // Iterate through the configured channels and create intents for them
   var channel_index;
   for (channel_index = 0; channel_index < conf.channels.length; channel_index++) {
      var channel = conf.channels[channel_index];
      // Build an intent name
      var intent = channel.activity.replace(" ", "");
      intent = intent.charAt(0).toUpperCase() + intent.slice(1);
      var utterance = channel.utterance_name.replace(" ", "");
      utterance = utterance.charAt(0).toUpperCase() + utterance.slice(1);
      intent = intent + utterance;
      app.intent(intent,
            {
                "slots" : {},
                "utterances" : ["{to|} " + channel.utterance_name]
            },
            getChannelFunction(channel));
      console.log('Added intent ' + intent +
            ' with utterance ' + channel.utterance_name +
            ' which triggers channel ' + channel.channel );
   }
}

module.exports = app;
<commit_msg>fix for Issue #20 and #15. Added correct application id for testing<commit_after>'use strict';
var alexa = require('alexa-app'),
    HarmonyUtils = require('harmony-hub-util'),
    harmony_clients = {},
    conf = require('./remote_conf.js'),
    Q = require('q'),
    hub_ip = conf.hub_ip,
    app_id = conf.app_id,
    MAX_ACTIVITY_WAIT_TIME_MS = 15000;

// Define an alexa-app
var app = new alexa.app('remote');
app.id = conf.app_id

app.launch(function(req, res) {
    console.log("Launching the application");
});

function execCmdDF(hutils, is_device, dev_or_act, cmd, cnt, fn, res) {
    console.log("execCmd called with cnt = " + cnt + " is_dev " + is_device +
                " dev/act " + dev_or_act + " cmd = " + cmd);
    if (cnt === 0) {
        fn(res);
        hutils.end();
        return;
    }
    hutils.executeCommand(is_device, dev_or_act, cmd).then(function (res) {
        console.log(cnt + ". Command " + cmd + " to device/activity " +
                    dev_or_act + " was executed with result : " + res);
        if (res) {
            setTimeout(function () {
                execCmdDF(hutils, is_device, dev_or_act, cmd, cnt - 1, fn, res);
            }, 100);
        }
    }, function(err) {
        console.log("ERROR Occured " + err);
        console.log("      stack " + err.stack);
    });
}

function execCmd(dev, cmd, cnt, fn, res) {
    new HarmonyUtils(hub_ip).then(function (hutil) {
        execCmdDF(hutil, true, dev, cmd, cnt, fn, res);
    });
}

function execCmdCurrentActivity(cmd, cnt, fn, res) {
    new HarmonyUtils(hub_ip).then(function (hutils) {
        hutils.readCurrentActivity().then(function (current_activity) {
            execCmdDF(hutils, false, current_activity, cmd, cnt, fn, res);
        });
    });
}

/**
 * Waits for a specific activity to be the current activity
 * (assumes the activity has already been executed).
 *
 * @param {string} hutils - The hutils to use
 * @param {string} act - The activity to wait for
 * @param {number} max_wait_timestamp - The timestamp to give up on waiting
 * @returns deferred promise
 */
function waitForActivity(hutils, act, max_wait_timestamp) {
   var deferred = Q.defer(),
      wait_interval = 3000;

   hutils.readCurrentActivity().then(function (current_activity) {
      if (current_activity != act) {
         if (Date.now() > max_wait_timestamp) {
            deferred.reject('Max wait time exceeded waiting for ' + act);
            return;
         }
         console.log(act + ' is not the current activity yet, waiting another ' + wait_interval + 'ms ...');
         setTimeout(function () {
            waitForActivity(hutils, act, max_wait_timestamp).then(function (res) {
               deferred.resolve(res);
            }, function (err) {
               deferred.reject(err);
            });
         }, wait_interval);
      } else {
         console.log(act + ' is now the current activity');
         deferred.resolve(true);
      }
   }, function (err) {
      deferred.reject(err);
   });

   return deferred.promise;
}

/**
 * Executes a command for a specific activity, executing and waiting
 * for that activity if needed.
 *
 * @param {string} act - The activity the command should be executed under
 * @param {string} cmd - The command to execute
 * @param {string} cnt - The count
 */
function execActivityCmd(act, cmd, cnt) {
   new HarmonyUtils(hub_ip).then(function (hutils) {
       hutils.readCurrentActivity().then(function (current_activity) {
          if (current_activity != act) {
             // Need to switch activities and wait
             execActivity(act, function (res) {
                waitForActivity(hutils, act, Date.now() + MAX_ACTIVITY_WAIT_TIME_MS).then(function (res) {
                   execCmdCurrentActivity(cmd, 1, function (res) {
                      hutils.end();
                      console.log('Command executed with result : ' + res);
                   });
                }, function (err) {
                   console.error(err);
                   hutils.end();
                });
             });
          } else {
             console.log(act + ' is already the current activity, executing command');
             execCmdCurrentActivity(cmd, 1, function (res) {
                console.log('Command executed with result : ' + res);
                hutils.end();
             });
          }
       });
   });
}

function execActivity(act, fn) {
    new HarmonyUtils(hub_ip).then(function (hutils) {
        hutils.executeActivity(act).then(function (res) {
            fn(res);
        });
    });
}

app.pre = function(req, res, type) {
    if (req.applicationId !== app_id) {
        console.log(" Received and invalid applicaiton ID " + req.applicationId);
        res.fail("Invalid applicationId");
    }
};

app.intent('IncreaseVolume',
    {
        "slots" : {'AMOUNT' : 'NUMBER'},
        "utterances" : ["{increase|} volume {by|} {1-9|AMOUNT}"]
    },
    function (req, res) {
        var amt = parseInt(req.slot('AMOUNT'), 10);
        if (isNaN(amt)) {
            amt = 1;
        }
        res.say('Increasing volume by ' + amt);
        console.log('Increasing volume by ' + amt);
        execCmdCurrentActivity('Volume,Volume Up', amt, function (res) {
            console.log("Command Volume UP was executed with result : " + res);
        });
    });
app.intent('DecreaseVolume',
    {
        "slots" : {'AMOUNT' : 'NUMBER'},
        "utterances" : ["{decrease volume|reduce volume|down volume|volume down} {by|} {1-9|AMOUNT}"]
    },
    function (req, res) {
        var amt = parseInt(req.slot('AMOUNT'), 10);
        if (isNaN(amt)) {
            amt = 1;
        }
        res.say('Decreasing volume by ' + amt);
        console.log('Decreasing volume by ' + amt);
        execCmdCurrentActivity('Volume,Volume Down', amt, function (res) {
            console.log("Command Volume Down was executed with result : " + res);
        });
    });

app.intent('MuteVolume',
    {
        "slots" : {},
        "utterances" : ["{mute|quiet|shut up|unmute}"]
    },
    function (req, res) {
        res.say('Muting!');
        console.log('Muting!');
        execCmdCurrentActivity('Volume,Mute', 1, function (res) {
            console.log("Command Mute executed with result : " + res);
        });
    });

app.intent('IncreaseTVVolume',
    {
        "slots" : {'AMOUNT' : 'NUMBER'},
        "utterances" : ["{increase|} TV volume by {1-9|AMOUNT}"]
    },
    function (req, res) {
        var amt = parseInt(req.slot('AMOUNT'), 10);
        if (isNaN(amt)) {
            amt = 1;
        }
        res.say('Increasing TV volume by ' + amt);
        console.log('Increasing volume by ' + amt);
        execCmd('TV', 'VolumeUp', amt, function (res) {
            console.log("Command Volume UP was executed with result : " + res);
        });
    });
app.intent('DecreaseTVVolume',
    {
        "slots" : {'AMOUNT' : 'NUMBER'},
        "utterances" : ["{decrease TV volume|reduce TV volume} by {1-9|AMOUNT}"]
    },
    function (req, res) {
        var amt = parseInt(req.slot('AMOUNT'), 10);
        if (isNaN(amt)) {
            amt = 1;
        }
        res.say('Decreasing TV volume by ' + amt);
        console.log('Decreasing volume by ' + amt);
        execCmd('TV', 'VolumeDown', amt, function (res) {
            console.log("Command Volume Down was executed with result : " + res);
        });
    });

app.intent('MuteTVVolume',
    {
        "slots" : {},
        "utterances" : ["{mute|unmute} {TV|telivision}"]
    },
    function (req, res) {
        res.say('Muting TV!');
        console.log('Muting!');
        execCmd('TV', 'Mute', 1, function (res) {
            console.log("Command Mute executed with result : " + res);
        });
    });

app.intent('TurnOffTV',
    {
        "slots" : {},
        "utterances" : ["{turn the TV off|turn TV off}"]
    },
    function (req, res) {
        res.say('Turning TV off!');
        console.log('Turning TV off!');
        execCmd('TV', 'PowerOff', 1, function (res) {
            console.log("Command TV PowerOff executed with result : " + res);
        });
    });

app.intent('TurnOnTV',
    {
        "slots" : {},
        "utterances" : ["{turn on the TV|turn the TV on|turn on TV|turn TV on}"]
    },
    function (req, res) {
        res.say('Turning TV on!');
        console.log('Turning TV on!');
        execCmd('TV', 'PowerOn', 1, function (res) {
            console.log("Command TV PowerOn executed with result : " + res);
        });
    });

app.intent('TurnOffAmplifier',
    {
        "slots" : {},
        "utterances" : ["{turn the amplifer off|turn amplifier off}"]
    },
    function (req, res) {
        res.say('Turning amplifer off!');
        console.log('Turning amplifier off!');
        execCmd('Amplifier', 'PowerToggle', 1, function (res) {
            console.log("Command for amplifer PowerToggle executed with result : " + res);
        });
    });

app.intent('TurnOnAmplifier',
    {
        "slots" : {},
        "utterances" : ["{turn on the amplifier|turn the amplifier on}"]
    },
    function (req, res) {
        res.say('Toggle power on the amplifier!');
        console.log('Turning amplifier on!');
        execCmd('Amplifier', 'PowerToggle', 1, function (res) {
            console.log("Command Amplifier PowerToggle executed with result : " + res);
        });
    });

app.intent('AmplifierInputNext',
    {
        "slots" : {},
        "utterances" : ["{select next amplifier input}"]
    },
    function (req, res) {
        res.say('selecting next input on amplifier!');
        console.log('Selecting next amplifier input!');
        execCmd('Amplifier', 'InputNext', 1, function (res) {
            console.log("Command Amplifier InputNext executed with result : " + res);
        });
    });

app.intent('SelectChromeCast',
    {
        "slots" : {},
        "utterances" : ["{to|} {select|} {chrome cast|chromecast}"]
    },
    function (req, res) {
        res.say('Selecting Chromecast!');
        console.log('Selecting Chromecast!');
        execCmd('TV', 'InputHdmi3', 1, function (res) {
            console.log("Command TV InputHdmi3 executed with result : " + res);
        });
    });

app.intent('SelectTivo',
    {
        "slots" : {},
        "utterances" : ["{to|} select tivo"]
    },
    function (req, res) {
        res.say('Selecting tivo!');
        console.log('Selecting tivo!');
        execCmd('TV', 'InputHdmi2', 1, function (res) {
            console.log("Command TV InputHdmi2 executed with result : " + res);
        });
    });

app.intent('SelectPlaystation',
    {
        "slots" : {},
        "utterances" : ["{select|} {playstation}"]
    },
    function (req, res) {
        res.say('Selecting ps4!');
        console.log('Selecting ps4!');
        execCmd('TV', 'InputHdmi1', 1, function (res) {
            console.log("Command TV InputHdmi1 executed with result : " + res);
        });
    });

app.intent('TurnOff',
    {
        "slots" : {},
        "utterances" : ["{shutdown|good night|power everything off|power off everything|turn everything off|turn off everything|shut down}"]
    },
    function (req, res) {
        res.say('Turning off everything!');
        console.log('Turning off everythign!');
        execActivity('PowerOff', function (res) {
            console.log("Command to PowerOff executed with result : " + res);
        });
    });

app.intent('Movie',
    {
        "slots" : {},
        "utterances" : ["{movie|start movie|watch movie}"]
    },
    function (req, res) {
        res.say('Turning on Movie Mode!');
        console.log('Turning on Movie Mode!');
        execActivity('Watch a Movie', function (res) {
            console.log("Command to Watch a Movie executed with result : " + res);
        });
    });

app.intent('TIVO',
    {
        "slots" : {},
        "utterances" : ["{tivo|start tivo|watch tivo}"]
    },
    function (req, res) {
        res.say('Turning on Tivo Mode!');
        console.log('Turning on Tivo Mode!');
        execActivity('Watch Tivo', function (res) {
            console.log("Command to Watch Tivo executed with result : " + res);
        });
    });

app.intent('Music',
    {
        "slots" : {},
        "utterances" : ["{music|start music}"]
    },
    function (req, res) {
        res.say('Turning on Music Mode!');
        console.log('Turning on Music Mode!');
        execActivity('Listen to Digital Music', function (res) {
            console.log("Command to Music executed with result : " + res);
        });
    });

/**
 * Creates an intent function for a specific channel configuration
 *
 * @param {object} channel - The channel configuration to create the function for
 * @returns {function} The channel intent function
 */
function getChannelFunction(channel) {
   return function (req, res) {
      res.say('Starting to ' + channel.utterance_name + '!');
      console.log('Starting to ' + channel.utterance_name + '!');
      var cmd = [], channel_chars = channel.channel.split(""), j;
      for (j = 0; j < channel_chars.length; j++) {
         cmd[j] = 'NumericBasic,' + channel_chars[j];
      }
      execActivityCmd(channel.activity, cmd, 1);
   }
}

if (conf.channels) {
   // Iterate through the configured channels and create intents for them
   var channel_index;
   for (channel_index = 0; channel_index < conf.channels.length; channel_index++) {
      var channel = conf.channels[channel_index];
      // Build an intent name
      var intent = channel.activity.replace(" ", "");
      intent = intent.charAt(0).toUpperCase() + intent.slice(1);
      var utterance = channel.utterance_name.replace(" ", "");
      utterance = utterance.charAt(0).toUpperCase() + utterance.slice(1);
      intent = intent + utterance;
      app.intent(intent,
            {
                "slots" : {},
                "utterances" : ["{to|} " + channel.utterance_name]
            },
            getChannelFunction(channel));
      console.log('Added intent ' + intent +
            ' with utterance ' + channel.utterance_name +
            ' which triggers channel ' + channel.channel );
   }
}

module.exports = app;
<|endoftext|>


<commit_before>import {hasRootPathPrefixInString, transformRelativeToRootPath} from '../plugin/helper';
import slash from 'slash';
import path from 'path';

describe('helper#transformRelativeToRootPath', () => {
  it('returns a string', () => {
    const func = transformRelativeToRootPath('');
    expect(func).to.be.a('string');
  });

  it('transforms given path relative path', () => {
    const rootPath = slash('./path');
    const result = transformRelativeToRootPath('~/some/path', '', '~', 'some/file.js');
    expect(result).to.equal(rootPath);
  });

  it('considers .. in relative path', () => {
    const rootPath = slash('./path');
    const result = transformRelativeToRootPath('~/util', '../shared', '~', 'test.js');
    expect(result).to.not.equal(`${path.resolve('../shared')}/util/test.js`);
  });

  it('considers multiple .. in relative path', () => {
    const rootPath = slash('./path');
    const result = transformRelativeToRootPath('~/util', '../../../shared', '~', 'test.js');
    expect(result).to.not.equal(`${path.resolve('../../../shared')}/util/test.js`);
  });

  it('stops adding .. after the first one has been reached', () => {
    const rootPath = slash('./path');
    const result = transformRelativeToRootPath('~/util', '../shared/test/../test', '~', 'test.js');
    expect(result).to.not.equal(`${path.resolve('../shared')}/util/test/../test/test.js`);
  });

  it('throws error if no string is passed', () => {
    expect(() => {
      transformRelativeToRootPath();
    }).to.throw(Error);
  });
});

describe('helper#hasRootPathPrefixInString', () => {
  it('returns a boolean', () => {
    const func = hasRootPathPrefixInString();
    expect(func).to.be.a('boolean');
  });

  it('check if "~/" is at the beginning of the string', () => {
    const withoutRootPathPrefix = hasRootPathPrefixInString('some/path');
    const withRootPathPrefix = hasRootPathPrefixInString('~/some/path');
    expect(withoutRootPathPrefix).to.be.false;
    expect(withRootPathPrefix).to.be.true;
  });

  it('returns false if no string is passed', () => {
    const nothingPassed = hasRootPathPrefixInString();
    const wrongTypePassed = hasRootPathPrefixInString([]);
    expect(nothingPassed).to.be.false;
    expect(wrongTypePassed).to.be.false;
  });
});
<commit_msg>Remove copy/pasted unnecessary statements<commit_after>import {hasRootPathPrefixInString, transformRelativeToRootPath} from '../plugin/helper';
import slash from 'slash';
import path from 'path';

describe('helper#transformRelativeToRootPath', () => {
  it('returns a string', () => {
    const func = transformRelativeToRootPath('');
    expect(func).to.be.a('string');
  });

  it('transforms given path relative path', () => {
    const rootPath = slash('./path');
    const result = transformRelativeToRootPath('~/some/path', '', '~', 'some/file.js');
    expect(result).to.equal(rootPath);
  });

  it('considers .. in relative path', () => {
    const result = transformRelativeToRootPath('~/util', '../shared', '~', 'test.js');
    expect(result).to.not.equal(`${path.resolve('../shared')}/util/test.js`);
  });

  it('considers multiple .. in relative path', () => {
    const result = transformRelativeToRootPath('~/util', '../../../shared', '~', 'test.js');
    expect(result).to.not.equal(`${path.resolve('../../../shared')}/util/test.js`);
  });

  it('stops adding .. after the first one has been reached', () => {
    const result = transformRelativeToRootPath('~/util', '../shared/test/../test', '~', 'test.js');
    expect(result).to.not.equal(`${path.resolve('../shared')}/util/test/../test/test.js`);
  });

  it('throws error if no string is passed', () => {
    expect(() => {
      transformRelativeToRootPath();
    }).to.throw(Error);
  });
});

describe('helper#hasRootPathPrefixInString', () => {
  it('returns a boolean', () => {
    const func = hasRootPathPrefixInString();
    expect(func).to.be.a('boolean');
  });

  it('check if "~/" is at the beginning of the string', () => {
    const withoutRootPathPrefix = hasRootPathPrefixInString('some/path');
    const withRootPathPrefix = hasRootPathPrefixInString('~/some/path');
    expect(withoutRootPathPrefix).to.be.false;
    expect(withRootPathPrefix).to.be.true;
  });

  it('returns false if no string is passed', () => {
    const nothingPassed = hasRootPathPrefixInString();
    const wrongTypePassed = hasRootPathPrefixInString([]);
    expect(nothingPassed).to.be.false;
    expect(wrongTypePassed).to.be.false;
  });
});
<|endoftext|>


<commit_before>/*
 * Copyright 2015 Shivan Kaul
 * Copyright 2015 Michael Williams
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
var poi_index = 0;
var current_poi = null;
var new_poi = false;

function set_poi(poi) {
	var name = trim(poi.name)
	var url = encodeURIComponent(name)
	$("#place_of_interest .place-name").html(name)
	var search = "http://www.yelp.ca/search?find_desc=" + url
	$("#link-yelp").attr("href", search)
	current_poi = poi;
	new_poi = true;
	setInterval('update_poi_image()', 500);
	initialize()
}

function update_poi_image() {
	if (new_poi && current_poi){
		if(current_poi.imageURL){
			$("#place_of_interest img").attr("src", current_poi.imageURL);
			$("#place_of_interest img").prop("hidden", false);
			new_poi = false;
		} else {
			$("#place_of_interest img").prop("hidden", true);
		}
		set_footer_offset();
	}
}

function next_poi(){
	if (poi_list.length == 0) {
		return null;
	}
	if (poi_index >= (poi_list.length - 1)) {
		return poi_list[poi_list.length - 1];
	} else {
		poi_index++;
		return poi_list[poi_index];
	}
}

function prev_poi(){
	if (poi_list.length == 0) {
		return null;
	}
	if (poi_index <= 0) {
		return poi_list[0];
	} else {
		poi_index--;
		return poi_list[poi_index];
	}
}

function is_next_poi(){
	return (poi_index < poi_list.length - 1);
}

function is_prev_poi(){
	return (poi_list > 0);
}

$(document).keyup(function(e) {
	e = e||window.event;
	if (e.keyCode == "37") { //Left Arrow
		set_poi(prev_poi());
	} else if (e.keyCode == "39") { //Right Arrow
		set_poi(next_poi());
	}
});

function set_footer_offset() {
	var f = $("footer");
	if (window.innerHeight >= $(document).height()) {
		f.css("position", "fixed");
		f.css("bottom", "2%");
		f.css("left", "50%");
		f.css("transform", "translateX(-50%)");
	} else {
		f.css("position", "relative");
		f.css("bottom", "auto");
		f.css("left", "auto");
		f.css("transform", "none");
	}

}

$(window).resize(function() {
	set_footer_offset();
});

$(document).ready(function(){
	set_footer_offset();
	get_location();
});
<commit_msg>fixed image over map issue<commit_after>/*
 * Copyright 2015 Shivan Kaul
 * Copyright 2015 Michael Williams
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
var poi_index = 0;
var current_poi = null;
var new_poi = false;

function set_poi(poi) {
	var name = trim(poi.name)
	var url = encodeURIComponent(name)
	$("#place_of_interest .place-name").html(name)
	var search = "http://www.yelp.ca/search?find_desc=" + url
	$("#link-yelp").attr("href", search)
	current_poi = poi;
	new_poi = true;
	setInterval('update_poi_image()', 500);
	initialize()
}

function update_poi_image() {
	if (new_poi && current_poi){
		if(current_poi.imageURL){
			$("#place_of_interest .img-circle").attr("src", current_poi.imageURL);
			$("#place_of_interest .img-circle").prop("hidden", false);
			new_poi = false;
		} else {
			$("#place_of_interest .img-circle").prop("hidden", true);
		}
		set_footer_offset();
	}
}

function next_poi(){
	if (poi_list.length == 0) {
		return null;
	}
	if (poi_index >= (poi_list.length - 1)) {
		return poi_list[poi_list.length - 1];
	} else {
		poi_index++;
		return poi_list[poi_index];
	}
}

function prev_poi(){
	if (poi_list.length == 0) {
		return null;
	}
	if (poi_index <= 0) {
		return poi_list[0];
	} else {
		poi_index--;
		return poi_list[poi_index];
	}
}

function is_next_poi(){
	return (poi_index < poi_list.length - 1);
}

function is_prev_poi(){
	return (poi_list > 0);
}

$(document).keyup(function(e) {
	e = e||window.event;
	if (e.keyCode == "37") { //Left Arrow
		set_poi(prev_poi());
	} else if (e.keyCode == "39") { //Right Arrow
		set_poi(next_poi());
	}
});

function set_footer_offset() {
	var f = $("footer");
	if (window.innerHeight >= $(document).height()) {
		f.css("position", "fixed");
		f.css("bottom", "2%");
		f.css("left", "50%");
		f.css("transform", "translateX(-50%)");
	} else {
		f.css("position", "relative");
		f.css("bottom", "auto");
		f.css("left", "auto");
		f.css("transform", "none");
	}

}

$(window).resize(function() {
	set_footer_offset();
});

$(document).ready(function(){
	set_footer_offset();
	get_location();
});
<|endoftext|>


<commit_before>;(function () {

// 1. create iframe pointing to script on jsconsole.com domain
// 2. create console object with: log, dir, etc?
// 3. console.log runs postMessage with json.stringified content
// 4. jsconsole.com/remote/?id.onMessage = send to server, and wait for response.

function sortci(a, b) {
  return a.toLowerCase() < b.toLowerCase() ? -1 : 1;
}

// from console.js
function stringify(o, simple) {
  var json = '', i, type = ({}).toString.call(o), parts = [], names = [];

  if (type == '[object String]') {
    json = '"' + o.replace(/\n/g, '\\n').replace(/"/g, '\\"') + '"';
  } else if (type == '[object Array]') {
    json = '[';
    for (i = 0; i < o.length; i++) {
      parts.push(stringify(o[i], simple));
    }
    json += parts.join(', ') + ']';
    json;
  } else if (type == '[object Object]') {
    json = '{';
    for (i in o) {
      names.push(i);
    }
    names.sort(sortci);
    for (i = 0; i < names.length; i++) {
      parts.push(stringify(names[i]) + ': ' + stringify(o[names[i] ], simple));
    }
    json += parts.join(', ') + '}';
  } else if (type == '[object Number]') {
    json = o+'';
  } else if (type == '[object Boolean]') {
    json = o ? 'true' : 'false';
  } else if (type == '[object Function]') {
    json = o.toString();
  } else if (o === null) {
    json = 'null';
  } else if (o === undefined) {
    json = 'undefined';
  } else if (simple == undefined) {
    json = type + '{\n';
    for (i in o) {
      names.push(i);
    }
    names.sort(sortci);
    for (i = 0; i < names.length; i++) {
      parts.push(names[i] + ': ' + stringify(o[names[i]], true)); // safety from max stack
    }
    json += parts.join(',\n') + '\n}';
  } else {
    try {
      json = o+''; // should look like an object
    } catch (e) {}
  }
  return json;
}

// Plugins that inject are screwing this up :(
// function getLastChild(el) {
//   return (el.lastChild && el.lastChild.nodeName != '#text') ? getLastChild(el.lastChild) : el;
// }

function getRemoteScript() {
  var scripts = document.getElementsByTagName('script'),
      remoteScript = scripts[scripts.length-1];
  for (var i = 0; i < scripts.length; i++) {
    if (/jsconsole\..*(:\d+)?\/remote.js/.test(scripts[i].src)) {
      remoteScript = scripts[i];
      break;
    }
  }

  return remoteScript;
}

var last = getRemoteScript();

// if (last.getAttribute('id') == '_firebugConsole') { // if Firebug is open, this all goes to crap
//   last = last.previousElementSibling;
// }

var lastSrc = last.getAttribute('src'),
    id = lastSrc.replace(/.*\?/, ''),
    origin = 'http://' + lastSrc.substr(7).replace(/\/.*$/, ''),
    remoteWindow = null,
    queue = [],
    msgType = '';

var remoteFrame = document.createElement('iframe');
remoteFrame.style.display = 'none';
remoteFrame.src = origin + '/remote.html?' + id;

// this is new - in an attempt to allow this code to be included in the head element
document.documentElement.appendChild(remoteFrame);

window.addEventListener('message', function (event) {
  if (event.origin != origin) return;

  // eval the event.data command
  try {
    if (event.data.indexOf('console.log') == 0) {
      eval('remote.echo(' + event.data.match(/console.log\((.*)\);?/)[1] + ', "' + event.data + '", true)');
    } else {
      remote.echo(eval(event.data), event.data, undefined); // must be undefined to work
    }
  } catch (e) {
    remote.error(e, event.data);
  }
}, false);

var timers = {} // timers for console.time and console.timeEnd

var remote = {
  log: function () {
    var argsObj = stringify(arguments.length == 1 ? arguments[0] : [].slice.call(arguments, 0));
    var response = [];
    [].forEach.call(arguments, function (args) {
      response.push(stringify(args, true));
    });

	var msg = JSON.stringify({ response: response, cmd: 'remote console.log', type: msgType });

    if (remoteWindow) {
      remoteWindow.postMessage(msg, origin);
    } else {
      queue.push(msg);
    }

    msgType = '';
  },
  info: function () {
    msgType = 'info';
    remote.log.apply(this, arguments);
  },
  echo: function () {
    var args = [].slice.call(arguments, 0),
        plain = args.pop(),
        cmd = args.pop(),
        response = args;

    var argsObj = stringify(response, plain),
        msg = JSON.stringify({ response: argsObj, cmd: cmd });
    if (remoteWindow) {
      remoteWindow.postMessage(msg, origin);
    } else {
      queue.push(msg);
    }
  },
  error: function (error, cmd) {
    var msg = JSON.stringify({ response: error.message, cmd: cmd, type: 'error' });
    if (remoteWindow) {
      remoteWindow.postMessage(msg, origin);
    } else {
      queue.push(msg);
    }
  },
  time: function(title){
    if(typeof title !== 'string') return;
    timers[title] = +new Date();
  },
  timeEnd: function(title){
    if(typeof title !== 'string' || !timers[title]) return;
    var execTime = +new Date() - timers[title];
    delete timers[title];
    var plain = title + ": " + execTime + "ms";
    var msg = JSON.stringify({ response: plain, cmd:  'remote console.log', type: '' });
    if (remoteWindow) {
      remoteWindow.postMessage(msg, origin);
    } else {
      queue.push(msg);
    }
  }
};

// just for extra support
remote.debug = remote.dir = remote.log;
remote.warn = remote.info;

remoteFrame.onload = function () {
  remoteWindow = remoteFrame.contentWindow;
  remoteWindow.postMessage('__init__', origin);

  remoteWindow.postMessage(stringify({ response: 'Connection established with ' + window.location.toString() + '\n' + navigator.userAgent, type: 'info' }), origin);

  for (var i = 0; i < queue.length; i++) {
    remoteWindow.postMessage(queue[i], origin);
  }
};

window.remote = remote;

window.addEventListener && window.addEventListener('error', function (event) {
  remote.error({ message: event.message }, event.filename + ':' + event.lineno);
}, false);

try {
  window.console = remote;
} catch (e) {
  console.log('cannot overwrite existing console object');
}

function warnUsage() {
  var useSS = false;
  try {
    sessionStorage.getItem('foo');
    useSS = true;
  } catch (e) {}
  if (!(useSS ? sessionStorage.jsconsole : window.name)) {
    if (useSS) sessionStorage.jsconsole = 1; else window.name = 1;
    alert('You will see this warning once per session.\n\nYou are using a remote control script on this site - if you accidently push it to production, anyone will have control of your visitor\'s browser. Remember to remove this script.');
  }
}

warnUsage();

})();
<commit_msg>fix for origin errors (#67)<commit_after>;(function () {

// 1. create iframe pointing to script on jsconsole.com domain
// 2. create console object with: log, dir, etc?
// 3. console.log runs postMessage with json.stringified content
// 4. jsconsole.com/remote/?id.onMessage = send to server, and wait for response.

function sortci(a, b) {
  return a.toLowerCase() < b.toLowerCase() ? -1 : 1;
}

// from console.js
function stringify(o, simple) {
  var json = '', i, type = ({}).toString.call(o), parts = [], names = [];

  if (type == '[object String]') {
    json = '"' + o.replace(/\n/g, '\\n').replace(/"/g, '\\"') + '"';
  } else if (type == '[object Array]') {
    json = '[';
    for (i = 0; i < o.length; i++) {
      parts.push(stringify(o[i], simple));
    }
    json += parts.join(', ') + ']';
    json;
  } else if (type == '[object Object]') {
    json = '{';
    for (i in o) {
      names.push(i);
    }
    names.sort(sortci);
    for (i = 0; i < names.length; i++) {
      parts.push(stringify(names[i]) + ': ' + stringify(o[names[i] ], simple));
    }
    json += parts.join(', ') + '}';
  } else if (type == '[object Number]') {
    json = o+'';
  } else if (type == '[object Boolean]') {
    json = o ? 'true' : 'false';
  } else if (type == '[object Function]') {
    json = o.toString();
  } else if (o === null) {
    json = 'null';
  } else if (o === undefined) {
    json = 'undefined';
  } else if (simple == undefined) {
    json = type + '{\n';
    for (i in o) {
      names.push(i);
    }
    names.sort(sortci);
    for (i = 0; i < names.length; i++) {
      parts.push(names[i] + ': ' + stringify(o[names[i]], true)); // safety from max stack
    }
    json += parts.join(',\n') + '\n}';
  } else {
    try {
      json = o+''; // should look like an object
    } catch (e) {}
  }
  return json;
}

// Plugins that inject are screwing this up :(
// function getLastChild(el) {
//   return (el.lastChild && el.lastChild.nodeName != '#text') ? getLastChild(el.lastChild) : el;
// }

function getRemoteScript() {
  var scripts = document.getElementsByTagName('script'),
      remoteScript = scripts[scripts.length-1];
  for (var i = 0; i < scripts.length; i++) {
    if (/jsconsole\..*(:\d+)?\/remote.js/.test(scripts[i].src)) {
      remoteScript = scripts[i];
      break;
    }
  }

  return remoteScript;
}

var last = getRemoteScript();

// if (last.getAttribute('id') == '_firebugConsole') { // if Firebug is open, this all goes to crap
//   last = last.previousElementSibling;
// }

var lastSrc = last.getAttribute('src'),
    id = lastSrc.replace(/.*\?/, ''),
    origin = document.location.protocol + '//' + lastSrc.substr(7).replace(/\/.*$/, ''),
    remoteWindow = null,
    queue = [],
    msgType = '';

var remoteFrame = document.createElement('iframe');
remoteFrame.style.display = 'none';
remoteFrame.src = origin + '/remote.html?' + id;

// this is new - in an attempt to allow this code to be included in the head element
document.documentElement.appendChild(remoteFrame);

window.addEventListener('message', function (event) {
  if (event.origin != origin) return;

  // eval the event.data command
  try {
    if (event.data.indexOf('console.log') == 0) {
      eval('remote.echo(' + event.data.match(/console.log\((.*)\);?/)[1] + ', "' + event.data + '", true)');
    } else {
      remote.echo(eval(event.data), event.data, undefined); // must be undefined to work
    }
  } catch (e) {
    remote.error(e, event.data);
  }
}, false);

var timers = {} // timers for console.time and console.timeEnd

var remote = {
  log: function () {
    var argsObj = stringify(arguments.length == 1 ? arguments[0] : [].slice.call(arguments, 0));
    var response = [];
    [].forEach.call(arguments, function (args) {
      response.push(stringify(args, true));
    });

	var msg = JSON.stringify({ response: response, cmd: 'remote console.log', type: msgType });

    if (remoteWindow) {
      remoteWindow.postMessage(msg, origin);
    } else {
      queue.push(msg);
    }

    msgType = '';
  },
  info: function () {
    msgType = 'info';
    remote.log.apply(this, arguments);
  },
  echo: function () {
    var args = [].slice.call(arguments, 0),
        plain = args.pop(),
        cmd = args.pop(),
        response = args;

    var argsObj = stringify(response, plain),
        msg = JSON.stringify({ response: argsObj, cmd: cmd });
    if (remoteWindow) {
      remoteWindow.postMessage(msg, origin);
    } else {
      queue.push(msg);
    }
  },
  error: function (error, cmd) {
    var msg = JSON.stringify({ response: error.message, cmd: cmd, type: 'error' });
    if (remoteWindow) {
      remoteWindow.postMessage(msg, origin);
    } else {
      queue.push(msg);
    }
  },
  time: function(title){
    if(typeof title !== 'string') return;
    timers[title] = +new Date();
  },
  timeEnd: function(title){
    if(typeof title !== 'string' || !timers[title]) return;
    var execTime = +new Date() - timers[title];
    delete timers[title];
    var plain = title + ": " + execTime + "ms";
    var msg = JSON.stringify({ response: plain, cmd:  'remote console.log', type: '' });
    if (remoteWindow) {
      remoteWindow.postMessage(msg, origin);
    } else {
      queue.push(msg);
    }
  }
};

// just for extra support
remote.debug = remote.dir = remote.log;
remote.warn = remote.info;

remoteFrame.onload = function () {
  remoteWindow = remoteFrame.contentWindow;
  remoteWindow.postMessage('__init__', origin);

  remoteWindow.postMessage(stringify({ response: 'Connection established with ' + window.location.toString() + '\n' + navigator.userAgent, type: 'info' }), origin);

  for (var i = 0; i < queue.length; i++) {
    remoteWindow.postMessage(queue[i], origin);
  }
};

window.remote = remote;

window.addEventListener && window.addEventListener('error', function (event) {
  remote.error({ message: event.message }, event.filename + ':' + event.lineno);
}, false);

try {
  window.console = remote;
} catch (e) {
  console.log('cannot overwrite existing console object');
}

function warnUsage() {
  var useSS = false;
  try {
    sessionStorage.getItem('foo');
    useSS = true;
  } catch (e) {}
  if (!(useSS ? sessionStorage.jsconsole : window.name)) {
    if (useSS) sessionStorage.jsconsole = 1; else window.name = 1;
    alert('You will see this warning once per session.\n\nYou are using a remote control script on this site - if you accidently push it to production, anyone will have control of your visitor\'s browser. Remember to remove this script.');
  }
}

warnUsage();

})();
<|endoftext|>