<commit_before>var hyperaudiolite = (function () {

  var hal = {},
    transcript,
    words,
    player,
    paraIndex;

  function init() {
    words = transcript.getElementsByTagName('a');
    paras = transcript.getElementsByTagName('p');
    player = document.getElementById("hyperplayer");
    paraIndex = 0;
    words[0].classList.add("active");
    paras[0].classList.add("active");
    transcript.addEventListener("click", setPlayHead, false);
    player.addEventListener("timeupdate", checkPlayHead, false);
  }

  function setPlayHead(e) {
    var target = (e.target) ? e.target : e.srcElement;
    target.setAttribute("class", "active");
    var timeSecs = parseInt(target.getAttribute("data-m"))/1000;

    if(!isNaN(parseFloat(timeSecs))) {
      player.currentTime = timeSecs;
      player.play();
    }
  }

  function checkPlayHead(e) {
    var activeitems = transcript.getElementsByClassName('active');
    var activeitemsLength = activeitems.length;

    for (var a = 0; a < activeitemsLength; a++) {
      if (activeitems[a]) { // TODO: look into why we need this
        activeitems[a].classList.remove("active");
      }
    }

    // Establish current paragraph index

    var currentParaIndex;

    for (i = 1; i < words.length; i++) {
      if (parseInt(words[i].getAttribute("data-m"))/1000 > player.currentTime) {

        // TODO: look for a better way of doing this
        var strayActive = transcript.getElementsByClassName('active')[0];
        strayActive.classList.remove("active");

        // word time is in the future - set the previous word as active.
        words[i-1].classList.add("active");
        words[i-1].parentNode.classList.add("active");

        paras = transcript.getElementsByTagName('p');

        for (a = 0; a < paras.length; a++) {

          if (paras[a].classList.contains("active")) {
            currentParaIndex = a;
            break;
          }
        }

        if (currentParaIndex != paraIndex) {

          Velocity(words[i].parentNode, "scroll", {
            /*container: transcript,*/
            duration: 800,
            delay: 0
          });

          paraIndex = currentParaIndex;
        }

        break;
      }
    }
  }

  hal.init = function() {
    transcript = document.getElementById("hypertranscript");
    init();
  }

  hal.loadTranscript = function(url) {
    var xmlhttp;

    if (window.XMLHttpRequest) {
      // code for IE7+, Firefox, Chrome, Opera, Safari
      xmlhttp = new XMLHttpRequest();
    } else {
      // code for IE6, IE5
      xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
    }

    xmlhttp.onreadystatechange = function() {
      if (xmlhttp.readyState == 4 ) {
        if(xmlhttp.status == 200){
          transcript = document.getElementById("hypertranscript");
          transcript.innerHTML = xmlhttp.responseText;
          init();
        }
        else if(xmlhttp.status == 400) {
          alert('There was an error 400')
        }
        else {
          alert('something else other than 200 was returned')
        }
      }
    }

    xmlhttp.open("GET", url, true);
    xmlhttp.send();
  }

  return hal;

})();
<commit_msg>included changes for playback point via URL<commit_after>var hyperaudiolite = (function () {

  var hal = {},
    transcript,
    words,
    player,
    paraIndex,
    start,
    end;

  function getParameter(name) {
    name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
    var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
        results = regex.exec(location.search);
    return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
  }

  function init() {
    words = transcript.getElementsByTagName('a');
    paras = transcript.getElementsByTagName('p');
    player = document.getElementById("hyperplayer");
    paraIndex = 0;
    words[0].classList.add("active");
    paras[0].classList.add("active");
    transcript.addEventListener("click", setPlayHead, false);
    player.addEventListener("timeupdate", checkPlayHead, false);

    //check for queryString params

    start = getParameter('s');

    if (!isNaN(parseFloat(start))) {
      player.currentTime = start/10;
      player.play();
    }

    end = parseFloat(getParameter('d')) + parseFloat(start);
  }

  function setPlayHead(e) {
    var target = (e.target) ? e.target : e.srcElement;
    target.setAttribute("class", "active");
    var timeSecs = parseInt(target.getAttribute("data-m"))/1000;

    if(!isNaN(parseFloat(timeSecs))) {
      player.currentTime = timeSecs;
      player.play();
    }
  }

  function checkPlayHead(e) {

    //check for end time of shared piece

    if (end && (end/10 < player.currentTime)) {
      player.pause();
    }

    var activeitems = transcript.getElementsByClassName('active');
    var activeitemsLength = activeitems.length;

    for (var a = 0; a < activeitemsLength; a++) {
      if (activeitems[a]) { // TODO: look into why we need this
        activeitems[a].classList.remove("active");
      }
    }

    // Establish current paragraph index

    var currentParaIndex;

    for (i = 1; i < words.length; i++) {
      if (parseInt(words[i].getAttribute("data-m"))/1000 > player.currentTime) {

        // TODO: look for a better way of doing this
        var strayActive = transcript.getElementsByClassName('active')[0];
        strayActive.classList.remove("active");

        // word time is in the future - set the previous word as active.
        words[i-1].classList.add("active");
        words[i-1].parentNode.classList.add("active");

        paras = transcript.getElementsByTagName('p');

        for (a = 0; a < paras.length; a++) {

          if (paras[a].classList.contains("active")) {
            currentParaIndex = a;
            break;
          }
        }

        if (currentParaIndex != paraIndex) {

          Velocity(words[i].parentNode, "scroll", {
            /*container: transcript,*/
            duration: 800,
            delay: 0
          });

          paraIndex = currentParaIndex;
        }

        break;
      }
    }
  }

  hal.init = function() {
    transcript = document.getElementById("hypertranscript");
    init();
  }

  hal.loadTranscript = function(url) {
    var xmlhttp;

    if (window.XMLHttpRequest) {
      // code for IE7+, Firefox, Chrome, Opera, Safari
      xmlhttp = new XMLHttpRequest();
    } else {
      // code for IE6, IE5
      xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
    }

    xmlhttp.onreadystatechange = function() {
      if (xmlhttp.readyState == 4 ) {
        if(xmlhttp.status == 200){
          transcript = document.getElementById("hypertranscript");
          transcript.innerHTML = xmlhttp.responseText;
          init();
        }
        else if(xmlhttp.status == 400) {
          alert('There was an error 400')
        }
        else {
          alert('something else other than 200 was returned')
        }
      }
    }

    xmlhttp.open("GET", url, true);
    xmlhttp.send();
  }

  return hal;

})();
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

Components.utils.import("chrome://htitle/content/HTitleShare.jsm");
Components.utils.import("chrome://htitle/content/HTitleUtils.jsm");

var HTitle = {
    ENABLED: true,

    window: null,

    currentMode: "auto",
    currentMethod: "xlib",
    isFirstStart: true,
    isStopped: true,
    previousState: 0,
    previousChangeTime: 0,

    init: function() {
        HTitleUtils.prefs.addObserver("", HTitle, false);

        if (HTitleUtils.prefs.getBoolPref("check_gnome_shell") && HTitleUtils.checkPresenceGnomeShell() != 0) {
            // Nothing doing if WM is not GNOME Shell.
            HTitle.ENABLED = false;
            return;
        }

        HTitle.window = window.document.documentElement;

        HTitle.start();

        if (typeof HTitleWindowControls != "undefined") {
            if (HTitleUtils.prefs.getBoolPref("window_controls.get_layout_by_gsettings"))
                HTitleWindowControls.setLayoutAttribute();

            if (HTitleUtils.prefs.getBoolPref("show_window_controls"))
                HTitleWindowControls.show();
        }
    },

    start: function() {
        HTitle.currentMode = (HTitleUtils.prefs.getIntPref("hide_mode") == 2) ? "always" : "auto";
        HTitle.currentMethod = (HTitleUtils.prefs.getBoolPref("legacy_mode.enable")) ? "hidechrome" : "xlib";

        if (HTitle.currentMethod == "xlib") {
            HTitleUtils.log("Start in normal mode", "DEBUG");
            var result = HTitleUtils.setWindowProperty(window, HTitle.currentMode);
            if (HTitle.isFirstStart && (HTitle.currentMode == "always" || HTitleShare.gtkVersion == 3) && result == 0) {
                // Really bad hack
                var timeouts = [100, 2*100, 3*100, 4*100, 10*100];
                for (let i = 0; i < timeouts.length; i++) {
                    setTimeout(function(){HTitleUtils.setWindowProperty(window, HTitle.currentMode);}, timeouts[i]);
                }
            }
            if (result == -1 && !HTitleShare.defaultMethodFailed) {
                HTitleShare.defaultMethodFailed = true;
                HTitle.currentMethod = "hidechrome";
                HTitleUtils.prefs.setBoolPref("legacy_mode.enable", true);
            }
        }

        if (HTitle.currentMethod == "hidechrome") {
            HTitleUtils.log("Start in legacy mode", "DEBUG");
            HTitleUtils.log("TIMEOUT_CHECK = " + HTitleUtils.timeoutCheck + "; TIMEOUT_BETWEEN_CHANGES = " + HTitleUtils.timeoutBetweenChanges, "DEBUG");
            window.addEventListener("sizemodechange", HTitle.onWindowStateChange);
            setTimeout(function(){HTitle.checkWindowState();}, HTitleUtils.timeoutCheck);
        }

        HTitle.window.setAttribute("htitlemode", HTitle.currentMode);
        HTitle.window.setAttribute("htitlemethod", HTitle.currentMethod);
        HTitle.isFirstStart = false;
        HTitle.isStopped = false;
    },

    stop: function() {
        if (HTitle.currentMethod == "xlib") {
            HTitleUtils.removeWindowProperty(window, HTitle.currentMode);
        }
        else {
            window.removeEventListener("sizemodechange", HTitle.onWindowStateChange);
            HTitle.window.setAttribute("hidechrome", false);
            HTitle.previousState = 0;
            HTitle.previousChangeTime = 0;
        }

        HTitle.window.removeAttribute("htitlemode");
        HTitle.window.removeAttribute("htitlemethod");
        HTitle.isStopped = true;
    },

    observe: function(subject, topic, data) {
        if (topic != "nsPref:changed")
            return;

        switch(data) {
            case "show_window_controls":
                if (typeof HTitleWindowControls == "undefined") {
                    break;
                }
                if (HTitleUtils.prefs.getBoolPref("show_window_controls")) {
                    HTitleUtils.log("Enable show close button", "DEBUG");
                    HTitleWindowControls.updatePosition(null);
                    HTitleWindowControls.show();
                }
                else {
                    HTitleUtils.log("Disable show close button", "DEBUG");
                    HTitleWindowControls.hide();
                }
                break;
            case "legacy_mode.enable":
                if (HTitle.ENABLED && !HTitleShare.defaultMethodFailed && !HTitle.isStopped) {
                    let wc = (typeof HTitleWindowControls != "undefined");
                    if (wc && HTitleUtils.prefs.getBoolPref("show_window_controls"))
                        HTitleWindowControls.hide();

                    HTitle.stop();
                    HTitleUtils.prefs.setIntPref("hide_mode", 1);
                    HTitle.start();

                    if (wc && HTitleUtils.prefs.getBoolPref("show_window_controls"))
                        HTitleWindowControls.show();
                }
                break;
            case "hide_mode":
                if (HTitle.ENABLED && !HTitle.isStopped) {
                    let wc = (typeof HTitleWindowControls != "undefined");
                    if (wc && HTitleUtils.prefs.getBoolPref("show_window_controls"))
                        HTitleWindowControls.hide();

                    HTitle.stop();
                    if (HTitleUtils.prefs.getIntPref("hide_mode") != 1)
                        HTitleUtils.prefs.setBoolPref("legacy_mode.enable", false);
                    HTitle.start();

                    if (wc && HTitleUtils.prefs.getBoolPref("show_window_controls"))
                        HTitleWindowControls.show();
                }
                break;
            case "check_gnome_shell":
                if (!HTitle.ENABLED && !HTitleUtils.prefs.getBoolPref("check_gnome_shell")) {
                    HTitle.ENABLED = true;
                    HTitle.start();
                }
                else if (HTitle.ENABLED && !HTitleUtils.prefs.getBoolPref("check_gnome_shell")) {
                    return;
                }
                else if (HTitleUtils.prefs.getBoolPref("check_gnome_shell") && HTitleUtils.checkPresenceGnomeShell() != 0 && HTitle.ENABLED) {
                    HTitle.ENABLED = false;
                    HTitle.stop();
                }
                break;
        }
    },

    onWindowStateChange: function(e) {
        if (HTitle.previousState == window.windowState || window.windowState == window.STATE_FULLSCREEN || window.windowState == window.STATE_MINIMIZED) {
            return;
        }

        if ((Date.now() - HTitle.previousChangeTime) < HTitleUtils.timeoutBetweenChanges) {
            if (window.windowState == window.STATE_NORMAL && HTitle.window.getAttribute("hidechrome"))
                 window.maximize();
            return;
        }

        HTitle.logWindowState("onWindowStateChange");

        HTitle.window.setAttribute("hidechrome", (window.windowState == window.STATE_MAXIMIZED));

        HTitle.previousState = window.windowState;
        HTitle.previousChangeTime = Date.now();
    },

    onClickTitlebar: function(e, window) {
        var e = e || window.event;
        if ("object" !== typeof e) {
            return;
        }
        var targets = ["window-controls", "minimize-button", "restore-button", "close-button"];
        if (targets.indexOf(e.target.id) != -1) {
            switch(e.button) {
                case 1:
                    switch(HTitleUtils.titlebarActions.middle) {
                        case "lower":
                            HTitleUtils.lowerWindow(window);
                            break;
                        case "minimize":
                            window.minimize();
                            break;
                    }
                    break;
            }
        }
        HTitleUtils.log("Detected click under #" + e.target.id, "DEBUG");
    },

    checkWindowState: function() {
        if (window.windowState == window.STATE_NORMAL) {
            HTitle.logWindowState("checkWindowState");
            HTitle.window.setAttribute("hidechrome", false);
            HTitle.previousState = window.STATE_NORMAL;
            HTitle.previousChangeTime = Date.now();
        }
        else if (window.windowState == window.STATE_MAXIMIZED) {
            HTitle.logWindowState("checkWindowState");
            HTitle.window.setAttribute("hidechrome", true);
            HTitle.previousState = window.STATE_MAXIMIZED;
            HTitle.previousChangeTime = Date.now();
        }
    },

    logWindowState: function(from) {
        if (HTitleShare.debug == false)
            return

        switch (window.windowState) {
            case window.STATE_MAXIMIZED:   var windowState = "maximized"; break;
            case window.STATE_NORMAL:      var windowState = "normal"; break;
            case window.STATE_FULLSCREEN:  var windowState = "fullscreen"; break;
            default: var windowState = window.windowState.toString();
        }

        HTitleUtils.log("Action = " + from + "; windowState = " + windowState + ";  hidechrome = " + HTitle.window.getAttribute("hidechrome"), "DEBUG");
    },

    shutdown: function() {
        HTitleUtils.prefs.removeObserver("", HTitle);
        //let wc = (typeof HTitleWindowControls != "undefined");
        //if (wc && HTitleUtils.prefs.getBoolPref("show_window_controls"))
        //    HTitleWindowControls.hide();
    },
}

window.addEventListener("load",   HTitle.init);
window.addEventListener("unload", HTitle.shutdown);
<commit_msg>Small fix in logging method<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

Components.utils.import("chrome://htitle/content/HTitleShare.jsm");
Components.utils.import("chrome://htitle/content/HTitleUtils.jsm");

var HTitle = {
    ENABLED: true,

    window: null,

    currentMode: "auto",
    currentMethod: "xlib",
    isFirstStart: true,
    isStopped: true,
    previousState: 0,
    previousChangeTime: 0,

    init: function() {
        HTitleUtils.prefs.addObserver("", HTitle, false);

        if (HTitleUtils.prefs.getBoolPref("check_gnome_shell") && HTitleUtils.checkPresenceGnomeShell() != 0) {
            // Nothing doing if WM is not GNOME Shell.
            HTitle.ENABLED = false;
            return;
        }

        HTitle.window = window.document.documentElement;

        HTitle.start();

        if (typeof HTitleWindowControls != "undefined") {
            if (HTitleUtils.prefs.getBoolPref("window_controls.get_layout_by_gsettings"))
                HTitleWindowControls.setLayoutAttribute();

            if (HTitleUtils.prefs.getBoolPref("show_window_controls"))
                HTitleWindowControls.show();
        }
    },

    start: function() {
        HTitle.currentMode = (HTitleUtils.prefs.getIntPref("hide_mode") == 2) ? "always" : "auto";
        HTitle.currentMethod = (HTitleUtils.prefs.getBoolPref("legacy_mode.enable")) ? "hidechrome" : "xlib";

        if (HTitle.currentMethod == "xlib") {
            HTitleUtils.log("Start in normal mode", "DEBUG");
            var result = HTitleUtils.setWindowProperty(window, HTitle.currentMode);
            if (HTitle.isFirstStart && (HTitle.currentMode == "always" || HTitleShare.gtkVersion == 3) && result == 0) {
                // Really bad hack
                var timeouts = [100, 2*100, 3*100, 4*100, 10*100];
                for (let i = 0; i < timeouts.length; i++) {
                    setTimeout(function(){HTitleUtils.setWindowProperty(window, HTitle.currentMode);}, timeouts[i]);
                }
            }
            if (result == -1 && !HTitleShare.defaultMethodFailed) {
                HTitleShare.defaultMethodFailed = true;
                HTitle.currentMethod = "hidechrome";
                HTitleUtils.prefs.setBoolPref("legacy_mode.enable", true);
            }
        }

        if (HTitle.currentMethod == "hidechrome") {
            HTitleUtils.log("Start in legacy mode", "DEBUG");
            HTitleUtils.log("TIMEOUT_CHECK = " + HTitleUtils.timeoutCheck + "; TIMEOUT_BETWEEN_CHANGES = " + HTitleUtils.timeoutBetweenChanges, "DEBUG");
            window.addEventListener("sizemodechange", HTitle.onWindowStateChange);
            setTimeout(function(){HTitle.checkWindowState();}, HTitleUtils.timeoutCheck);
        }

        HTitle.window.setAttribute("htitlemode", HTitle.currentMode);
        HTitle.window.setAttribute("htitlemethod", HTitle.currentMethod);
        HTitle.isFirstStart = false;
        HTitle.isStopped = false;
    },

    stop: function() {
        if (HTitle.currentMethod == "xlib") {
            HTitleUtils.removeWindowProperty(window, HTitle.currentMode);
        }
        else {
            window.removeEventListener("sizemodechange", HTitle.onWindowStateChange);
            HTitle.window.setAttribute("hidechrome", false);
            HTitle.previousState = 0;
            HTitle.previousChangeTime = 0;
        }

        HTitle.window.removeAttribute("htitlemode");
        HTitle.window.removeAttribute("htitlemethod");
        HTitle.isStopped = true;
    },

    observe: function(subject, topic, data) {
        if (topic != "nsPref:changed")
            return;

        switch(data) {
            case "show_window_controls":
                if (typeof HTitleWindowControls == "undefined") {
                    break;
                }
                if (HTitleUtils.prefs.getBoolPref("show_window_controls")) {
                    HTitleUtils.log("Enable show close button", "DEBUG");
                    HTitleWindowControls.updatePosition(null);
                    HTitleWindowControls.show();
                }
                else {
                    HTitleUtils.log("Disable show close button", "DEBUG");
                    HTitleWindowControls.hide();
                }
                break;
            case "legacy_mode.enable":
                if (HTitle.ENABLED && !HTitleShare.defaultMethodFailed && !HTitle.isStopped) {
                    let wc = (typeof HTitleWindowControls != "undefined");
                    if (wc && HTitleUtils.prefs.getBoolPref("show_window_controls"))
                        HTitleWindowControls.hide();

                    HTitle.stop();
                    HTitleUtils.prefs.setIntPref("hide_mode", 1);
                    HTitle.start();

                    if (wc && HTitleUtils.prefs.getBoolPref("show_window_controls"))
                        HTitleWindowControls.show();
                }
                break;
            case "hide_mode":
                if (HTitle.ENABLED && !HTitle.isStopped) {
                    let wc = (typeof HTitleWindowControls != "undefined");
                    if (wc && HTitleUtils.prefs.getBoolPref("show_window_controls"))
                        HTitleWindowControls.hide();

                    HTitle.stop();
                    if (HTitleUtils.prefs.getIntPref("hide_mode") != 1)
                        HTitleUtils.prefs.setBoolPref("legacy_mode.enable", false);
                    HTitle.start();

                    if (wc && HTitleUtils.prefs.getBoolPref("show_window_controls"))
                        HTitleWindowControls.show();
                }
                break;
            case "check_gnome_shell":
                if (!HTitle.ENABLED && !HTitleUtils.prefs.getBoolPref("check_gnome_shell")) {
                    HTitle.ENABLED = true;
                    HTitle.start();
                }
                else if (HTitle.ENABLED && !HTitleUtils.prefs.getBoolPref("check_gnome_shell")) {
                    return;
                }
                else if (HTitleUtils.prefs.getBoolPref("check_gnome_shell") && HTitleUtils.checkPresenceGnomeShell() != 0 && HTitle.ENABLED) {
                    HTitle.ENABLED = false;
                    HTitle.stop();
                }
                break;
        }
    },

    onWindowStateChange: function(e) {
        if (HTitle.previousState == window.windowState || window.windowState == window.STATE_FULLSCREEN || window.windowState == window.STATE_MINIMIZED) {
            return;
        }

        if ((Date.now() - HTitle.previousChangeTime) < HTitleUtils.timeoutBetweenChanges) {
            if (window.windowState == window.STATE_NORMAL && HTitle.window.getAttribute("hidechrome"))
                 window.maximize();
            return;
        }

        HTitle.logWindowState("onWindowStateChange");

        HTitle.window.setAttribute("hidechrome", (window.windowState == window.STATE_MAXIMIZED));

        HTitle.previousState = window.windowState;
        HTitle.previousChangeTime = Date.now();
    },

    onClickTitlebar: function(e, window) {
        var e = e || window.event;
        if ("object" !== typeof e) {
            return;
        }
        var targets = ["window-controls", "minimize-button", "restore-button", "close-button"];
        if (targets.indexOf(e.target.id) != -1) {
            switch(e.button) {
                case 1:
                    switch(HTitleUtils.titlebarActions.middle) {
                        case "lower":
                            HTitleUtils.lowerWindow(window);
                            break;
                        case "minimize":
                            window.minimize();
                            break;
                    }
                    break;
            }
        }
        HTitleUtils.log("Detected click under #" + e.target.id, "DEBUG");
    },

    checkWindowState: function() {
        if (window.windowState == window.STATE_NORMAL) {
            HTitle.logWindowState("checkWindowState");
            HTitle.window.setAttribute("hidechrome", false);
            HTitle.previousState = window.STATE_NORMAL;
            HTitle.previousChangeTime = Date.now();
        }
        else if (window.windowState == window.STATE_MAXIMIZED) {
            HTitle.logWindowState("checkWindowState");
            HTitle.window.setAttribute("hidechrome", true);
            HTitle.previousState = window.STATE_MAXIMIZED;
            HTitle.previousChangeTime = Date.now();
        }
    },

    logWindowState: function(from) {
        if (HTitleShare.debug == false)
            return;

        switch (window.windowState) {
            case window.STATE_MAXIMIZED:  var windowState = "maximized"; break;
            case window.STATE_NORMAL:     var windowState = "normal"; break;
            case window.STATE_FULLSCREEN: var windowState = "fullscreen"; break;
            default:                      var windowState = window.windowState.toString();
        }

        HTitleUtils.log("Action = " + from + "; windowState = " + windowState + ";  hidechrome = " + HTitle.window.getAttribute("hidechrome"), "DEBUG");
    },

    shutdown: function() {
        HTitleUtils.prefs.removeObserver("", HTitle);
        //let wc = (typeof HTitleWindowControls != "undefined");
        //if (wc && HTitleUtils.prefs.getBoolPref("show_window_controls"))
        //    HTitleWindowControls.hide();
    },
}

window.addEventListener("load",   HTitle.init);
window.addEventListener("unload", HTitle.shutdown);
<|endoftext|>


<commit_before>/**
 *  Copyright 2014 IBM Corp. All Rights Reserved
 *
 *  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 passportStrategy = require('passport-strategy');
var util = require('util');
var logger = require('log4js').getLogger('mca-website-strategy');
var request = require('request');

var FilterUtil = require('./util/filter-util');
var ERRORS = require("./Errors");

function Strategy(options) {
	options = options || {};
	var mcaCredentials = FilterUtil.getMcaServiceCredentials();

	this.clientId = (options.clientId) ? options.clientId : mcaCredentials.clientId;
	this.secret = (options.secret) ? options.secret : mcaCredentials.secret;
	this.authorizationEndpoint = (options.authorizationEndpoint) ? options.authorizationEndpoint : mcaCredentials.authorizationEndpoint;
	this.tokenEndpoint = (options.tokenEndpoint) ? options.tokenEndpoint : mcaCredentials.tokenEndpoint;
	this.callbackUrl = options.callbackUrl;

	logger.debug("clientiD", this.clientId);
	logger.debug("secret", this.secret.substr(0, 1) + "............................" + this.secret.substr(this.secret.length - 1));
	logger.debug("authorizationEndpoint", this.authorizationEndpoint);
	logger.debug("tokenEndpoint", this.tokenEndpoint);
	logger.debug("callbackUrl", this.callbackUrl);

	if (typeof(this.clientId) !== "string" ||
		typeof(this.secret) !== "string" ||
		typeof(this.authorizationEndpoint) !== "string" ||
		typeof(this.tokenEndpoint) !== "string" ||
		typeof(this.callbackUrl) !== "string") {
		throw new TypeError(ERRORS.OPTIONS_OBJECT_MISSING_PROPERTIES);
	}

	this.name = Strategy.NAME;
}

util.inherits(Strategy, passportStrategy.Strategy);

Strategy.prototype.authenticate = function (req, options) {
	logger.debug("authenticate", req.url, ", user :: ", req.user);
	options = options || {};

	// Handle possible error
	if (req.query && req.query.error) {
		logger.error(req.query.error);
		logger.error(req.query.error_description);
		return this.fail({message: req.query.error_description});
	}

	// Handle oauth callback
	if (options.isOAuthCallback) {
		return retrieveAccessToken(req, this, options);
	}

	// Check for authentication
	if (req.user) {
		logger.debug("Request already authenticated");
		return this.pass();
	}

	// Redirect to authorization endpoint
	logger.debug("Request not authenticated, redirecting to authorizationEndpoint");
	var redirectUrl = this.authorizationEndpoint;
	redirectUrl += "?response_type=code";
	redirectUrl += "&client_id=" + this.clientId;
	redirectUrl += "&redirect_uri=" + this.callbackUrl;
	req.session.originalUrl = req.url;
	this.redirect(redirectUrl);
};

function retrieveAccessToken(req, strategy) {
	logger.debug("retrieveAccessToken");

	var formData = {
		grant_type: "code",
		client_id: strategy.clientId,
		redirect_uri: strategy.callbackUrl,
		code: req.query.code
	};

	request.post({
		url: strategy.tokenEndpoint,
		form: formData
	}, function (err, response, body) {
		if (err) {
			logger.error(err);
			return strategy.fail();
		}

		if (response && response.statusCode == 200 && body) {
			logger.debug("Got tokens, parsing", body);
			var parsedBody = JSON.parse(body);
			req.session = req.session || {};
			req.session.accessToken = parsedBody.access_token;
			req.session.idToken = parsedBody.id_token;
			var idTokenComponents = parsedBody.id_token.split("."); // [header, payload, signature]
			var decodedIdentity = new Buffer(idTokenComponents[1], "base64").toString();
			req.securityContext = JSON.parse(decodedIdentity);
			req.session.securityContext = JSON.parse(decodedIdentity);
			return strategy.success(req.securityContext);
		}

		logger.error("Failed to get tokens", body);
		return strategy.fail();
	}).auth(strategy.clientId, strategy.secret);
}

Strategy.MCA_WEBSITE_STRATEGY = "mca-website-strategy";
Strategy.NAME = Strategy.MCA_WEBSITE_STRATEGY;
Strategy.MCA_LOGIN_URL = "/" + Strategy.MCA_WEBSITE_STRATEGY + "/oauth/login";
Strategy.setup = function (passport) {
	passport.serializeUser(function (user, done) {
		done(null, user || {});
	});

	passport.deserializeUser(function (user, done) {
		done(null, user);
	});
};

exports = module.exports = Strategy;<commit_msg>change 'code' to 'authorization_code' https://github.com/ibm-bluemix-mobile-services/bms-mca-token-validation-strategy/issues/7<commit_after>/**
 *  Copyright 2014 IBM Corp. All Rights Reserved
 *
 *  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 passportStrategy = require('passport-strategy');
var util = require('util');
var logger = require('log4js').getLogger('mca-website-strategy');
var request = require('request');

var FilterUtil = require('./util/filter-util');
var ERRORS = require("./Errors");

function Strategy(options) {
	options = options || {};
	var mcaCredentials = FilterUtil.getMcaServiceCredentials();

	this.clientId = (options.clientId) ? options.clientId : mcaCredentials.clientId;
	this.secret = (options.secret) ? options.secret : mcaCredentials.secret;
	this.authorizationEndpoint = (options.authorizationEndpoint) ? options.authorizationEndpoint : mcaCredentials.authorizationEndpoint;
	this.tokenEndpoint = (options.tokenEndpoint) ? options.tokenEndpoint : mcaCredentials.tokenEndpoint;
	this.callbackUrl = options.callbackUrl;

	logger.debug("clientiD", this.clientId);
	logger.debug("secret", this.secret.substr(0, 1) + "............................" + this.secret.substr(this.secret.length - 1));
	logger.debug("authorizationEndpoint", this.authorizationEndpoint);
	logger.debug("tokenEndpoint", this.tokenEndpoint);
	logger.debug("callbackUrl", this.callbackUrl);

	if (typeof(this.clientId) !== "string" ||
		typeof(this.secret) !== "string" ||
		typeof(this.authorizationEndpoint) !== "string" ||
		typeof(this.tokenEndpoint) !== "string" ||
		typeof(this.callbackUrl) !== "string") {
		throw new TypeError(ERRORS.OPTIONS_OBJECT_MISSING_PROPERTIES);
	}

	this.name = Strategy.NAME;
}

util.inherits(Strategy, passportStrategy.Strategy);

Strategy.prototype.authenticate = function (req, options) {
	logger.debug("authenticate", req.url, ", user :: ", req.user);
	options = options || {};

	// Handle possible error
	if (req.query && req.query.error) {
		logger.error(req.query.error);
		logger.error(req.query.error_description);
		return this.fail({message: req.query.error_description});
	}

	// Handle oauth callback
	if (options.isOAuthCallback) {
		return retrieveAccessToken(req, this, options);
	}

	// Check for authentication
	if (req.user) {
		logger.debug("Request already authenticated");
		return this.pass();
	}

	// Redirect to authorization endpoint
	logger.debug("Request not authenticated, redirecting to authorizationEndpoint");
	var redirectUrl = this.authorizationEndpoint;
	redirectUrl += "?response_type=code";
	redirectUrl += "&client_id=" + this.clientId;
	redirectUrl += "&redirect_uri=" + this.callbackUrl;
	req.session.originalUrl = req.url;
	this.redirect(redirectUrl);
};

function retrieveAccessToken(req, strategy) {
	logger.debug("retrieveAccessToken");

	var formData = {
		grant_type: "authorization_code",
		client_id: strategy.clientId,
		redirect_uri: strategy.callbackUrl,
		code: req.query.code
	};

	request.post({
		url: strategy.tokenEndpoint,
		form: formData
	}, function (err, response, body) {
		if (err) {
			logger.error(err);
			return strategy.fail();
		}

		if (response && response.statusCode == 200 && body) {
			logger.debug("Got tokens, parsing", body);
			var parsedBody = JSON.parse(body);
			req.session = req.session || {};
			req.session.accessToken = parsedBody.access_token;
			req.session.idToken = parsedBody.id_token;
			var idTokenComponents = parsedBody.id_token.split("."); // [header, payload, signature]
			var decodedIdentity = new Buffer(idTokenComponents[1], "base64").toString();
			req.securityContext = JSON.parse(decodedIdentity);
			req.session.securityContext = JSON.parse(decodedIdentity);
			return strategy.success(req.securityContext);
		}

		logger.error("Failed to get tokens", body);
		return strategy.fail();
	}).auth(strategy.clientId, strategy.secret);
}

Strategy.MCA_WEBSITE_STRATEGY = "mca-website-strategy";
Strategy.NAME = Strategy.MCA_WEBSITE_STRATEGY;
Strategy.MCA_LOGIN_URL = "/" + Strategy.MCA_WEBSITE_STRATEGY + "/oauth/login";
Strategy.setup = function (passport) {
	passport.serializeUser(function (user, done) {
		done(null, user || {});
	});

	passport.deserializeUser(function (user, done) {
		done(null, user);
	});
};

exports = module.exports = Strategy;<|endoftext|>


<commit_before>define(function (require) {

  // This generates a handler function for each route
  // This is where all the State lifecycle management happens

  // each handler is an everliving object, which can store
  // a reference to a context (in router.js language) or
  // a state (in cherrytree language). The state is where
  // the users of cherrytree libraries do interesting things
  // like fetching data, creating views and nesting them.

  // So a route, has an everliving handler which manages
  // a state that can be instantiated with params, activated,
  // and destroyed when handler becomes inactive

  /**
    TODO
    * look into not having a state thing, but just using
      handlers as they're meant to be used, to avoid memory
      leaks, simply return context from model, if the handler is
      never activated, the context won't be saved, etc.
  */

  var _ = require("underscore");
  var RSVP = require("rsvp");
  var BaseState = require("./state");

  return function getHandlerFunction(router) {
    var seen = {};
    var stateClasses = router.stateClasses;
    var prepares = router.prepares;
    var preparesCalled = {};

    var currentHandlers = [];
    var abandonedStates = [];

    return function(name) {
      var handler;

      // if (name === 'application') {
      //   // Inject default `routeTo` handler.
      //   handler.events = handler.events || {};
      //   handler.events.routeTo = handler.events.routeTo || Ember.TransitionEvent.defaultHandler;
      // }

      if (seen[name]) { return seen[name]; }

      // if (_.isObject(states[name])) {
      //   handler = states[name];
      //   handler.routeName = name;
      //   seen[name] = handler;
      //   return handler;
      // }

      if (name === "loading" && !stateClasses["loading"]) {
        handler = {};
        seen[name] = handler;
        return handler;
      }

      var lastParams, lastQueryParams, state, oldState;

      function destroyState(state) {
        // console.log("cherry:", state.name, ":", "destroying", (state || {}).id);
        state.destroy();
        state._destroyed = true;

        // check if we're in the right closure
        // TODO: the closure stuff seems quite tricky here
        // not clear what's happening in different situations, perhaps it's better
        // to store all these infos on an object instead of in a closure
        if (state.name === name) {
          state = null;
          lastParams = null;
          lastQueryParams = null;
        }
      }

      handler = {
        serialize: function (params) {
          // console.log("serializing", name, params);
          if (params === state) {
            return lastParams || {};
          } else {
            // normally we're not passing models to
            // generate/transitionTo/replaceWith, etc.
            // we simply pass in an object with params,
            // therefore in those cases we just want to
            // return whatever we passed in
            // TODO routing: perhaps this should be deprecated
            // perhaps we should only allow using the new syntax
            // this.transitionTo("foo.bar", 32, "b");
            // instead of
            // this.transitionTo("foo.bar", {
            //   param1: 32,
            //   param2: "b"
            // })
            return params;
          }
        },
        beforeModel: function (queryParams, transition) {
          if (transition === undefined) {
            transition = queryParams;
            queryParams = false;
          }

          // keep clearing the providedModels object,
          // we currently aren't using this functionality where
          // an instance of the model can passed in transitionTo calls, etc.
          // therefore we never want router.js to think that some models
          // are provided, we always wanna be calling the model method in
          // this handler, that's where we decide ourselves if the state
          // should be reinstantiated or not, etc.
          // We could probably still pass in real instances via transitionTo
          // and handle that in the model function - params would contain
          // those instances that we could possible pass on to the states to consume
          // etc.
          transition.providedModels = {};
          transition.providedModelsArray = [];

          // clean up in case we didn't have a chance to cleanup before
          // that happens when we transition while transitioning, which means
          // we abandon some states and even though we've destroyed them, we
          // were in the wrong closure to clean up the closure variables...
          if (state && state._destroyed) {
            state = null;
            lastParams = null;
            lastQueryParams = null;
          }
        },
        model: function (params, queryParams, transition) {

          if (transition === undefined) {
            transition = queryParams;
            queryParams = false;
          }

          // console.log("cherry:", name, ":", "model", (state || {}).id);

          if (name === "application") {
            // reset the currentHandlers
            currentHandlers = [handler];
          } else if (name !== "loading") {
            // extend the currentHandlers
            currentHandlers.push(handler);
          }

          // normalize params
          if (_.isEmpty(params)) {
            params = false;
          }
          if (_.isEmpty(queryParams)) {
            queryParams = false;
          }

          // if params didn't change - we keep this state
          if (_.isEqual(lastParams, params) && _.isEqual(lastQueryParams, queryParams)) {
            return state;
          }

          // keep a record of the new params
          lastParams = _.clone(params);
          lastQueryParams = _.clone(queryParams);

          // if the params changed - call an optional update
          // method on the state - return value false,
          // prevents the desctruction of the state and proceeds
          // with the transition. Otherwise we will destroy this
          // state and recreate it
          if ((params || queryParams) && state && state.update) {
            if (state.update(params, queryParams) === false) {
              return state;
            }
          }

          function createState(State) {
            oldState = state;
            // console.log("cherry:", name, ":", "createState", (state || {}).id, "with params", params);
            state = new State(name, _.extend(params || {}, {
              router: router,
              queryParams: queryParams || {}
            }));

            // need to set parent here..?
            if (transition) {
              transition.then(function () {
                // TODO what should we do with the abandoned states?
                _.each(abandonedStates, function (state) {
                  if (!state._activated) {
                    destroyState(state);
                  }
                });
                abandonedStates = [];
              }, function () {
                // in case the transition was aborted, we might need to
                // destroy this state, after the transition has settled in
                // currently there is no way to call into
                if (!state._activated && transition.isAborted) {
                  abandonedStates.push(state);
                }
              });
              var parentState = transition.data.parentState;
              if (!parentState) {
                // var parentName = name.split(".");
                // parentName.pop();
                // parentName = parentName.join(".");
                // if (!parentName) {
                //   parentName = "application";
                // }
                // parentState = transition.resolvedModels[parentName];
                // var resolvedModels = _.values(transition.resolvedModels);
                var leafState = _.find(transition.resolvedModels, function (leafState) {
                  // it's a leafState only if every other state is not pointing to it
                  return _.every(transition.resolvedModels, function (state) {
                    return state.parent !== leafState;
                  });
                });
                parentState = leafState;
              }
              state.setParent(parentState);
              transition.data.parentState = state;

            }
            var modelPromise = state.model();

            var whenModelResolved = function () {
              if (state.shouldActivate) {
                // destroy everything in the currentHandlerInfos down to the
                // match point. then we can
                // TODO: what if the transition is later aborted, I guess
                // we're fucked? but aborting would usually mean a failur
                // which you want to handle, not just stop transitioning...
                // aborting otherwise should be done before the deserialzie
                // is even called
                state.activate();
                // console.log("ACTIVATING", name);
                state._activated = true;
              }
              return state;
            };
            if (modelPromise && modelPromise.then) {
              return RSVP.resolve(modelPromise).then(whenModelResolved);
            } else {
              whenModelResolved();
              return state;
            }
          }

          var State;
          // if we don't have a prepare method for this state
          // or if it's already been called - proceed with creating
          // the state
          if (!prepares[name] || preparesCalled[name]) {
            State = stateClasses[name] || router.BaseState || BaseState;
            if (State) {
              return createState(State);
            }
          } else {
            var promise = new RSVP.Promise(function(resolve, reject){
              // TODO routing: once a prepare has been called, we should remember
              // that and not call it again in the future
              prepares[name](router, function () {
                // record that this prepare has been called - we only
                // do this per the lifetime of the application as it's
                // mostly intended for loading extra code
                preparesCalled[name] = true;

                // now that we gave the prepare method a chance to preload the states
                State = stateClasses[name];
                if (State) {
                  resolve(createState(State));
                } else {
                  reject(new Error("called the prepare, but that didn't provide the state for route", name));
                }
              });
            });
            return promise;
          }
        },

        afterModel: function (state, queryParams, transition) {
          if (transition === undefined) {
            transition = queryParams;
            queryParams = false;
          }
          // console.log("cherry:", name, ":", "afterModel", (state || {}).id);
          if (state) {
            // update the transition's parentState
            // transition.data.parentState = state;
            if (state.afterModel) {
              state.afterModel(transition);
            }
          }
        },
        setup: function () {
          // console.log("cherry:", name, ":", "setup", (state || {}).id);
          if (state) {
            if (!state._activated) {
              state.activate();
              state._activated = true;
            }
          }
          // now that we've activated the new state,
          // we can destroy the old one
          if (oldState) {
            // console.log("cherry:", name, ":", "destroying old state", (oldState || {}).id);
            destroyState(oldState);
          }
        },
        exit: function () {
          // console.log("cherry:", name, ":", "exit", (state || {}).id);
          if (state) {
            destroyState(state);
          }
        },
        events: {
          willTransition: function (transition) {
            // TODO if transition is aborted - should we
            // still be popping the handler?
            currentHandlers.pop();
            if (state && state.willTransition) {
              return state.willTransition(transition);
            } else {
              return true;
            }
          },
          error: function (err, transition) {
            if (state && state.error) {
              // console.error("ERROR IN STATE", name, e);
              return state.error(err, transition);
            } else {
              return true;
            }
          }
        }
      };

      handler.routeName = name;
      seen[name] = handler;

      return handler;
    };
  };
});<commit_msg>Remove the oldState bookkeeping - it's not actually needed.<commit_after>define(function (require) {

  // This generates a handler function for each route
  // This is where all the State lifecycle management happens

  // each handler is an everliving object, which can store
  // a reference to a context (in router.js language) or
  // a state (in cherrytree language). The state is where
  // the users of cherrytree libraries do interesting things
  // like fetching data, creating views and nesting them.

  // So a route, has an everliving handler which manages
  // a state that can be instantiated with params, activated,
  // and destroyed when handler becomes inactive

  /**
    TODO
    * look into not having a state thing, but just using
      handlers as they're meant to be used, to avoid memory
      leaks, simply return context from model, if the handler is
      never activated, the context won't be saved, etc.
  */

  var _ = require("underscore");
  var RSVP = require("rsvp");
  var BaseState = require("./state");

  return function getHandlerFunction(router) {
    var seen = {};
    var stateClasses = router.stateClasses;
    var prepares = router.prepares;
    var preparesCalled = {};

    var currentHandlers = [];
    var abandonedStates = [];

    return function(name) {
      var handler;

      // if (name === 'application') {
      //   // Inject default `routeTo` handler.
      //   handler.events = handler.events || {};
      //   handler.events.routeTo = handler.events.routeTo || Ember.TransitionEvent.defaultHandler;
      // }

      if (seen[name]) { return seen[name]; }

      // if (_.isObject(states[name])) {
      //   handler = states[name];
      //   handler.routeName = name;
      //   seen[name] = handler;
      //   return handler;
      // }

      if (name === "loading" && !stateClasses["loading"]) {
        handler = {};
        seen[name] = handler;
        return handler;
      }

      var lastParams, lastQueryParams, state;

      function destroyState(state) {
        // console.log("cherry:", state.name, ":", "destroying", (state || {}).id);
        state.destroy();
        state._destroyed = true;

        // check if we're in the right closure
        // TODO: the closure stuff seems quite tricky here
        // not clear what's happening in different situations, perhaps it's better
        // to store all these infos on an object instead of in a closure
        if (state.name === name) {
          state = null;
          lastParams = null;
          lastQueryParams = null;
        }
      }

      handler = {
        serialize: function (params) {
          // console.log("serializing", name, params);
          if (params === state) {
            return lastParams || {};
          } else {
            // normally we're not passing models to
            // generate/transitionTo/replaceWith, etc.
            // we simply pass in an object with params,
            // therefore in those cases we just want to
            // return whatever we passed in
            // TODO routing: perhaps this should be deprecated
            // perhaps we should only allow using the new syntax
            // this.transitionTo("foo.bar", 32, "b");
            // instead of
            // this.transitionTo("foo.bar", {
            //   param1: 32,
            //   param2: "b"
            // })
            return params;
          }
        },
        beforeModel: function (queryParams, transition) {
          if (transition === undefined) {
            transition = queryParams;
            queryParams = false;
          }

          // keep clearing the providedModels object,
          // we currently aren't using this functionality where
          // an instance of the model can passed in transitionTo calls, etc.
          // therefore we never want router.js to think that some models
          // are provided, we always wanna be calling the model method in
          // this handler, that's where we decide ourselves if the state
          // should be reinstantiated or not, etc.
          // We could probably still pass in real instances via transitionTo
          // and handle that in the model function - params would contain
          // those instances that we could possible pass on to the states to consume
          // etc.
          transition.providedModels = {};
          transition.providedModelsArray = [];

          // clean up in case we didn't have a chance to cleanup before
          // that happens when we transition while transitioning, which means
          // we abandon some states and even though we've destroyed them, we
          // were in the wrong closure to clean up the closure variables...
          if (state && state._destroyed) {
            state = null;
            lastParams = null;
            lastQueryParams = null;
          }
        },
        model: function (params, queryParams, transition) {

          if (transition === undefined) {
            transition = queryParams;
            queryParams = false;
          }

          // console.log("cherry:", name, ":", "model", (state || {}).id);

          if (name === "application") {
            // reset the currentHandlers
            currentHandlers = [handler];
          } else if (name !== "loading") {
            // extend the currentHandlers
            currentHandlers.push(handler);
          }

          // normalize params
          if (_.isEmpty(params)) {
            params = false;
          }
          if (_.isEmpty(queryParams)) {
            queryParams = false;
          }

          // if params didn't change - we keep this state
          if (_.isEqual(lastParams, params) && _.isEqual(lastQueryParams, queryParams)) {
            return state;
          }

          // keep a record of the new params
          lastParams = _.clone(params);
          lastQueryParams = _.clone(queryParams);

          // if the params changed - call an optional update
          // method on the state - return value false,
          // prevents the desctruction of the state and proceeds
          // with the transition. Otherwise we will destroy this
          // state and recreate it
          if ((params || queryParams) && state && state.update) {
            if (state.update(params, queryParams) === false) {
              return state;
            }
          }

          function createState(State) {
            // console.log("cherry:", name, ":", "createState", (state || {}).id, "with params", params);
            state = new State(name, _.extend(params || {}, {
              router: router,
              queryParams: queryParams || {}
            }));

            // need to set parent here..?
            if (transition) {
              transition.then(function () {
                // TODO what should we do with the abandoned states?
                _.each(abandonedStates, function (state) {
                  if (!state._activated) {
                    destroyState(state);
                  }
                });
                abandonedStates = [];
              }, function () {
                // in case the transition was aborted, we might need to
                // destroy this state, after the transition has settled in
                // currently there is no way to call into
                if (!state._activated && transition.isAborted) {
                  abandonedStates.push(state);
                }
              });
              var parentState = transition.data.parentState;
              if (!parentState) {
                // var parentName = name.split(".");
                // parentName.pop();
                // parentName = parentName.join(".");
                // if (!parentName) {
                //   parentName = "application";
                // }
                // parentState = transition.resolvedModels[parentName];
                // var resolvedModels = _.values(transition.resolvedModels);
                var leafState = _.find(transition.resolvedModels, function (leafState) {
                  // it's a leafState only if every other state is not pointing to it
                  return _.every(transition.resolvedModels, function (state) {
                    return state.parent !== leafState;
                  });
                });
                parentState = leafState;
              }
              state.setParent(parentState);
              transition.data.parentState = state;

            }
            var modelPromise = state.model();

            var whenModelResolved = function () {
              if (state.shouldActivate) {
                // destroy everything in the currentHandlerInfos down to the
                // match point. then we can
                // TODO: what if the transition is later aborted, I guess
                // we're fucked? but aborting would usually mean a failur
                // which you want to handle, not just stop transitioning...
                // aborting otherwise should be done before the deserialzie
                // is even called
                state.activate();
                // console.log("ACTIVATING", name);
                state._activated = true;
              }
              return state;
            };
            if (modelPromise && modelPromise.then) {
              return RSVP.resolve(modelPromise).then(whenModelResolved);
            } else {
              whenModelResolved();
              return state;
            }
          }

          var State;
          // if we don't have a prepare method for this state
          // or if it's already been called - proceed with creating
          // the state
          if (!prepares[name] || preparesCalled[name]) {
            State = stateClasses[name] || router.BaseState || BaseState;
            if (State) {
              return createState(State);
            }
          } else {
            var promise = new RSVP.Promise(function(resolve, reject){
              // TODO routing: once a prepare has been called, we should remember
              // that and not call it again in the future
              prepares[name](router, function () {
                // record that this prepare has been called - we only
                // do this per the lifetime of the application as it's
                // mostly intended for loading extra code
                preparesCalled[name] = true;

                // now that we gave the prepare method a chance to preload the states
                State = stateClasses[name];
                if (State) {
                  resolve(createState(State));
                } else {
                  reject(new Error("called the prepare, but that didn't provide the state for route", name));
                }
              });
            });
            return promise;
          }
        },

        afterModel: function (state, queryParams, transition) {
          if (transition === undefined) {
            transition = queryParams;
            queryParams = false;
          }
          // console.log("cherry:", name, ":", "afterModel", (state || {}).id);
          if (state) {
            // update the transition's parentState
            // transition.data.parentState = state;
            if (state.afterModel) {
              state.afterModel(transition);
            }
          }
        },
        setup: function () {
          // console.log("cherry:", name, ":", "setup", (state || {}).id);
          if (state) {
            if (!state._activated) {
              state.activate();
              state._activated = true;
            }
          }
        },
        exit: function () {
          // console.log("cherry:", name, ":", "exit", (state || {}).id);
          if (state) {
            destroyState(state);
          }
        },
        events: {
          willTransition: function (transition) {
            // TODO if transition is aborted - should we
            // still be popping the handler?
            currentHandlers.pop();
            if (state && state.willTransition) {
              return state.willTransition(transition);
            } else {
              return true;
            }
          },
          error: function (err, transition) {
            if (state && state.error) {
              // console.error("ERROR IN STATE", name, e);
              return state.error(err, transition);
            } else {
              return true;
            }
          }
        }
      };

      handler.routeName = name;
      seen[name] = handler;

      return handler;
    };
  };
});<|endoftext|>


<commit_before>import { expect } from 'chai';

import { mockStore } from '_test/utils/renderers';
// uncomment following for data to be used when testing update or delete
// import data from '_test/utils/data';
import nock from 'nock';

import {
  getAllProjects,
  addProject,
  ALL_PROJECTS,
  ADD_PROJECT,
} from '_store/projects/actions.js';

const SERVER = `${HOST}:${PORT}`;
const API = `${REST_API_PATH}/projects/`;

describe('projects actions', () => {
  describe('action creators', () => {
    let store;
    beforeEach(() => {
      store = mockStore({ projects: {} });
    });
    afterEach(() => {
      nock.cleanAll();
    });
    describe('getAllProjects', () => {
      it('standard reply', done => {
        const body = [
          { pid: 25, name: 'Writing a Book on Web Dev Tools' },
          { pid: 34, name: 'Cook a Spanish omelette' },
        ];
        nock(SERVER)
          .get(API)
          .query({ fields: 'pid,name,pending' })
          .reply(200, body);

        store.dispatch(getAllProjects())
          .then(() => {
            expect(store.getActions()).to.eql(
              [
                {
                  type: ALL_PROJECTS,
                  meta: { request: true },
                  payload: {},
                },
                {
                  type: ALL_PROJECTS,
                  payload: body,
                },
              ]);
          })
          .then(done)
          .catch(done);
      });
      it('error reply', done => {
        nock(SERVER)
          .get(API)
          .query({ fields: 'pid,name,pending' })
          .reply(404, 'Not found');

        store.dispatch(getAllProjects())
          .then(() => {
            const acts = store.getActions();
            expect(acts[0]).to.eql({
              type: ALL_PROJECTS,
              meta: { request: true },
              payload: {},
            });
            expect(acts[1].type).to.equal(ALL_PROJECTS);
            expect(acts[1].error).to.be.true;
            expect(acts[1].payload.status).to.equal(404);
            expect(acts[1].payload).to.have.all.keys('status', 'action', 'url');
            // url: contents might change in future releases
            // msg: nock will always return null, see: https://github.com/node-nock/nock/issues/469
          })
          .then(done)
          .catch(done);
      });
    });

    describe('addProject', () => {
      it('standard request', done => {
        nock(SERVER)
          .post(API)
          .reply(200, { pid: '45' });

        store.dispatch(addProject('name', 'descr'))
          .then(() => {
            expect(store.getActions()).to.eql(
              [
                {
                  type: ADD_PROJECT,
                  payload: {
                    name: 'name',
                    descr: 'descr',
                  },
                  meta: { request: true },
                },
                {
                  type: ADD_PROJECT,
                  payload: { pid: '45', name: 'name', descr: 'descr' },
                },
              ]);
          })
          .then(done)
          .catch(done);
      });
      it('error reply', done => {
        nock(SERVER)
          .post(API)
          .reply(404, 'Not found');

        store.dispatch(addProject('name', 'descr'))
          .then(() => {
            const acts = store.getActions();
            expect(acts[0].type).to.equal(ADD_PROJECT);
            expect(acts[1].type).to.equal(ADD_PROJECT);
            expect(acts[1].error).to.be.true;
            expect(acts[1].payload.status).to.equal(404);
            expect(acts[1].payload).to.have.all.keys('status', 'action', 'url');
            // url: contents might change in future releases
            // msg: nock will always return null, see: https://github.com/node-nock/nock/issues/469
          })
          .then(done)
          .catch(done);
      });
    });
    //
    // All other tests are very much alike
    //
  });
});
<commit_msg>Using actionExpects for assertions, testing new asyncActionCreator<commit_after>import { expect } from 'chai';

import {
  mockStore,
  actionExpects,
} from '_test/utils/renderers';
// uncomment following for data to be used when testing update or delete
// import data from '_test/utils/data';
import nock from 'nock';

import {
  getAllProjects,
  addProject,
  ALL_PROJECTS,
  ADD_PROJECT,
  REQUEST_SENT,
  REPLY_RECEIVED,
} from '_store/actions.js';

const SERVER = `${HOST}:${PORT}`;
const API = `${REST_API_PATH}/projects/`;

describe('projects actions', () => {
  describe('action creators', () => {
    let store;
    beforeEach(() => {
      store = mockStore({ projects: {} });
    });
    afterEach(() => {
      nock.cleanAll();
    });
    describe('getAllProjects', () => {
      it('standard reply', () => {
        const body = [
          { pid: 25, name: 'Writing a Book on Web Dev Tools' },
          { pid: 34, name: 'Cook a Spanish omelette' },
        ];
        nock(SERVER)
          .get(API)
          .query({ fields: 'pid,name,pending' })
          .reply(200, body);

        return store.dispatch(getAllProjects())
          .then(() => actionExpects(store,
            {
              type: ALL_PROJECTS,
              meta: { asyncAction: REQUEST_SENT },
              payload: {},
            },
            {
              type: ALL_PROJECTS,
              payload: body,
              meta: { asyncAction: REPLY_RECEIVED },
            }
          ));
      });
      it('error reply', () => {
        nock(SERVER)
          .get(API)
          .query({ fields: 'pid,name,pending' })
          .reply(404, 'Not found');

        return store.dispatch(getAllProjects())
          .then(() => actionExpects(store,
            {
              type: ALL_PROJECTS,
              meta: { asyncAction: REQUEST_SENT },
              payload: {},
            },
            action => {
              expect(action.type).to.equal(ALL_PROJECTS);
              expect(action.error).to.be.true;
              expect(action.payload.status).to.equal(404);
              expect(action.payload).to.have.all.keys('message', 'status', 'actionType', 'url');
            // url: contents might change in future releases
            // msg: nock will always return null, see: https://github.com/node-nock/nock/issues/469
            }
          ));
      });
    });

    describe('addProject', () => {
      it('standard request', () => {
        nock(SERVER)
          .post(API)
          .reply(200, { pid: '45' });

        return store.dispatch(addProject('name', 'descr'))
          .then(() => actionExpects(store,
            {
              type: ADD_PROJECT,
              payload: {
                name: 'name',
                descr: 'descr',
              },
              meta: { asyncAction: REQUEST_SENT },
            },
            {
              type: ADD_PROJECT,
              payload: { pid: '45', name: 'name', descr: 'descr' },
              meta: { asyncAction: REPLY_RECEIVED },
            }
          ));
      });
      it('error reply', () => {
        nock(SERVER)
          .post(API)
          .reply(404, 'Not found');

        return store.dispatch(addProject('name', 'descr'))
          .then(() => actionExpects(store,
            {
              type: ADD_PROJECT,
              payload: {
                name: 'name',
                descr: 'descr',
              },
              meta: { asyncAction: REQUEST_SENT },
            },
            action => {
              expect(action.type).to.equal(ADD_PROJECT);
              expect(action.error).to.be.true;
              expect(action.payload.status).to.equal(404);
              expect(action.payload).to.have.all.keys('message', 'status', 'actionType', 'url');
            }
            // url: contents might change in future releases
            // msg: nock will always return null, see: https://github.com/node-nock/nock/issues/469
          ));
      });
    });
    //
    // All other tests are very much alike
    //
  });
});
<|endoftext|>


<commit_before>fdom.apis.set("identity", {
  //e.g. var id = identity.id
  'id': {type: "property", value: "string"},
  //Log into the network
  //e.g. login(String agent, String version, String url)
  //Returns nothing
  'login': {type: "method", value: ["string", "string", "string"]},
  //Gets the profile of a user
  //If id is null, return self
  //e.g. identity.getProfile(String id);
  //Returns {
  //  'me': {
  //    'userMe1': {                //Must internal 'userId'
  //    ' userId': 'string',        //ID (e.g. alice@gmail.com) username
  //      'name': 'string',         //Name (e.g. Alice Underpants)
  //      'url': 'string',          //Homepage URL
  //      'network': 'string',      //Name of network
  //      'clients': {
  //        'client1': {              //Array of clients (NOTE: key must match 'clientId' in card)
  //          'clientId': 'string',   //ID of client (e.g. alice@gmail.com/Android-23nadsv32f)
  //          'status': 'string'      //Status (['messageable', 'online', 'offline'])
  //        },
  //        'client2': ...
  //      }
  //    },
  //    'userMe2': ...
  //  },
  //  'roster': {                 //List of friends
  //    'user1': {                //NOTE: Key must match 'userId' in user card
  //      'userId': 'string',
  //      'name': 'string',
  //      'url': string,
  //      'clients': {
  //        'client1': {          //NOTE: Key must match 'clientId' in client card
  //          'clientId': 'string',
  //          'status': 'string'
  //        },
  //        'client2': ...
  //      }
  //    },
  //    'user2': ...
  //  }
  //}
  'getProfile': {type: "method", value: ["string"]},
  //Send a message to user on your network
  //e.g. sendMessage(String destination_id, String message)
  //Returns nothing
  'sendMessage': {type: "method", value: ["string", "string"]},
  //Logs out of the network associated with the given userId
  //If userId is null, log out of all networks
  //e.g. logout(String userId)
  //Returns {
  //  'userId': 'string',
  //  'success': 'boolean',
  //  'message': 'string'
  //}
  'logout': {type: "method", value: ["string"]},
  //Event on change in profile
  //(includes changes to roster)
  'onChange': {type: "event", value: {
    'userId': 'string',
    'name': 'string',
    'url': 'string',
    'network': 'string',
    'clients': 'object'
  }},
  //Event on incoming message
  'onMessage': {type: "event", value: {
    "fromUserId": "string",   //userId of user message is from
    "fromClientId": "string", //clientId of user message is from
    "toUserId": "string",     //userId of user message is to
    "toClientId": "string",   //clientId of user message is to
    "message": "object"       //message contents
  }},
  //Event on provider status
  //Can be 'offline', 'online', 'authenticating', 'connecting' or 'error'
  'onStatus': {type: "event", value: {
    "userId": "string", //userId of network this is about
    "status": "string", //One of the above statuses
    "message": "string" //More detailed message about status
  }}
});

<commit_msg>Moved network property to be client-specific (not user-specific)<commit_after>fdom.apis.set("identity", {
  //e.g. var id = identity.id
  'id': {type: "property", value: "string"},
  //Log into the network
  //e.g. login(String agent, String version, String url)
  //Returns nothing
  'login': {type: "method", value: ["string", "string", "string"]},
  //Gets the profile of a user
  //If id is null, return self
  //e.g. identity.getProfile(String id);
  //Returns {
  //  'me': {
  //    'userMe1': {                //Must internal 'userId'
  //    ' userId': 'string',        //ID (e.g. alice@gmail.com) username
  //      'name': 'string',         //Name (e.g. Alice Underpants)
  //      'url': 'string',          //Homepage URL
  //      'clients': {
  //        'client1': {              //Array of clients (NOTE: key must match 'clientId' in card)
  //          'clientId': 'string',   //ID of client (e.g. alice@gmail.com/Android-23nadsv32f)
  //          'network': 'string',    //Name of network
  //          'status': 'string'      //Status (['messageable', 'online', 'offline'])
  //        },
  //        'client2': ...
  //      }
  //    },
  //    'userMe2': ...
  //  },
  //  'roster': {                 //List of friends
  //    'user1': {                //NOTE: Key must match 'userId' in user card
  //      'userId': 'string',
  //      'name': 'string',
  //      'url': string,
  //      'clients': {
  //        'client1': {          //NOTE: Key must match 'clientId' in client card
  //          'clientId': 'string',
  //          'network': 'string'
  //          'status': 'string'
  //        },
  //        'client2': ...
  //      }
  //    },
  //    'user2': ...
  //  }
  //}
  'getProfile': {type: "method", value: ["string"]},
  //Send a message to user on your network
  //e.g. sendMessage(String destination_id, String message)
  //Returns nothing
  'sendMessage': {type: "method", value: ["string", "string"]},
  //Logs out of the network associated with the given userId
  //If userId is null, log out of all networks
  //e.g. logout(String userId)
  //Returns {
  //  'userId': 'string',
  //  'success': 'boolean',
  //  'message': 'string'
  //}
  'logout': {type: "method", value: ["string"]},
  //Event on change in profile
  //(includes changes to roster)
  'onChange': {type: "event", value: {
    'userId': 'string',
    'name': 'string',
    'url': 'string',
    'clients': 'object'
  }},
  //Event on incoming message
  'onMessage': {type: "event", value: {
    "fromUserId": "string",   //userId of user message is from
    "fromClientId": "string", //clientId of user message is from
    "toUserId": "string",     //userId of user message is to
    "toClientId": "string",   //clientId of user message is to
    "message": "object"       //message contents
  }},
  //Event on provider status
  //Can be 'offline', 'online', 'authenticating', 'connecting' or 'error'
  'onStatus': {type: "event", value: {
    "userId": "string", //userId of network this is about
    "network": "string",//name of the network (chosen by identity provider)
    "status": "string", //One of the above statuses
    "message": "string" //More detailed message about status
  }}
});

<|endoftext|>


<commit_before>'use strict';

exports.type = 'perItem';

exports.active = true;

var regViewBox = /^0\s0\s([\-+]?\d*\.?\d+([eE][\-+]?\d+)?)\s([\-+]?\d*\.?\d+([eE][\-+]?\d+)?)$/,
    viewBoxElems = ['svg', 'pattern'];

/**
 * Remove viewBox attr which coincides with a width/height box.
 *
 * @see http://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute
 *
 * @example
 * <svg width="100" height="50" viewBox="0 0 100 50">
 *             ⬇
 * <svg width="100" height="50">
 *
 * @param {Object} item current iteration item
 * @return {Boolean} if false, item will be filtered out
 *
 * @author Kir Belevich
 */
exports.fn = function(item) {

    if (
        item.isElem(viewBoxElems) &&
        item.hasAttr('viewBox') &&
        item.hasAttr('width') &&
        item.hasAttr('height')
    ) {

        var match = item.attr('viewBox').value.match(regViewBox);

        if (match) {
            if (
                item.attr('width').value === match[1] &&
                item.attr('height').value === match[3]
            ) {
                item.removeAttr('viewBox');
            }
        }

    }

};
<commit_msg>disable removeViewBox by default (fix #139)<commit_after>'use strict';

exports.type = 'perItem';

exports.active = false;

var regViewBox = /^0\s0\s([\-+]?\d*\.?\d+([eE][\-+]?\d+)?)\s([\-+]?\d*\.?\d+([eE][\-+]?\d+)?)$/,
    viewBoxElems = ['svg', 'pattern'];

/**
 * Remove viewBox attr which coincides with a width/height box.
 *
 * @see http://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute
 *
 * @example
 * <svg width="100" height="50" viewBox="0 0 100 50">
 *             ⬇
 * <svg width="100" height="50">
 *
 * @param {Object} item current iteration item
 * @return {Boolean} if false, item will be filtered out
 *
 * @author Kir Belevich
 */
exports.fn = function(item) {

    if (
        item.isElem(viewBoxElems) &&
        item.hasAttr('viewBox') &&
        item.hasAttr('width') &&
        item.hasAttr('height')
    ) {

        var match = item.attr('viewBox').value.match(regViewBox);

        if (match) {
            if (
                item.attr('width').value === match[1] &&
                item.attr('height').value === match[3]
            ) {
                item.removeAttr('viewBox');
            }
        }

    }

};
<|endoftext|>


<commit_before>require('../../support/test_helper');

var assert = require('../../support/assert');
var TestClient = require('../../support/test-client');

function makeMapconfig(markerWidth, markerFill) {
    return {
        "version": "1.4.0",
        "layers": [
            {
                "type": 'mapnik',
                "options": {
                    "cartocss_version": '2.3.0',
                    "sql": 'SELECT * FROM populated_places_simple_reduced',
                    "cartocss": createCartocss(markerWidth, markerFill)
                }
            }
        ]
    };
}

function createCartocss(markerWidth, markerFill) {
    return [
        "#populated_places_simple_reduced {",
        "  marker-fill-opacity: 0.9;",
        "  marker-line-color: #FFF;",
        "  marker-line-width: 1;",
        "  marker-line-opacity: 1;",
        "  marker-placement: point;",
        "  marker-type: ellipse;",
        "  marker-allow-overlap: true;",
        "  marker-width: " + (markerWidth || '10') + ";",
        "  marker-fill: " + (markerFill || 'red') + ";",
        "}"
    ].join('\n');
}

var ERROR_RESPONSE = {
    status: 400,
    headers: {
        'Content-Type': 'application/json; charset=utf-8'
    }
};

describe('turbo-carto error cases', function() {
    afterEach(function (done) {
        if (this.testClient) {
            this.testClient.drain(done);
        }
    });

    it('should return invalid number of ramp error', function(done) {
        this.testClient = new TestClient(makeMapconfig('ramp([pop_max], 8, 96, 3, (8,24,96,128))'));
        this.testClient.getLayergroup(ERROR_RESPONSE, function(err, layergroup) {
            assert.ok(!err, err);

            assert.ok(layergroup.hasOwnProperty('errors'));
            assert.equal(layergroup.errors.length, 1);
            assert.ok(layergroup.errors[0].match(/^Failed\sto\sprocess/));
            assert.ok(layergroup.errors[0].match(/invalid\sramp\slength/i));

            done();
        });
    });

    it('should return invalid column from datasource', function(done) {
        this.testClient = new TestClient(makeMapconfig(null, 'ramp([wadus_column], (red, green, blue))'));
        this.testClient.getLayergroup(ERROR_RESPONSE, function(err, layergroup) {
            assert.ok(!err, err);

            assert.ok(layergroup.hasOwnProperty('errors'));
            assert.equal(layergroup.errors.length, 1);
            assert.ok(layergroup.errors[0].match(/^Failed\sto\sprocess/));
            assert.ok(layergroup.errors[0].match(/unable\sto\scompute\sramp/i));
            assert.ok(layergroup.errors[0].match(/wadus_column/));

            done();
        });
    });

    it('should return invalid method from datasource', function(done) {
        this.testClient = new TestClient(makeMapconfig(null, 'ramp([wadus_column], (red, green, blue), wadusmethod)'));
        this.testClient.getLayergroup(ERROR_RESPONSE, function(err, layergroup) {
            assert.ok(!err, err);

            assert.ok(layergroup.hasOwnProperty('errors'));
            assert.equal(layergroup.errors.length, 1);
            assert.ok(layergroup.errors[0].match(/^Failed\sto\sprocess/));
            assert.ok(layergroup.errors[0].match(/unable\sto\scompute\sramp/i));
            assert.ok(layergroup.errors[0].match(/invalid\smethod\s\"wadusmethod\"/i));

            done();
        });
    });

    it('should fail by falling back to normal carto parser', function(done) {
        this.testClient = new TestClient(makeMapconfig('ramp([price], (8,24,96), (8,24,96));//(red, green, blue))'));
        this.testClient.getLayergroup(ERROR_RESPONSE, function(err, layergroup) {
            assert.ok(!err, err);

            assert.ok(layergroup.hasOwnProperty('errors'));
            assert.equal(layergroup.errors.length, 1);
            assert.ok(!layergroup.errors[0].match(/^Failed\sto\sprocess/));
            assert.ok(layergroup.errors[0].match(/invalid\scode/i));

            done();
        });
    });

    it('turbo-carto: should return error invalid column from datasource with some context', function(done) {
        this.testClient = new TestClient(makeMapconfig(null, 'ramp([wadus_column], (red, green, blue))'));
        this.testClient.getLayergroup(ERROR_RESPONSE, function(err, layergroup) {
            assert.ok(!err, err);

            assert.ok(layergroup.hasOwnProperty('errors'));
            assert.equal(layergroup.errors_with_context.length, 1);
            assert.equal(layergroup.errors_with_context[0].type, 'layer');
            assert.equal(layergroup.errors_with_context[0].subtype, 'turbo-carto');
            assert.ok(layergroup.errors_with_context[0].message.match(/^Failed\sto\sprocess/));
            assert.ok(layergroup.errors_with_context[0].message.match(/unable\sto\scompute\sramp/i));
            assert.ok(layergroup.errors_with_context[0].message.match(/wadus_column/));

            assert.equal(layergroup.errors_with_context[0].layer.id, 'layer0');
            assert.equal(layergroup.errors_with_context[0].layer.index, 0);
            assert.equal(layergroup.errors_with_context[0].layer.type, 'mapnik');

            assert.equal(layergroup.errors_with_context[0].layer.context.selector, '#populated_places_simple_reduced');
            assert.deepEqual(layergroup.errors_with_context[0].layer.context.source, {
                start: {
                    line: 10,
                    column: 3
                },
                end: {
                    line: 10,
                    column: 56
                }
            });
            done();
        });
    });

    it('should return multiple errors', function(done) {

        var multipleErrorsMapConfig = {
            "version": "1.4.0",
            "layers": [
                {
                    "type": 'mapnik',
                    "options": {
                        "cartocss_version": '2.3.0',
                        "sql": 'SELECT * FROM populated_places_simple_reduced',
                        "cartocss": createCartocss(null, 'ramp([wadus_column], (red, green, blue))')
                    }
                },
                {
                    "type": 'mapnik',
                    "options": {
                        "cartocss_version": '2.3.0',
                        "sql": 'SELECT * FROM populated_places_simple_reduced',
                        "cartocss": createCartocss('ramp([invalid_column], (red, green, blue))')
                    }
                }
            ]
        };

        this.testClient = new TestClient(multipleErrorsMapConfig);
        this.testClient.getLayergroup(ERROR_RESPONSE, function(err, layergroup) {
            assert.ok(!err, err);

            assert.ok(layergroup.hasOwnProperty('errors'));
            assert.equal(layergroup.errors_with_context.length, 2);

            assert.equal(layergroup.errors_with_context[0].type, 'layer');
            assert.equal(layergroup.errors_with_context[0].subtype, 'turbo-carto');
            assert.ok(layergroup.errors_with_context[0].message.match(/^Failed\sto\sprocess/));
            assert.ok(layergroup.errors_with_context[0].message.match(/unable\sto\scompute\sramp/i));
            assert.ok(layergroup.errors_with_context[0].message.match(/wadus_column/));
            assert.equal(layergroup.errors_with_context[0].layer.id, 'layer0');

            assert.equal(layergroup.errors_with_context[1].type, 'layer');
            assert.equal(layergroup.errors_with_context[1].subtype, 'turbo-carto');
            assert.ok(layergroup.errors_with_context[1].message.match(/^Failed\sto\sprocess/));
            assert.ok(layergroup.errors_with_context[1].message.match(/unable\sto\scompute\sramp/i));
            assert.ok(layergroup.errors_with_context[1].message.match(/invalid_column/));
            assert.equal(layergroup.errors_with_context[1].layer.id, 'layer1');

            done();
        });
    });
});
<commit_msg>Output actual error message<commit_after>require('../../support/test_helper');

var assert = require('../../support/assert');
var TestClient = require('../../support/test-client');

function makeMapconfig(markerWidth, markerFill) {
    return {
        "version": "1.4.0",
        "layers": [
            {
                "type": 'mapnik',
                "options": {
                    "cartocss_version": '2.3.0',
                    "sql": 'SELECT * FROM populated_places_simple_reduced',
                    "cartocss": createCartocss(markerWidth, markerFill)
                }
            }
        ]
    };
}

function createCartocss(markerWidth, markerFill) {
    return [
        "#populated_places_simple_reduced {",
        "  marker-fill-opacity: 0.9;",
        "  marker-line-color: #FFF;",
        "  marker-line-width: 1;",
        "  marker-line-opacity: 1;",
        "  marker-placement: point;",
        "  marker-type: ellipse;",
        "  marker-allow-overlap: true;",
        "  marker-width: " + (markerWidth || '10') + ";",
        "  marker-fill: " + (markerFill || 'red') + ";",
        "}"
    ].join('\n');
}

var ERROR_RESPONSE = {
    status: 400,
    headers: {
        'Content-Type': 'application/json; charset=utf-8'
    }
};

describe('turbo-carto error cases', function() {
    afterEach(function (done) {
        if (this.testClient) {
            this.testClient.drain(done);
        }
    });

    it('should return invalid number of ramp error', function(done) {
        this.testClient = new TestClient(makeMapconfig('ramp([pop_max], 8, 96, 3, (8,24,96,128))'));
        this.testClient.getLayergroup(ERROR_RESPONSE, function(err, layergroup) {
            assert.ok(!err, err);

            assert.ok(layergroup.hasOwnProperty('errors'));
            assert.equal(layergroup.errors.length, 1);
            assert.ok(layergroup.errors[0].match(/^Failed\sto\sprocess/), layergroup.errors[0]);
            assert.ok(layergroup.errors[0].match(/invalid\sramp\slength/i), layergroup.errors[0]);

            done();
        });
    });

    it('should return invalid column from datasource', function(done) {
        this.testClient = new TestClient(makeMapconfig(null, 'ramp([wadus_column], (red, green, blue))'));
        this.testClient.getLayergroup(ERROR_RESPONSE, function(err, layergroup) {
            assert.ok(!err, err);

            assert.ok(layergroup.hasOwnProperty('errors'));
            assert.equal(layergroup.errors.length, 1);
            assert.ok(layergroup.errors[0].match(/^Failed\sto\sprocess/));
            assert.ok(layergroup.errors[0].match(/unable\sto\scompute\sramp/i));
            assert.ok(layergroup.errors[0].match(/wadus_column/));

            done();
        });
    });

    it('should return invalid method from datasource', function(done) {
        this.testClient = new TestClient(makeMapconfig(null, 'ramp([wadus_column], (red, green, blue), wadusmethod)'));
        this.testClient.getLayergroup(ERROR_RESPONSE, function(err, layergroup) {
            assert.ok(!err, err);

            assert.ok(layergroup.hasOwnProperty('errors'));
            assert.equal(layergroup.errors.length, 1);
            assert.ok(layergroup.errors[0].match(/^Failed\sto\sprocess/));
            assert.ok(layergroup.errors[0].match(/unable\sto\scompute\sramp/i));
            assert.ok(layergroup.errors[0].match(/invalid\smethod\s\"wadusmethod\"/i));

            done();
        });
    });

    it('should fail by falling back to normal carto parser', function(done) {
        this.testClient = new TestClient(makeMapconfig('ramp([price], (8,24,96), (8,24,96));//(red, green, blue))'));
        this.testClient.getLayergroup(ERROR_RESPONSE, function(err, layergroup) {
            assert.ok(!err, err);

            assert.ok(layergroup.hasOwnProperty('errors'));
            assert.equal(layergroup.errors.length, 1);
            assert.ok(!layergroup.errors[0].match(/^Failed\sto\sprocess/));
            assert.ok(layergroup.errors[0].match(/invalid\scode/i));

            done();
        });
    });

    it('turbo-carto: should return error invalid column from datasource with some context', function(done) {
        this.testClient = new TestClient(makeMapconfig(null, 'ramp([wadus_column], (red, green, blue))'));
        this.testClient.getLayergroup(ERROR_RESPONSE, function(err, layergroup) {
            assert.ok(!err, err);

            assert.ok(layergroup.hasOwnProperty('errors'));
            assert.equal(layergroup.errors_with_context.length, 1);
            assert.equal(layergroup.errors_with_context[0].type, 'layer');
            assert.equal(layergroup.errors_with_context[0].subtype, 'turbo-carto');
            assert.ok(layergroup.errors_with_context[0].message.match(/^Failed\sto\sprocess/));
            assert.ok(layergroup.errors_with_context[0].message.match(/unable\sto\scompute\sramp/i));
            assert.ok(layergroup.errors_with_context[0].message.match(/wadus_column/));

            assert.equal(layergroup.errors_with_context[0].layer.id, 'layer0');
            assert.equal(layergroup.errors_with_context[0].layer.index, 0);
            assert.equal(layergroup.errors_with_context[0].layer.type, 'mapnik');

            assert.equal(layergroup.errors_with_context[0].layer.context.selector, '#populated_places_simple_reduced');
            assert.deepEqual(layergroup.errors_with_context[0].layer.context.source, {
                start: {
                    line: 10,
                    column: 3
                },
                end: {
                    line: 10,
                    column: 56
                }
            });
            done();
        });
    });

    it('should return multiple errors', function(done) {

        var multipleErrorsMapConfig = {
            "version": "1.4.0",
            "layers": [
                {
                    "type": 'mapnik',
                    "options": {
                        "cartocss_version": '2.3.0',
                        "sql": 'SELECT * FROM populated_places_simple_reduced',
                        "cartocss": createCartocss(null, 'ramp([wadus_column], (red, green, blue))')
                    }
                },
                {
                    "type": 'mapnik',
                    "options": {
                        "cartocss_version": '2.3.0',
                        "sql": 'SELECT * FROM populated_places_simple_reduced',
                        "cartocss": createCartocss('ramp([invalid_column], (red, green, blue))')
                    }
                }
            ]
        };

        this.testClient = new TestClient(multipleErrorsMapConfig);
        this.testClient.getLayergroup(ERROR_RESPONSE, function(err, layergroup) {
            assert.ok(!err, err);

            assert.ok(layergroup.hasOwnProperty('errors'));
            assert.equal(layergroup.errors_with_context.length, 2);

            assert.equal(layergroup.errors_with_context[0].type, 'layer');
            assert.equal(layergroup.errors_with_context[0].subtype, 'turbo-carto');
            assert.ok(layergroup.errors_with_context[0].message.match(/^Failed\sto\sprocess/));
            assert.ok(layergroup.errors_with_context[0].message.match(/unable\sto\scompute\sramp/i));
            assert.ok(layergroup.errors_with_context[0].message.match(/wadus_column/));
            assert.equal(layergroup.errors_with_context[0].layer.id, 'layer0');

            assert.equal(layergroup.errors_with_context[1].type, 'layer');
            assert.equal(layergroup.errors_with_context[1].subtype, 'turbo-carto');
            assert.ok(layergroup.errors_with_context[1].message.match(/^Failed\sto\sprocess/));
            assert.ok(layergroup.errors_with_context[1].message.match(/unable\sto\scompute\sramp/i));
            assert.ok(layergroup.errors_with_context[1].message.match(/invalid_column/));
            assert.equal(layergroup.errors_with_context[1].layer.id, 'layer1');

            done();
        });
    });
});
<|endoftext|>


<commit_before>var body = document.body;
var html = document.documentElement;
var body = html.getElementsByTagName("body")[0];
var table = body.getElementsByTagName("table")[0];
var size = 20; // Size of cell

// Credit to http://stackoverflow.com/questions/1145850/how-to-get-height-of-entire-document-with-javascript
var height = Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight);
var width = Math.max(body.scrollWidth, body.offsetWidth, html.clientWidth, html.scrollWidth, html.offsetWidth);
width -= 10; // Make the table a little smaller so there's a valid border.
height -= 10;
width = Math.floor(width/size);
height = Math.floor(height/size);
console.log("Width: "+width, " Height: "+height);
table.setAttribute("height", height*size + "px");
table.setAttribute("width", width*size + "px");
for (var i=0; i<height; i+=1) {
	var row = document.createElement("tr");
	for (var j=0; j<width; j+=1) {
	  var cell = document.createElement("td");
	  var btn = document.createElement("button");
	  cell.setAttribute("class", "emptycell");
	  cell.setAttribute("name", i+"_"+j);
	  btn.setAttribute("onclick", "placeFood("+i+","+j+")");
	  row.appendChild(cell);
	}
	table.appendChild(row);
}

function updateBoard(snake) {
  console.log(snake);
  var rows = table.getElementsByTagName("tr"); // Syntax says so.
  console.log("Rows: "+rows);
	for (var row in rows) {
  	console.log("Row: "+row);
  	var cells = row.getElementsByTagName("td");
	  for (var cell in cells) {
	    var isSnakeCell = false;
	    for (var s in snake) {
  	    if (cell.name == s[0]+"_"+s[1]) {
    	    isSnakeCell = true;
    	    break;
    	  }
	    }
	    console.log(cell);
	    if (isSnakeCell) {
        cell.setAttribute("class", "snakecell");
	    } else {
  	    cell.setAttribute("class", "emptycell");
  	  }
	  }
	}
}<commit_msg>Better better debug.<commit_after>var body = document.body;
var html = document.documentElement;
var body = html.getElementsByTagName("body")[0];
var table = body.getElementsByTagName("table")[0];
var size = 20; // Size of cell

// Credit to http://stackoverflow.com/questions/1145850/how-to-get-height-of-entire-document-with-javascript
var height = Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight);
var width = Math.max(body.scrollWidth, body.offsetWidth, html.clientWidth, html.scrollWidth, html.offsetWidth);
width -= 10; // Make the table a little smaller so there's a valid border.
height -= 10;
width = Math.floor(width/size);
height = Math.floor(height/size);
console.log("Width: "+width, " Height: "+height);
table.setAttribute("height", height*size + "px");
table.setAttribute("width", width*size + "px");
for (var i=0; i<height; i+=1) {
	var row = document.createElement("tr");
	for (var j=0; j<width; j+=1) {
	  var cell = document.createElement("td");
	  var btn = document.createElement("button");
	  cell.setAttribute("class", "emptycell");
	  cell.setAttribute("name", i+"_"+j);
	  btn.setAttribute("onclick", "placeFood("+i+","+j+")");
	  row.appendChild(cell);
	}
	table.appendChild(row);
}

function updateBoard(snake) {
  console.log(snake);
  var rows = table.getElementsByTagName("tr"); // Syntax says so.
  console.log("Rows");
  console.log(rows);
	for (var row in rows) {
  	console.log("Row:");
  	console.log(row);
  	var cells = row.getElementsByTagName("td");
	  for (var cell in cells) {
	    var isSnakeCell = false;
	    for (var s in snake) {
  	    if (cell.name == s[0]+"_"+s[1]) {
    	    isSnakeCell = true;
    	    break;
    	  }
	    }
	    console.log(cell);
	    if (isSnakeCell) {
        cell.setAttribute("class", "snakecell");
	    } else {
  	    cell.setAttribute("class", "emptycell");
  	  }
	  }
	}
}<|endoftext|>