<commit_before>var recipe_id;

function fetchInsane() {
    fetchRecipe(true);
}

function fetchRecipe(insane=false) {
    var api_url = "https://rrg.jamesoff.net/recipe";
    if (insane) {
        api_url += '-insane'
    }
    $.getJSON(api_url, function(data) {
        $("div#recipe").replaceWith(
            formatRecipe(data)
        );
        $("div#share").replaceWith(
            "
<input id='share' type='button' value='Get share link' onclick='shareRecipe()' />
"
        );
        recipe_id = data.metadata.recipe_id;
        location.hash = '';
    } )
        .fail(function() {
            formatRecipeError();
            $("div#share").replaceWith(
                "

"
            );
            recipe_id = '';
        } );
}

function formatRecipeError() {
    $("div#recipe").replaceWith("
Oh no, dropped all the ingredients on the floor, sorry :(

");
    $("div#yum").replaceWith("
Yum.
");
}

function formatRecipe(data) {
    var HTML = '
';
    HTML += '
Serves ' + data.serves + '
';
    HTML += '
' + data.title + '
';
    HTML += 'You will need:
';
    data.ingredients.forEach(function(i) {
        HTML += "
" + i + "
";
    });
    HTML += "
";
    HTML += "Instructions:";
    HTML += "
";
    data.steps.forEach(function(s) {
        HTML += "
" + s + "
";
    });
    HTML + "
";
    HTML += "
";
    return HTML;
}

function writeSharebutton() {
    $("div#share").replaceWith(
        "
<input id='share' type='button' value='Get share link' onclick='shareRecipe()' />
"
    );
}

function clearShareButton() {
    $("div#share").replaceWith(
        "

"
    );
}

function fetchSpecificRecipe(rid) {
    $.getJSON("https://rrg.jamesoff.net/" + rid + ".json", function(data) {
        $("div#recipe").replaceWith(
            formatRecipe(data)
        );
        recipe_id = rid;
        giveShareLink()
    } )
        .fail(function() {
            formatRecipeError();
            clearShareButton();
            recipe_id = '';
        } );
}

function shareRecipe() {
    if (recipe_id == '') {
        return false;
    }
    $.getJSON("https://rrg.jamesoff.net/recipe-persist/" + recipe_id, function(data) {
        giveShareLink();
    } )
        .fail(function() {
            giveShareFailure();
        } );
}

function giveShareLink() {
    var share_url = 'https://jamesoff.net/rrg/' + recipe_id;
    $("div#share").replaceWith(
        "
Sharable link: " + share_url + "
"
    );
}

function giveShareFailure() {
    $("div#share").replaceWith(
        "
<input id='share' type='button' value='Get share link' onclick='shareRecipe()' /> An error occurred while generating the share link.
"
    );
}

function loadHandler() {
    var recipe_id = window.location.hash.substr(1);
    if (recipe_id == "") {
        fetchRecipe();
    }
    else {
        fetchSpecificRecipe(recipe_id);
    }
}

$(window).load(loadHandler());

$(document).keydown(function (e) {
    if (e.which == 82) {
        // r
        fetchRecipe();
        return false;
    }
    if (e.which == 73) {
        // i
        fetchInsane();
        return false;
    }
});

<commit_msg>Remove strikethrough on successful recipe<commit_after>var recipe_id;

function fetchInsane() {
    fetchRecipe(true);
}

function fetchRecipe(insane=false) {
    var api_url = "https://rrg.jamesoff.net/recipe";
    if (insane) {
        api_url += '-insane'
    }
    $.getJSON(api_url, function(data) {
        $("div#recipe").replaceWith(
            formatRecipe(data)
        );
        $("div#share").replaceWith(
            "
<input id='share' type='button' value='Get share link' onclick='shareRecipe()' />
"
        );
        $("div#yum").replaceWith("
Yum.
");
        recipe_id = data.metadata.recipe_id;
        location.hash = '';
    } )
        .fail(function() {
            formatRecipeError();
            $("div#share").replaceWith(
                "

"
            );
            recipe_id = '';
        } );
}

function formatRecipeError() {
    $("div#recipe").replaceWith("
Oh no, dropped all the ingredients on the floor, sorry :(

");
    $("div#yum").replaceWith("
Yum.
");
}

function formatRecipe(data) {
    var HTML = '
';
    HTML += '
Serves ' + data.serves + '
';
    HTML += '
' + data.title + '
';
    HTML += 'You will need:
';
    data.ingredients.forEach(function(i) {
        HTML += "
" + i + "
";
    });
    HTML += "
";
    HTML += "Instructions:";
    HTML += "
";
    data.steps.forEach(function(s) {
        HTML += "
" + s + "
";
    });
    HTML + "
";
    HTML += "
";
    return HTML;
}

function writeSharebutton() {
    $("div#share").replaceWith(
        "
<input id='share' type='button' value='Get share link' onclick='shareRecipe()' />
"
    );
}

function clearShareButton() {
    $("div#share").replaceWith(
        "

"
    );
}

function fetchSpecificRecipe(rid) {
    $.getJSON("https://rrg.jamesoff.net/" + rid + ".json", function(data) {
        $("div#recipe").replaceWith(
            formatRecipe(data)
        );
        recipe_id = rid;
        giveShareLink()
    } )
        .fail(function() {
            formatRecipeError();
            clearShareButton();
            recipe_id = '';
        } );
}

function shareRecipe() {
    if (recipe_id == '') {
        return false;
    }
    $.getJSON("https://rrg.jamesoff.net/recipe-persist/" + recipe_id, function(data) {
        giveShareLink();
    } )
        .fail(function() {
            giveShareFailure();
        } );
}

function giveShareLink() {
    var share_url = 'https://jamesoff.net/rrg/' + recipe_id;
    $("div#share").replaceWith(
        "
Sharable link: " + share_url + "
"
    );
}

function giveShareFailure() {
    $("div#share").replaceWith(
        "
<input id='share' type='button' value='Get share link' onclick='shareRecipe()' /> An error occurred while generating the share link.
"
    );
}

function loadHandler() {
    var recipe_id = window.location.hash.substr(1);
    if (recipe_id == "") {
        fetchRecipe();
    }
    else {
        fetchSpecificRecipe(recipe_id);
    }
}

$(window).load(loadHandler());

$(document).keydown(function (e) {
    if (e.which == 82) {
        // r
        fetchRecipe();
        return false;
    }
    if (e.which == 73) {
        // i
        fetchInsane();
        return false;
    }
});

<|endoftext|>


<commit_before>var Scd = function(videoEl, options, undefined) {
    // Public properties.
    // Contains detected scene changes timecodes.
    this.sceneTimecodes = [];

    // Private properties.
    var that = this;
    var document = window.document;
    var Math = window.Math;

    // The width and height at which the frames will be resized down to for comparison.
    var _step = 50;

    // The minimal duration of a scene in s. 2 consecutive scene changes can be detected within this interval.
    var _minSceneDuration = 0.25;

    // Percentage color difference above which a scene change is detected (0 <= threshold <= 100).
    var _threshold = 25;

    // Display detected scenes first frame.
    var _debug = 0;

    /**
     * Maximum color difference possible.
     * @const
     * @type {number}
     */
    //var maxDiff = Math.sqrt(Math.pow(255, 2) * 3);
    var maxDiff = 441.67;

    var _currentTime = 0;

    var _width = 0;
    var _height = 0;

    var _canvasA = document.createElement("canvas");
    var _canvasB = document.createElement("canvas");
    var _ctxA = _canvasA.getContext("2d");
    var _ctxB = _canvasB.getContext("2d");

    var _stop = 0;
    var _callback = 0;

    // Options.
    if(typeof options !== undefined) {
        if(options.step) {
            _step = parseInt(options.step, 10);
        }
        if(options.minSceneDuration) {
            _minSceneDuration = parseFloat(options.minSceneDuration);
        }
        if(options.threshold) {
            _threshold = parseFloat(options.threshold);
        }
        if(options.debug) {
            _debug = Boolean(options.debug);
        }
    }
    // _threshold is set between 0 and maxDiff interval to save calculations later.
    _threshold = _threshold * maxDiff / 100;
    // The number of pixels of resized frames. Used to speed up average calculation.
    var _step_sq = Math.pow(_step, 2);

    // Debug
    if(_debug) {
        var _debugContainer = document.createElement("div");
        _debugContainer.className = "scd-debug";
        document.getElementsByTagName("body")[0].appendChild(_debugContainer);
    }

    // @todo: Call this function is Scd is instantiated after durationchange was triggered.
    videoEl.addEventListener("durationchange", function() {
        // durationchange appears to be the first event triggered by video that exposes width and height.
        _width = this.videoWidth;
        _height = this.videoHeight;
        _canvasA.width = _step;
        _canvasA.height = _step;
        _canvasB.width = _step;
        _canvasB.height = _step;
        //_ctxA.drawImage(this, 0, 0, _width, _height, 0, 0, _step, _step);

        // @todo: store this in a variable and set it back at the end.
        this.controls = 0;

    // @todo: remove this event listener.
    }, false);

    videoEl.addEventListener("seeked", function() {
        detectSceneChange();
    }, false);

    // Public methods.
    this.start = function(callback) {
        if(callback !== undefined) {
            _callback = callback;
        }
        detectSceneChange();
    };

    this.pause = function() {
        _stop = 1;
    };

    var detectSceneChange = function() {
        if(_stop) {
            return;
        }

        // @fixme: Bug on Opera. duration is not always defined.
        if(videoEl.ended || _currentTime > videoEl.duration) {
            if(_callback) {
                _callback();
            }
            return;
        }

        _ctxA.drawImage(videoEl, 0, 0, _width, _height, 0, 0, _step, _step);
        var diff = computeDifferences(_ctxA, _ctxB);

        if(diff[1] > _threshold) {
            that.sceneTimecodes.push(videoEl.currentTime);

            if(_debug) {
                var tmpContainer = document.createElement("div");
                var tmpCanvasA = document.createElement("canvas");
                tmpCanvasA.width = _width / 2;
                tmpCanvasA.height = _height / 2;
                tmpCanvasA.getContext("2d").drawImage(videoEl, 0, 0, _width, _height, 0, 0, _width / 2, _height / 2);
                tmpContainer.appendChild(tmpCanvasA);
                tmpContainer.appendChild(document.createElement("br"));
                tmpContainer.appendChild(document.createTextNode("max: " + Math.round(diff[0] / maxDiff * 100) + "%, avg: " + Math.round(diff[1] / maxDiff * 100) + "%, min: " + Math.round(diff[2] / maxDiff * 100) + "%"));
                _debugContainer.appendChild(tmpContainer);
            }
        }

        _ctxB.drawImage(_canvasA, 0, 0, _step, _step, 0, 0, _step, _step);
        _currentTime += _minSceneDuration;
        videoEl.currentTime = _currentTime;
    };

    var computeDifferences = function(ctxA, ctxB) {
        var colorsA = ctxA.getImageData(0, 0, _step, _step).data;
        var colorsB = ctxB.getImageData(0, 0, _step, _step).data;
        var diff = [];
        var i = colorsA.length;

        do {
            diff.push(getColorDistance(colorsA[i-4], colorsA[i+1-4], colorsA[i+2-4], colorsB[i-4], colorsB[i+1-4], colorsB[i+2-4]));
        } while(i = i - 4);

        var avg = getAverage(diff);
        if(_debug) {
            var max = getMaxOfArray(diff);
            var min = getMinOfArray(diff);
            return [max, avg, min];
        }else {
            return ["0", avg];
        }
    };

    /**
     * Calculates the distance between 2 colors RGB compounds.
     * @param {number} RA Red compound value of color A.
     * @param {number} GA Green compound value of color A.
     * @param {number} BA Blue compound value of color A.
     * @param {number} RB Red compound value of color B.
     * @param {number} GB Green compound value of color B.
     * @param {number} BB Blue compound value of color B.
     * @return {number} The distance.
     */
    var getColorDistance = function(RA, GA, BA, RB, GB, BB) {
        return Math.sqrt(Math.pow(RA - RB, 2) + Math.pow(GA - GB, 2) + Math.pow(BA - BB, 2));
    };

    /**
     * Calculates the maximum value of an array.
     * @param {Array.<number>} numArray An array of values.
     * @return {number} The maximum value.
     */
    var getMaxOfArray = function(numArray) {
        return Math.max.apply(null, numArray);
    };

    /**
     * Calculates the minimum value of an array.
     * @param {Array.<number>} numArray An array of values.
     * @return {number} The minimum value.
     */
    var getMinOfArray = function(numArray) {
        return Math.min.apply(null, numArray);
    };

    /**
     * Calculates the average value of an array.
     * @param {Array.<number>} numArray An array of values.
     * @return {number} The average value.
     */
    var getAverage = function(numArray) {
        return numArray.reduce(function(a, b) {
            return a + b;
        }) / _step_sq;
    };
};
<commit_msg>Restore the computed value for maxDiff<commit_after>var Scd = function(videoEl, options, undefined) {
    // Public properties.
    // Contains detected scene changes timecodes.
    this.sceneTimecodes = [];

    // Private properties.
    var that = this;
    var document = window.document;
    var Math = window.Math;

    // The width and height at which the frames will be resized down to for comparison.
    var _step = 50;

    // The minimal duration of a scene in s. 2 consecutive scene changes can be detected within this interval.
    var _minSceneDuration = 0.25;

    // Percentage color difference above which a scene change is detected (0 <= threshold <= 100).
    var _threshold = 25;

    // Display detected scenes first frame.
    var _debug = 0;

    /**
     * Maximum color difference possible.
     * @const
     * @type {number}
     */
    var maxDiff = Math.sqrt(Math.pow(255, 2) * 3);

    var _currentTime = 0;

    var _width = 0;
    var _height = 0;

    var _canvasA = document.createElement("canvas");
    var _canvasB = document.createElement("canvas");
    var _ctxA = _canvasA.getContext("2d");
    var _ctxB = _canvasB.getContext("2d");

    var _stop = 0;
    var _callback = 0;

    // Options.
    if(typeof options !== undefined) {
        if(options.step) {
            _step = parseInt(options.step, 10);
        }
        if(options.minSceneDuration) {
            _minSceneDuration = parseFloat(options.minSceneDuration);
        }
        if(options.threshold) {
            _threshold = parseFloat(options.threshold);
        }
        if(options.debug) {
            _debug = Boolean(options.debug);
        }
    }
    // _threshold is set between 0 and maxDiff interval to save calculations later.
    _threshold = _threshold * maxDiff / 100;
    // The number of pixels of resized frames. Used to speed up average calculation.
    var _step_sq = Math.pow(_step, 2);

    // Debug
    if(_debug) {
        var _debugContainer = document.createElement("div");
        _debugContainer.className = "scd-debug";
        document.getElementsByTagName("body")[0].appendChild(_debugContainer);
    }

    // @todo: Call this function is Scd is instantiated after durationchange was triggered.
    videoEl.addEventListener("durationchange", function() {
        // durationchange appears to be the first event triggered by video that exposes width and height.
        _width = this.videoWidth;
        _height = this.videoHeight;
        _canvasA.width = _step;
        _canvasA.height = _step;
        _canvasB.width = _step;
        _canvasB.height = _step;
        //_ctxA.drawImage(this, 0, 0, _width, _height, 0, 0, _step, _step);

        // @todo: store this in a variable and set it back at the end.
        this.controls = 0;

    // @todo: remove this event listener.
    }, false);

    videoEl.addEventListener("seeked", function() {
        detectSceneChange();
    }, false);

    // Public methods.
    this.start = function(callback) {
        if(callback !== undefined) {
            _callback = callback;
        }
        detectSceneChange();
    };

    this.pause = function() {
        _stop = 1;
    };

    var detectSceneChange = function() {
        if(_stop) {
            return;
        }

        // @fixme: Bug on Opera. duration is not always defined.
        if(videoEl.ended || _currentTime > videoEl.duration) {
            if(_callback) {
                _callback();
            }
            return;
        }

        _ctxA.drawImage(videoEl, 0, 0, _width, _height, 0, 0, _step, _step);
        var diff = computeDifferences(_ctxA, _ctxB);

        if(diff[1] > _threshold) {
            that.sceneTimecodes.push(videoEl.currentTime);

            if(_debug) {
                var tmpContainer = document.createElement("div");
                var tmpCanvasA = document.createElement("canvas");
                tmpCanvasA.width = _width / 2;
                tmpCanvasA.height = _height / 2;
                tmpCanvasA.getContext("2d").drawImage(videoEl, 0, 0, _width, _height, 0, 0, _width / 2, _height / 2);
                tmpContainer.appendChild(tmpCanvasA);
                tmpContainer.appendChild(document.createElement("br"));
                tmpContainer.appendChild(document.createTextNode("max: " + Math.round(diff[0] / maxDiff * 100) + "%, avg: " + Math.round(diff[1] / maxDiff * 100) + "%, min: " + Math.round(diff[2] / maxDiff * 100) + "%"));
                _debugContainer.appendChild(tmpContainer);
            }
        }

        _ctxB.drawImage(_canvasA, 0, 0, _step, _step, 0, 0, _step, _step);
        _currentTime += _minSceneDuration;
        videoEl.currentTime = _currentTime;
    };

    var computeDifferences = function(ctxA, ctxB) {
        var colorsA = ctxA.getImageData(0, 0, _step, _step).data;
        var colorsB = ctxB.getImageData(0, 0, _step, _step).data;
        var diff = [];
        var i = colorsA.length;

        do {
            diff.push(getColorDistance(colorsA[i-4], colorsA[i+1-4], colorsA[i+2-4], colorsB[i-4], colorsB[i+1-4], colorsB[i+2-4]));
        } while(i = i - 4);

        var avg = getAverage(diff);
        if(_debug) {
            var max = getMaxOfArray(diff);
            var min = getMinOfArray(diff);
            return [max, avg, min];
        }else {
            return ["0", avg];
        }
    };

    /**
     * Calculates the distance between 2 colors RGB compounds.
     * @param {number} RA Red compound value of color A.
     * @param {number} GA Green compound value of color A.
     * @param {number} BA Blue compound value of color A.
     * @param {number} RB Red compound value of color B.
     * @param {number} GB Green compound value of color B.
     * @param {number} BB Blue compound value of color B.
     * @return {number} The distance.
     */
    var getColorDistance = function(RA, GA, BA, RB, GB, BB) {
        return Math.sqrt(Math.pow(RA - RB, 2) + Math.pow(GA - GB, 2) + Math.pow(BA - BB, 2));
    };

    /**
     * Calculates the maximum value of an array.
     * @param {Array.<number>} numArray An array of values.
     * @return {number} The maximum value.
     */
    var getMaxOfArray = function(numArray) {
        return Math.max.apply(null, numArray);
    };

    /**
     * Calculates the minimum value of an array.
     * @param {Array.<number>} numArray An array of values.
     * @return {number} The minimum value.
     */
    var getMinOfArray = function(numArray) {
        return Math.min.apply(null, numArray);
    };

    /**
     * Calculates the average value of an array.
     * @param {Array.<number>} numArray An array of values.
     * @return {number} The average value.
     */
    var getAverage = function(numArray) {
        return numArray.reduce(function(a, b) {
            return a + b;
        }) / _step_sq;
    };
};
<|endoftext|>


<commit_before>// Copyright © Thaddee Tyl. MIT license.
(function (window, undefined) {

var SET = {};

// Parse code.
//

var whiteSpace = new RegExp('[\n\r \t\u0009\u0020\u00a0\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u200c\u200d\u202f\u205f\u2060\u3000\ufeff]');
var spaces = new RegExp('[ \t\u0009\u0020\u00a0\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u200c\u200d\u202f\u205f\u2060\u3000\ufeff]');
var newline = new RegExp('[\n\r]');
var stringEscape = new RegExp('["\\\/bfnrtu\n]');
var hexadecimal = new RegExp('[a-fA-F0-9]');
var digit = new RegExp('[0-9]');

function SetStream(text, filename) {
  this.text = text;
  this.filename = filename || '(set)';
}

SetStream.prototype = {
  text: '',
  filename: '',
  offset: 0,
  line: 0,
  column: 0,
  indent: 0,
  getChar: function() {
    var c = this.text[this.offset];
    if (c == '\n' || c == '\r') {
      this.column = 0;
      this.line ++;
    } else {
      this.column ++;
    }
    return this.text[this.offset++];
  },
  peekChar: function() {
    return this.text[this.offset];
  },
  skip: function(chars) {
    var rchars = new RegExp(chars);
    while (rchars.test(this.text[this.offset])) {
      if (!this.getChar()) { return; }
    }
  },
  skipWhitespace: function() {
    var i;
    this.skip(spaces);
    ch = this.peekChar();
    if (!ch) { return; }
    if (ch === '#') {
      // Comment!
      while (!newline.test(ch)) {
        if (!ch) { return; }
        ch = this.getChar();
      }
    }
    this.skip(newline);
    for (i = 0; ; i++) {
      ch = this.peekChar();
      if (!ch) { return; }
      if (ch === " ") {
        ch = this.getChar();
      } else {
        break;
      }
    }
    this.indent = i;
  },
  error: function(msg) {
    throw new Error(this.filename + ':' + this.line + ':' + this.column +
      ' ' + msg
    );
  },

  readPrimitive: function() {
    var ch, slice;
    this.skipWhitespace();
    ch = this.peekChar();
    if (ch === '"') {
      // String!
      return this.readString();
    }
    if (ch === '-') {
      ch = this.text[this.offset + 1];
      if (whiteSpace.test(ch)) {
        // Array!
        return this.readArray();
      } else {
        // Number!
        return this.readNumber();
      }
    }
    if (digit.test(ch)) {
      // Number!
      return this.readNumber();
    }
    slice = this.text.slice(this.offset, this.offset + 3);
    if (slice === "yes") {
      if (newline.test(this.text[this.offset + 3])) {
        // Boolean!
        for (i = 0; i < 3; i++) { this.getChar(); }
        return true;
      }
    }
    if (slice === "nil") {
      if (newline.test(this.text[this.offset + 3])) {
        // Null!
        for (i = 0; i < 3; i++) { this.getChar(); }
        return null;
      }
    }
    slice = this.text.slice(this.offset, this.offset + 2);
    if (slice === "no") {
      if (newline.test(this.text[this.offset + 3])) {
        // Boolean!
        for (i = 0; i < 2; i++) { this.getChar(); }
        return false;
      }
    }
    // Dictionary!
    return this.readDictionary();
  },

  // We are at the " of the string.
  readString: function() {
    var ch, start, end, i;
    start = end = this.offset;
    this.getChar();  // Go past the ".
    for (;;) {
      ch = this.getChar();
      if (ch === '\\') {
        ch = this.getChar();
        if (!stringEscape.test(ch)) {
          // ERROR
          this.error('Unescaped backslash.');
        } else if (ch === 'u') {
          // Unicode escape.
          for (i = 0; i < 4; i++) {
            ch = this.getChar();
            if (!hexadecimal.test(ch)) {
              // ERROR
              this.error('Invalid unicode escape.');
            }
          }
        }
      } else if (ch === '"') {
        end = this.offset;
        break;
      }
    }
    return JSON.parse(this.text.slice(start, end));
  },

  // We're at the start of a number.
  readNumber: function() {
    var ch, start, end;
    start = end = this.offset;
    ch = this.getChar();
    if (ch === '-') {
      ch = this.getChar();
    }
    if (ch === "0") {
      // Expect a dot.
      ch = this.getChar();  // skip the dot.
      if (ch !== ".") {
        // ERROR
        this.error('Number starting with 0 must be floating-point.');
      }
      this.skipNumberDecimals();
    } else if (digit.test(ch)) {
      for (;;) {
        ch = this.peekChar();
        if (!digit.test(ch)) {
          if (ch === ".") {
            this.getChar();  // skip the dot.
            this.skipNumberDecimals();
          }
          break;
        } else {
          this.getChar();
        }
      }
    } else {
      // ERROR
      this.error('Number started with - is invalid.');
    }
    this.skipExponent();
    end = this.offset;
    return JSON.parse(this.text.slice(start, end));
  },

  skipNumberDecimals: function() {
    var ch;
    for (;;) {
      ch = this.peekChar();
      if (!digit.test(ch)) {
        return;
      }
      this.getChar();
    }
  },

  skipExponent: function() {
    var ch;
    ch = this.peekChar();
    if (ch !== "e" && ch !== "E") {
      return;
    }
    this.getChar();
    ch = this.getChar();
    if (ch === "+" || ch === "-") {
      ch = this.getChar();
    }
    for (;;) {
      ch = this.peekChar();
      if (!digit.test(ch)) {
        return;
      }
      this.getChar();
    }
  },

  // Starts at -.
  readArray: function() {
    var ar, ch, indent;
    ar = [];
    indent = this.indent;
    while (this.indent >= indent) {
      ch = this.getChar();
      if (!ch) { break; }
      if (ch !== "-") {
        this.error('Invalid array, found ' + JSON.stringify(ch) +
            ' instead of "-".');
      }
      indent = this.indent;  // Readjust indentation.
      ar.push(this.readPrimitive());
      this.skipWhitespace();
    }
    return ar;
  },

  // Starts with a key.
  readDictionary: function() {
    var start, end, key, ch, indent, dict;
    dict = {};
    indent = this.indent;
    while (this.indent >= indent) {

      ch = this.peekChar();
      if (ch === '"') {
        key = this.readString();
        this.getChar();  // skip :.
      } else {
        start = end = this.offset;
        for (;;) {
          ch = this.getChar();
          if (!ch) { break; }
          if (ch === ":") {
            break;
          }
        }
        end = this.offset - 1;
        key = this.text.slice(start, end);
      }
      if (!ch) { break; }

      indent = this.indent;  // Readjust indentation.
      dict[key] = this.readPrimitive();
      this.skipWhitespace();
    }

    return dict;
  }
}

SET.parse = function (text) {
  // `toplevel` is the object returned.
  var input = new SetStream(text);
  return input.readPrimitive();
};

// Stringify code.
//

SET.stringify = function SET_STRINGIFY(object, sizeIndent) {
  return set_stringify(object, sizeIndent,
    0,    // indentation
    false // indent the first item
  );
};

function set_stringify(object, sizeIndent, indentation, noIndentFirstItem) {
  var indent = "", i = 0, str = "";
  sizeIndent = sizeIndent || 2;
  for (i = 0; i < indentation * sizeIndent; i++) {
    indent += " ";
  }
  if (object === null || object === undefined) {
    return "nil";
  } else if (object === true) {
    return "yes";
  } else if (object === false) {
    return "no";
  } else if (typeof object === "number") {
    return new String(object);
  } else if (typeof object === "string") {
    return JSON.stringify(object);
  } else if (object instanceof Array) {
    for (i = 0; i < object.length; i++) {
      str += ((noIndentFirstItem && i === 0)? "": indent) + "- "
          + set_stringify(object[i], sizeIndent, indentation + 1, true)
          + '\n';
    }
  } else if (typeof object === "object") {
    i = 0;
    for (var e in object) {
      str += ((noIndentFirstItem && i === 0)? "": indent) + wrapKey(e) + ":"
          + (typeof object[e] === "object"? "\n": " ")
          + set_stringify(object[e], sizeIndent, indentation + 1)
          + '\n';
      i = 1;
    }
  }
  return str;
};

function wrapKey(key) {
  if (key[0] === '-' || key[0] === '"') {
    return JSON.stringify(new String(key));
  } else if (key.indexOf(':') === -1) {
    return new String(key);
  }
  return JSON.stringify(new String(key));
}

if (!window.module) {
  // node.js environment.
  module.exports = SET;
} else {
  window.SET = SET;
}

}(this));
<commit_msg>Better indentation detection.<commit_after>// Copyright © Thaddee Tyl. MIT license.
(function (window, undefined) {

var SET = {};

// Parse code.
//

var whiteSpace = new RegExp('[\n\r \t\u0009\u0020\u00a0\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u200c\u200d\u202f\u205f\u2060\u3000\ufeff]');
var spaces = new RegExp('[ \t\u0009\u0020\u00a0\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u200c\u200d\u202f\u205f\u2060\u3000\ufeff]');
var newline = new RegExp('[\n\r]');
var stringEscape = new RegExp('["\\\/bfnrtu\n]');
var hexadecimal = new RegExp('[a-fA-F0-9]');
var digit = new RegExp('[0-9]');

function SetStream(text, filename) {
  this.text = text;
  this.filename = filename || '(set)';
}

SetStream.prototype = {
  text: '',
  filename: '',
  offset: 0,
  line: 0,
  column: 0,
  sol: 0,  // index of start of line.
  getChar: function() {
    var c = this.text[this.offset];
    if (c == '\n' || c == '\r') {
      this.column = 0;
      this.line ++;
    } else {
      this.column ++;
    }
    return this.text[this.offset++];
  },
  peekChar: function() {
    return this.text[this.offset];
  },
  skip: function(chars) {
    var rchars = new RegExp(chars);
    while (rchars.test(this.text[this.offset])) {
      if (!this.getChar()) { return; }
    }
  },
  skipWhitespace: function() {
    var i, ch, offset;
    this.skip(spaces);
    ch = this.peekChar();
    if (!ch) { return; }
    if (ch === '#') {
      // Comment!
      while (!newline.test(ch)) {
        if (!ch) { return; }
        ch = this.getChar();
      }
    }
    offset = this.offset;
    this.skip(newline);
    if (offset < this.offset) {
      // We did skip a newline.
      this.sol = this.offset;
    }
    this.skip(spaces);
  },
  currentIndent: function() {
    var i, sol;
    for (sol = i = this.sol; ; i++) {
      ch = this.text[i];
      if (ch !== " ") {
        break;
      }
    }
    return i - sol;
  },
  error: function(msg) {
    throw new Error(this.filename + ':' + this.line + ':' + this.column +
      ' ' + msg
    );
  },

  readPrimitive: function() {
    var ch, slice;
    this.skipWhitespace();
    ch = this.peekChar();
    if (ch === '"') {
      // String!
      return this.readString();
    }
    if (ch === '-') {
      ch = this.text[this.offset + 1];
      if (whiteSpace.test(ch)) {
        // Array!
        return this.readArray();
      } else {
        // Number!
        return this.readNumber();
      }
    }
    if (digit.test(ch)) {
      // Number!
      return this.readNumber();
    }
    slice = this.text.slice(this.offset, this.offset + 3);
    if (slice === "yes") {
      if (newline.test(this.text[this.offset + 3])) {
        // Boolean!
        for (i = 0; i < 3; i++) { this.getChar(); }
        return true;
      }
    }
    if (slice === "nil") {
      if (newline.test(this.text[this.offset + 3])) {
        // Null!
        for (i = 0; i < 3; i++) { this.getChar(); }
        return null;
      }
    }
    slice = this.text.slice(this.offset, this.offset + 2);
    if (slice === "no") {
      if (newline.test(this.text[this.offset + 3])) {
        // Boolean!
        for (i = 0; i < 2; i++) { this.getChar(); }
        return false;
      }
    }
    // Dictionary!
    return this.readDictionary();
  },

  // We are at the " of the string.
  readString: function() {
    var ch, start, end, i;
    start = end = this.offset;
    this.getChar();  // Go past the ".
    for (;;) {
      ch = this.getChar();
      if (ch === '\\') {
        ch = this.getChar();
        if (!stringEscape.test(ch)) {
          // ERROR
          this.error('Unescaped backslash.');
        } else if (ch === 'u') {
          // Unicode escape.
          for (i = 0; i < 4; i++) {
            ch = this.getChar();
            if (!hexadecimal.test(ch)) {
              // ERROR
              this.error('Invalid unicode escape.');
            }
          }
        }
      } else if (ch === '"') {
        end = this.offset;
        break;
      }
    }
    return JSON.parse(this.text.slice(start, end));
  },

  // We're at the start of a number.
  readNumber: function() {
    var ch, start, end;
    start = end = this.offset;
    ch = this.getChar();
    if (ch === '-') {
      ch = this.getChar();
    }
    if (ch === "0") {
      // Expect a dot.
      ch = this.getChar();  // skip the dot.
      if (ch !== ".") {
        // ERROR
        this.error('Number starting with 0 must be floating-point.');
      }
      this.skipNumberDecimals();
    } else if (digit.test(ch)) {
      for (;;) {
        ch = this.peekChar();
        if (!digit.test(ch)) {
          if (ch === ".") {
            this.getChar();  // skip the dot.
            this.skipNumberDecimals();
          }
          break;
        } else {
          this.getChar();
        }
      }
    } else {
      // ERROR
      this.error('Number started with - is invalid.');
    }
    this.skipExponent();
    end = this.offset;
    return JSON.parse(this.text.slice(start, end));
  },

  skipNumberDecimals: function() {
    var ch;
    for (;;) {
      ch = this.peekChar();
      if (!digit.test(ch)) {
        return;
      }
      this.getChar();
    }
  },

  skipExponent: function() {
    var ch;
    ch = this.peekChar();
    if (ch !== "e" && ch !== "E") {
      return;
    }
    this.getChar();
    ch = this.getChar();
    if (ch === "+" || ch === "-") {
      ch = this.getChar();
    }
    for (;;) {
      ch = this.peekChar();
      if (!digit.test(ch)) {
        return;
      }
      this.getChar();
    }
  },

  // Starts at -.
  readArray: function() {
    var ar, ch, indent;
    ar = [];
    indent = this.currentIndent();
    while (this.currentIndent() >= indent) {
      ch = this.getChar();
      if (!ch) { break; }
      if (ch !== "-") {
        this.error('Invalid array, found ' + JSON.stringify(ch) +
            ' instead of "-".');
      }
      indent = this.currentIndent();  // Readjust indentation.
      ar.push(this.readPrimitive());
      this.skipWhitespace();
    }
    return ar;
  },

  // Starts with a key.
  readDictionary: function() {
    var start, end, key, ch, indent, dict;
    dict = {};
    indent = this.currentIndent();
    while (this.currentIndent() >= indent) {

      ch = this.peekChar();
      if (ch === '"') {
        key = this.readString();
        this.getChar();  // skip :.
      } else {
        start = end = this.offset;
        for (;;) {
          ch = this.getChar();
          if (!ch) { break; }
          if (ch === ":") {
            break;
          }
        }
        end = this.offset - 1;
        key = this.text.slice(start, end);
      }
      if (!ch) { break; }

      indent = this.currentIndent();  // Readjust indentation.
      dict[key] = this.readPrimitive();
      this.skipWhitespace();
    }

    return dict;
  }
}

SET.parse = function (text) {
  // `toplevel` is the object returned.
  var input = new SetStream(text);
  return input.readPrimitive();
};

// Stringify code.
//

SET.stringify = function SET_STRINGIFY(object, sizeIndent) {
  return set_stringify(object, sizeIndent,
    0,    // indentation
    false // indent the first item
  );
};

function set_stringify(object, sizeIndent, indentation, noIndentFirstItem) {
  var indent = "", i = 0, str = "";
  sizeIndent = sizeIndent || 2;
  for (i = 0; i < indentation * sizeIndent; i++) {
    indent += " ";
  }
  if (object === null || object === undefined) {
    return "nil";
  } else if (object === true) {
    return "yes";
  } else if (object === false) {
    return "no";
  } else if (typeof object === "number") {
    return new String(object);
  } else if (typeof object === "string") {
    return JSON.stringify(object);
  } else if (object instanceof Array) {
    for (i = 0; i < object.length; i++) {
      str += ((noIndentFirstItem && i === 0)? "": indent) + "- "
          + set_stringify(object[i], sizeIndent, indentation + 1, true)
          + '\n';
    }
  } else if (typeof object === "object") {
    i = 0;
    for (var e in object) {
      str += ((noIndentFirstItem && i === 0)? "": indent) + wrapKey(e) + ":"
          + (typeof object[e] === "object"? "\n": " ")
          + set_stringify(object[e], sizeIndent, indentation + 1)
          + '\n';
      i = 1;
    }
  }
  return str;
};

function wrapKey(key) {
  if (key[0] === '-' || key[0] === '"') {
    return JSON.stringify(new String(key));
  } else if (key.indexOf(':') === -1) {
    return new String(key);
  }
  return JSON.stringify(new String(key));
}

if (!window.module) {
  // node.js environment.
  module.exports = SET;
} else {
  window.SET = SET;
}

}(this));
<|endoftext|>


<commit_before>/**
 * --------------------------------------------------------------------------
 * Figuration (v2.0.0): tab.js
 * Licensed under MIT (https://github.com/cast-org/figuration/blob/master/LICENSE)
 * --------------------------------------------------------------------------
 */

(function($) {
    'use strict';

    var CFW_Widget_Tab = function(element, options) {
        this.$triggerElm = $(element);
        this.$navElm = null;
        this.$targetElm = null;

        this.settings = $.extend({}, CFW_Widget_Tab.DEFAULTS, this._parseDataAttr(), options);

        this._init();
    };

    CFW_Widget_Tab.DEFAULTS = {
        animate     : true,     // If tabs should be allowed fade in and out
        speed       : 150,      // Speed of animation in milliseconds
        hidden      : true      // Use aria-hidden on target containers by default
    };

    CFW_Widget_Tab.prototype = {

        _init : function() {
            var $selfRef = this;

            // Find nav and target elements
            this.$navElm = this.$triggerElm.closest('ul:not(.dropdown-menu)');
            this.$navElm.attr('role', 'tablist');

            var $selector = $(this.settings.target);
            if (!$selector.length) {
                $selector = $(this.$triggerElm.attr('href'));
            }
            this.$targetElm = $($selector);

            if (!this.$targetElm.length) {
                return false;
            }

            this.$triggerElm.attr('data-cfw', 'tab');

            // Check for presence of trigger id - set if not present
            var triggerID = this._getID(this.$triggerElm, 'cfw-tab');

            // Target should have id already - set ARIA attributes
            this.$targetElm.attr({
                'tabindex': -1,
                'role': 'tabpanel',
                'aria-labelledby': triggerID
            });
            if (this.settings.hidden) {
                this.$targetElm.attr('aria-hidden', true);
            }
            if (this.settings.animate) {
                this.fadeEnable();
            } else {
                this.fadeDisable();
            }

            // Set ARIA attributes on trigger
            this.$triggerElm.attr({
                'tabindex': -1,
                'role': 'tab',
                'aria-selected': 'false',
                'aria-expanded': 'false',
                'aria-controls': this.$targetElm.attr('id')
            });

            // Bind click handler
            this.$triggerElm.on('click', function(e) {
                $selfRef.show(e);
            });

            // Bind key handler
            this.$triggerElm.on('keydown', function(e) {
                $selfRef._actionsKeydown(e, this);
            });

            // Display panel if trigger is marked active
            if (this.$triggerElm.closest('li').hasClass('active')) {
                this.$triggerElm.attr({
                    'tabindex': 0,
                    'aria-selected': 'true',
                    'aria-expanded': 'true'
                });
                this.$targetElm.attr('tabindex', 0)
                    .addClass('active');

                if (this.settings.hidden) {
                    this.$targetElm.attr('aria-hidden', false);
                }
                if (this.settings.animate) {
                    this.$targetElm.addClass('in');
                }
            }

            // Check to see if there is an active element defined - if not set current one as active
            if (this.$navElm.find('li.active').length <= 0) {
                this.$triggerElm.closest('li').addClass('active');

                if (this.$triggerElm.parent('.dropdown-menu').length) {
                    this.$triggerElm.closest('li.dropdown').addClass('active');
                }

                this.$triggerElm.attr({
                    'tabindex': 0,
                    'aria-selected': 'true',
                    'aria-expanded': 'true'
                });
                this.$targetElm.attr('tabindex', 0)
                    .addClass('active');

                if (this.settings.hidden) {
                    this.$targetElm.attr('aria-hidden', 'false');
                }
                if (this.settings.animate) {
                    this.$targetElm.addClass('in');
                }
            }

            this._trigger(this.$triggerElm, 'init.cfw.tab');
        },

        show : function(e) {
            if (e) {
                e.preventDefault();
            }

            if (this.$triggerElm.parent('li').hasClass('active')
                || this.$triggerElm.hasClass('disabled')
                || this.$triggerElm[0].hasAttribute('disabled')) {
                return;
            }

            var $previous = this.$navElm.find('.active:last a[data-cfw="tab"]');
            if ($previous.length) {

                if (!this._trigger($previous, 'beforeHide.cfw.tab', { relatedTarget: this.$triggerElm[0] })) {
                    return;
                }
            }

            if (!this._trigger(this.$triggerElm, 'beforeShow.cfw.tab', { relatedTarget: $previous[0] })) {
                return;
            }

            if ($previous.length) {
                $previous.attr({
                    'tabindex': -1,
                    'aria-selected': 'false',
                    'aria-expanded': 'false'
                });
                this._trigger($previous, 'afterHide.cfw.tab', { relatedTarget: this.$triggerElm[0] });
                // Following line for backwards compatibility (not sure if used anywhere)
                this._trigger($previous, 'hide.cfw.tab');
            }

            this.$triggerElm.attr({
                'tabindex': 0,
                'aria-selected': 'true',
                'aria-expanded': 'true'
            });

            this._activateTab(this.$triggerElm.closest('li'), this.$navElm, false, $previous);
            this._activateTab(this.$targetElm, this.$targetElm.parent(), true, $previous);
        },

        fadeEnable : function() {
            if (!$.support.transitionEnd) { return; }
            this.$targetElm.addClass('fade');
            if (this.$targetElm.hasClass('active')) {
                this.$targetElm.addClass('in');
            }
            this.settings.animate = true;
        },

        fadeDisable : function() {
            this.$targetElm.removeClass('fade in');
            if (this.$targetElm.hasClass('active')) {
                this.$targetElm.addClass('in');
            }
            this.settings.animate = false;
        },

        hiddenDisable : function() {
            this.$targetElm.removeAttr('aria-hidden');
            this.settings.hidden = false;
        },

        _actionsKeydown : function(e, node) {
            // 37-left, 38-up, 39-right, 40-down
            var k = e.which;
            if (!/(37|38|39|40)/.test(k)) { return; }

            e.stopPropagation();
            e.preventDefault();

            var $node = $(node);
            var $ul = $node.closest('ul[role="tablist"]');
            var $items = $ul.find('[role="tab"]:visible');
            var index = $items.index($items.filter('[aria-selected="true"]'));

            if ((k == 38 || k == 37) && index > 0)                 { index--; }     // up & left
            if ((k == 39 || k == 40) && index < $items.length - 1) { index++; }     // down & right
            if (!~index)                                           { index = 0; }   // force first item

            var nextTab = $items.eq(index);
            nextTab.CFW_Tab('show').trigger('focus');
        },

        _activateTab : function(node, container, isPanel, $previous) {
            var $selfRef = this;
            var $prevActive = container.find('> .active');
            var doTransition = isPanel && $.support.transitionEnd && this.settings.animate;

            function displayTab() {
                $prevActive.removeClass('active')
                    .find('> .dropdown-menu > .active')
                    .removeClass('active');

                node.addClass('active');

                if (isPanel) {
                    $prevActive.attr({
                        'tabindex': -1,
                        'aria-hidden': 'true'
                    });
                    node.attr({
                        'tabindex': 0,
                        'aria-hidden': 'false'
                    });
                }

                if (doTransition) {
                    node[0].offsetWidth; // Reflow for transition
                    node.addClass('in');
                } else {
                    if (isPanel) {
                        $selfRef.settings.animate = false;
                    }
                    node.removeClass('fade');
                }

                if (node.parent('.dropdown-menu').length) {
                    node.closest('li.dropdown').addClass('active');
                }

                if (isPanel) {
                    $selfRef._trigger($selfRef.$triggerElm, 'afterShow.cfw.tab', { relatedTarget: $previous[0] });
                }
            }

            if (doTransition) {
                node.one('cfwTransitionEnd', displayTab)
                .CFW_emulateTransitionEnd(this.settings.speed);
            } else {
                displayTab();
            }

            $prevActive.removeClass('in');
        },

        _getID : function($node, prefix) {
            var nodeID = $node.attr('id');
            if (nodeID === undefined) {
                do nodeID = prefix + '-' + ~~(Math.random() * 1000000);
                while (document.getElementById(nodeID));
                $node.attr('id', nodeID);
            }
            return nodeID;
        },

        _parseDataAttr : function() {
            var parsedData = {};
            var data = this.$triggerElm.data();

            if (typeof data.cfwTabTarget  !== 'undefined') { parsedData.target  = data.cfwTabTarget;  }
            if (typeof data.cfwTabAnimate !== 'undefined') { parsedData.animate = data.cfwTabAnimate; }
            if (typeof data.cfwTabSpeed   !== 'undefined') { parsedData.speed   = data.cfwTabSpeed;   }
            if (typeof data.cfwTabHidden  !== 'undefined') { parsedData.hidden  = data.cfwTabHidden;  }
            return parsedData;
        },

        _trigger : function($callingElm, eventName, extraData) {
            var e = $.Event(eventName);
            if ($.isPlainObject(extraData)) {
                e = $.extend({}, e, extraData);
            }
            $callingElm.trigger(e);
            if (e.isDefaultPrevented()) {
                return false;
            }
            return true;
        }
    };

    function Plugin(option) {
        var args = [].splice.call(arguments, 1);
        return this.each(function() {
            var $this = $(this);
            var data = $this.data('cfw.tab');
            var options = typeof option === 'object' && option;

            if (!data) {
                $this.data('cfw.tab', (data = new CFW_Widget_Tab(this, options)));
            }
            if (typeof option === 'string') {
                data[option].apply(data, args);
            }
        });
    }

    $.fn.CFW_Tab = Plugin;
    $.fn.CFW_Tab.Constructor = CFW_Widget_Tab;

})(jQuery);
<commit_msg>stop tabs from focusing disabled items<commit_after>/**
 * --------------------------------------------------------------------------
 * Figuration (v2.0.0): tab.js
 * Licensed under MIT (https://github.com/cast-org/figuration/blob/master/LICENSE)
 * --------------------------------------------------------------------------
 */

(function($) {
    'use strict';

    var CFW_Widget_Tab = function(element, options) {
        this.$triggerElm = $(element);
        this.$navElm = null;
        this.$targetElm = null;

        this.settings = $.extend({}, CFW_Widget_Tab.DEFAULTS, this._parseDataAttr(), options);

        this._init();
    };

    CFW_Widget_Tab.DEFAULTS = {
        animate     : true,     // If tabs should be allowed fade in and out
        speed       : 150,      // Speed of animation in milliseconds
        hidden      : true      // Use aria-hidden on target containers by default
    };

    CFW_Widget_Tab.prototype = {

        _init : function() {
            var $selfRef = this;

            // Find nav and target elements
            this.$navElm = this.$triggerElm.closest('ul:not(.dropdown-menu)');
            this.$navElm.attr('role', 'tablist');

            var $selector = $(this.settings.target);
            if (!$selector.length) {
                $selector = $(this.$triggerElm.attr('href'));
            }
            this.$targetElm = $($selector);

            if (!this.$targetElm.length) {
                return false;
            }

            this.$triggerElm.attr('data-cfw', 'tab');

            // Check for presence of trigger id - set if not present
            var triggerID = this._getID(this.$triggerElm, 'cfw-tab');

            // Target should have id already - set ARIA attributes
            this.$targetElm.attr({
                'tabindex': -1,
                'role': 'tabpanel',
                'aria-labelledby': triggerID
            });
            if (this.settings.hidden) {
                this.$targetElm.attr('aria-hidden', true);
            }
            if (this.settings.animate) {
                this.fadeEnable();
            } else {
                this.fadeDisable();
            }

            // Set ARIA attributes on trigger
            this.$triggerElm.attr({
                'tabindex': -1,
                'role': 'tab',
                'aria-selected': 'false',
                'aria-expanded': 'false',
                'aria-controls': this.$targetElm.attr('id')
            });

            // Bind click handler
            this.$triggerElm.on('click', function(e) {
                $selfRef.show(e);
            });

            // Bind key handler
            this.$triggerElm.on('keydown', function(e) {
                $selfRef._actionsKeydown(e, this);
            });

            // Display panel if trigger is marked active
            if (this.$triggerElm.closest('li').hasClass('active')) {
                this.$triggerElm.attr({
                    'tabindex': 0,
                    'aria-selected': 'true',
                    'aria-expanded': 'true'
                });
                this.$targetElm.attr('tabindex', 0)
                    .addClass('active');

                if (this.settings.hidden) {
                    this.$targetElm.attr('aria-hidden', false);
                }
                if (this.settings.animate) {
                    this.$targetElm.addClass('in');
                }
            }

            // Check to see if there is an active element defined - if not set current one as active
            if (this.$navElm.find('li.active').length <= 0) {
                this.$triggerElm.closest('li').addClass('active');

                if (this.$triggerElm.parent('.dropdown-menu').length) {
                    this.$triggerElm.closest('li.dropdown').addClass('active');
                }

                this.$triggerElm.attr({
                    'tabindex': 0,
                    'aria-selected': 'true',
                    'aria-expanded': 'true'
                });
                this.$targetElm.attr('tabindex', 0)
                    .addClass('active');

                if (this.settings.hidden) {
                    this.$targetElm.attr('aria-hidden', 'false');
                }
                if (this.settings.animate) {
                    this.$targetElm.addClass('in');
                }
            }

            this._trigger(this.$triggerElm, 'init.cfw.tab');
        },

        show : function(e) {
            if (e) {
                e.preventDefault();
            }

            if (this.$triggerElm.parent('li').hasClass('active')
                || this.$triggerElm.hasClass('disabled')
                || this.$triggerElm[0].hasAttribute('disabled')) {
                return;
            }

            var $previous = this.$navElm.find('.active:last a[data-cfw="tab"]');
            if ($previous.length) {

                if (!this._trigger($previous, 'beforeHide.cfw.tab', { relatedTarget: this.$triggerElm[0] })) {
                    return;
                }
            }

            if (!this._trigger(this.$triggerElm, 'beforeShow.cfw.tab', { relatedTarget: $previous[0] })) {
                return;
            }

            if ($previous.length) {
                $previous.attr({
                    'tabindex': -1,
                    'aria-selected': 'false',
                    'aria-expanded': 'false'
                });
                this._trigger($previous, 'afterHide.cfw.tab', { relatedTarget: this.$triggerElm[0] });
                // Following line for backwards compatibility (not sure if used anywhere)
                this._trigger($previous, 'hide.cfw.tab');
            }

            this.$triggerElm.attr({
                'tabindex': 0,
                'aria-selected': 'true',
                'aria-expanded': 'true'
            });

            this._activateTab(this.$triggerElm.closest('li'), this.$navElm, false, $previous);
            this._activateTab(this.$targetElm, this.$targetElm.parent(), true, $previous);
        },

        fadeEnable : function() {
            if (!$.support.transitionEnd) { return; }
            this.$targetElm.addClass('fade');
            if (this.$targetElm.hasClass('active')) {
                this.$targetElm.addClass('in');
            }
            this.settings.animate = true;
        },

        fadeDisable : function() {
            this.$targetElm.removeClass('fade in');
            if (this.$targetElm.hasClass('active')) {
                this.$targetElm.addClass('in');
            }
            this.settings.animate = false;
        },

        hiddenDisable : function() {
            this.$targetElm.removeAttr('aria-hidden');
            this.settings.hidden = false;
        },

        _actionsKeydown : function(e, node) {
            // 37-left, 38-up, 39-right, 40-down
            var k = e.which;
            if (!/(37|38|39|40)/.test(k)) { return; }

            e.stopPropagation();
            e.preventDefault();

            var $node = $(node);
            var $ul = $node.closest('ul[role="tablist"]');
            var $items = $ul.find('[role="tab"]:visible');
            var index = $items.index($items.filter('[aria-selected="true"]'));

            if ((k == 38 || k == 37) && index > 0)                 { index--; }     // up & left
            if ((k == 39 || k == 40) && index < $items.length - 1) { index++; }     // down & right
            if (!~index)                                           { index = 0; }   // force first item

            var $nextTab = $items.eq(index);
            if (!$nextTab.hasClass('disabled')) {
                $nextTab.CFW_Tab('show').trigger('focus');
            }
        },

        _activateTab : function(node, container, isPanel, $previous) {
            var $selfRef = this;
            var $prevActive = container.find('> .active');
            var doTransition = isPanel && $.support.transitionEnd && this.settings.animate;

            function displayTab() {
                $prevActive.removeClass('active')
                    .find('> .dropdown-menu > .active')
                    .removeClass('active');

                node.addClass('active');

                if (isPanel) {
                    $prevActive.attr({
                        'tabindex': -1,
                        'aria-hidden': 'true'
                    });
                    node.attr({
                        'tabindex': 0,
                        'aria-hidden': 'false'
                    });
                }

                if (doTransition) {
                    node[0].offsetWidth; // Reflow for transition
                    node.addClass('in');
                } else {
                    if (isPanel) {
                        $selfRef.settings.animate = false;
                    }
                    node.removeClass('fade');
                }

                if (node.parent('.dropdown-menu').length) {
                    node.closest('li.dropdown').addClass('active');
                }

                if (isPanel) {
                    $selfRef._trigger($selfRef.$triggerElm, 'afterShow.cfw.tab', { relatedTarget: $previous[0] });
                }
            }

            if (doTransition) {
                node.one('cfwTransitionEnd', displayTab)
                .CFW_emulateTransitionEnd(this.settings.speed);
            } else {
                displayTab();
            }

            $prevActive.removeClass('in');
        },

        _getID : function($node, prefix) {
            var nodeID = $node.attr('id');
            if (nodeID === undefined) {
                do nodeID = prefix + '-' + ~~(Math.random() * 1000000);
                while (document.getElementById(nodeID));
                $node.attr('id', nodeID);
            }
            return nodeID;
        },

        _parseDataAttr : function() {
            var parsedData = {};
            var data = this.$triggerElm.data();

            if (typeof data.cfwTabTarget  !== 'undefined') { parsedData.target  = data.cfwTabTarget;  }
            if (typeof data.cfwTabAnimate !== 'undefined') { parsedData.animate = data.cfwTabAnimate; }
            if (typeof data.cfwTabSpeed   !== 'undefined') { parsedData.speed   = data.cfwTabSpeed;   }
            if (typeof data.cfwTabHidden  !== 'undefined') { parsedData.hidden  = data.cfwTabHidden;  }
            return parsedData;
        },

        _trigger : function($callingElm, eventName, extraData) {
            var e = $.Event(eventName);
            if ($.isPlainObject(extraData)) {
                e = $.extend({}, e, extraData);
            }
            $callingElm.trigger(e);
            if (e.isDefaultPrevented()) {
                return false;
            }
            return true;
        }
    };

    function Plugin(option) {
        var args = [].splice.call(arguments, 1);
        return this.each(function() {
            var $this = $(this);
            var data = $this.data('cfw.tab');
            var options = typeof option === 'object' && option;

            if (!data) {
                $this.data('cfw.tab', (data = new CFW_Widget_Tab(this, options)));
            }
            if (typeof option === 'string') {
                data[option].apply(data, args);
            }
        });
    }

    $.fn.CFW_Tab = Plugin;
    $.fn.CFW_Tab.Constructor = CFW_Widget_Tab;

})(jQuery);
<|endoftext|>


<commit_before>var runtimes = new function()
{
  var __runtimes = {};

  __windowsFolding = {};

  var self = this;

  var registerRuntime = function(id)
  {
    if( !(id in __runtimes) )
    {
      opera.postError('runtime id does not exist')
      __runtimes[id] = null;
      services['ecmascript-debugger'].getRuntime( tagManager.setCB(null, parseRuntime), id );
    }
  }

  var removeRuntime = function(id)
  {
    var sc = null ;
    /*
    TODO check for existing breakpoints before cleaning up
    for( sc in __scripts )
    {
      if( __scripts[sc]['runtime-id'] == id )
      {
        delete __scripts[sc];
      }
    }
    */
    delete __runtimes[id];
  }

  this.handleRuntimeStarted = function(xml)
  {
    parseRuntime(xml);
  }

  this.handleRuntimesReplay = function(xml)
  {
    parseRuntime(xml);
  }

  var parseRuntime = function(xml)
  {
    var r_ts = xml.getElementsByTagName('runtime'), r_t=null, i=0;
    var runtimeId = '', runtime=null, prop = '',
      children = null, child = null, j = 0;
    var cur = '';
    for ( ; r_t = r_ts[i]; i++)
    {
      runtimeId = r_t.getNodeData('runtime-id');
      if(runtimeId)
      {
        runtime={};
        children = r_t.childNodes;
        for(j=0 ; child = children[j]; j++)
        {
          runtime[child.nodeName] = child.textContent;
        }
        for( cur in __runtimes )
        {
          if( __runtimes[cur] && __runtimes[cur]['uri'] == runtime['uri'] )
          {
            if( __runtimes[cur]['unfolded'] )
            {
              runtime['unfolded'] = __runtimes[cur]['unfolded'];
            }
            delete __runtimes[cur];
          }
        }
        getTitleRuntime(runtimeId);
        __runtimes[runtimeId] = runtime;

        if(__next_runtime_id_to_select == runtimeId)
        {
          self.setSelectedRuntime(runtime);
          __next_runtime_id_to_select = '';
        }

        views.runtimes.update();
      }
    }
  }

  var getTitleRuntime = function(rt_id)
  {
    var tag = tagManager.setCB(null, parseGetTitle, [rt_id]);
    var script = "return $" + rt_id + ".document.title || ''";
    services['ecmascript-debugger'].eval(tag, rt_id, '', '', script, ['$' + rt_id, rt_id] );
  }

  var parseGetTitle = function(xml, rt_id)
  {
    if(xml.getNodeData('status') == 'completed' )
    {
      __runtimes[rt_id]['title'] = xml.getNodeData('string');
    }
    else
    {
      opera.postError('getting title has failed in runtimes getTitleRuntime');
    }
  }

  var __scripts = {};

/** checks if that script is already known from a previous runtime
  * checks first for the url and the for the script data.
  * Both checks are not really reliable.
  * TODO we need a better logic to handle this
  */
  var registerScript = function(script)
  {
    var sc = null, is_known = false;
    var new_script_id = script['script-id'];
    for( sc in __scripts )
    {
      if( ( __scripts[sc]['uri'] && ( __scripts[sc]['uri'] == script['uri'] ) )|| __scripts[sc]['script-data'] == script['script-data'] )
      {
        is_known = true;
        break;
      }
    }

    if( is_known )
    {
      var old_b_ps = __scripts[sc]['breakpoints'];
      var new_b_ps = script['breakpoints'];
      for( b_p in old_b_ps )
      {
        services['ecmascript-debugger'].setBreakpoint(new_script_id, b_p, old_b_ps[b_p]);
        new_b_ps[b_p] = old_b_ps[b_p];
      }
      delete __scripts[sc];
    }

    __scripts[new_script_id] = script;

    var a ='', b='';
    for( a in __scripts ) b+=a+'; ';

  }

/*
"<new-script>"
   "<runtime-id>" UNSIGNED "</runtime-id>"
   "<script-id>" UNSIGNED "</script-id>"
   "<script-type>"
      ( "inline" | "event" | "linked" | "timeout" | "java" | "unknown" )
   "</script-type>"
   "<script-data>" TEXT "</script-data>"
   "<uri>" TEXT "</uri>"             ; present if SCRIPT-TYPE is "linked"
 "</new-script>" ;
*/

  var breakpoint_count = 1;

  var getBreakpointId = function()
  {
    return ( breakpoint_count++ ).toString();
  }

  var script_count = 1;

  var getScriptId = function()
  {
    return ( script_count++ ).toString();
  }

  this.handle = function(new_script_event)
  {
    var script = {};
    var children = new_script_event.documentElement.childNodes, child=null, i=0;
    for ( ; child = children[i]; i++)
    {
      script[child.nodeName] = child.firstChild.nodeValue;
    }
    script['breakpoints'] = {};
    registerRuntime( script['runtime-id'] );
    registerScript( script );
    views.runtimes.update();
  }

  var thread_queues = {};
  var current_threads = {};
  var threads = {}

  var runtime_stoped_queue = [];
  var stoped_threads = {};

  /// clear_thread_id(rt_id, thread_id);
  var clear_thread_id = function(rt_id, thread_id)
  {
    var cur = '', i = 0;
    var thread_queue = thread_queues[rt_id];
    var current_thread = current_threads[rt_id];
    // it seems that the order of the thread-finished events can get reversed
    for(i = 0 ; cur = current_thread[i]; i++)
    {
      if( cur == thread_id )
      {
        current_thread.splice(i, 1);
        break;
      }
    }
    for(i = 0 ; cur = thread_queue[i]; i++)
    {
      if( cur == thread_id )
      {
        thread_queue.splice(i, 1);
        delete stoped_threads[rt_id][thread_id];
        return true;
      }
    }
    return false;
  }

/*

  <thread-started>
    <runtime-id>3</runtime-id>
    <thread-id>3</thread-id>
    <parent-thread-id>0</parent-thread-id>
    <thread-type>inline</thread-type>
  </thread-started>

  <thread-finished>
    <runtime-id>3</runtime-id>
    <thread-id>3</thread-id>
    <status>completed</status>
    <value type="null"/>
  </thread-finished>

  */

  /* stop_at:

    this.getControlsEnabled = function()
  {
    return __controls_enabled;
  }

  */

  this.handleThreadStarted = function(xml)
  {
    var rt_id = xml.getNodeData("runtime-id");
    var id = xml.getNodeData("thread-id");
    var parent_thread_id = xml.getNodeData("parent-thread-id");
    var thread_queue = thread_queues[rt_id] || ( thread_queues[rt_id] = [] );
    var current_thread = current_threads[rt_id] || ( current_threads[rt_id] = [] );
    thread_queue[thread_queue.length] = id;
    if( !current_thread.length ||
      ( parent_thread_id != '0' && parent_thread_id == current_thread[ current_thread.length - 1 ] ) )
    {
      current_thread[current_thread.length] = id;
    }
  }

  this.handleThreadStopedAt = function(xml)
  {
    var rt_id = xml.getNodeData("runtime-id");
    var thread_id = xml.getNodeData("thread-id");
    var current_thread = current_threads[rt_id];
    // the current thread id must be set in 'thread-started' event
    if( !stop_at.getControlsEnabled () && thread_id == current_thread[ current_thread.length - 1 ] )
    {
      stop_at.handle(xml);
    }
    else
    {
      // it is sure to assume that per runtime there can be only one <stoped-at> event
      if( ! stoped_threads[rt_id] )
      {
        stoped_threads[rt_id] = {};
      }
      stoped_threads[rt_id] = xml;
      runtime_stoped_queue[runtime_stoped_queue.length] = rt_id;
    }
  }

  this.handleThreadFinished = function(xml)
  {
      /* TODO
      status "completed" | "unhandled-exception" | "aborted" | "cancelled-by-scheduler"
      */
      var rt_id = xml.getNodeData("runtime-id");
      var thread_id = xml.getNodeData("thread-id");
      clear_thread_id(rt_id, thread_id);
      if( !stop_at.getControlsEnabled () && runtime_stoped_queue.length )
      {
        stop_at.handle( stoped_threads[runtime_stoped_queue.shift()] );
      }
      /*
      for( ; thread_id = thread_queue[i]; i++)
      {
        if( thread = threads[thread_id] )
        {
          parent_thread_id = thread.getNodeData("parent-thread-id");
          if( !current_thread.length ||
              ( parent_thread_id != '0' && parent_thread_id == current_thread[ current_thread.length - 1 ] ) )
          {
            current_thread[current_thread.length] = thread_id;
            stop_at.handle(threads[thread_id]);
            break;
          }
        }
      }
      */
    }

    // messages.post('host-state', {state: 'ready'});
    // fires when stop_at releases the control to the host
    // if there is already a <thread-stoped> event in the queue
    // it has to be handled here
    var onHostStateChange = function(msg)
    {
      if( !stop_at.getControlsEnabled() && runtime_stoped_queue.length )
      {
        stop_at.handle( stoped_threads[runtime_stoped_queue.shift()] );
      }
    }

  /*
  <runtime-stopped>
  <runtime-id>1</runtime-id>
</runtime-stopped>

*/
  this.handleRuntimeStoped = function(xml)
  {
    var rt_id = xml.getNodeData('runtime-id');
    if(rt_id)
    {
      removeRuntime(rt_id);
      views.runtimes.update();
      var script_id = views.js_source.getCurrentScriptId();
      if( script_id  && ( self.getScriptsRuntimeId(script_id) == rt_id ) )
      {
        views.js_source.clearView();
      }
    }
  }

  // windows means runtime containers here to stay in sync with the xml protocol

  this.getWindows = function()
  {
    var ret = [], r = '', is_unfolded = true;
    for( r in __runtimes )
    {
      if( __runtimes[r]['html-frame-path'] && __runtimes[r]['html-frame-path'].indexOf('[') == -1 )
      {
        is_unfolded = true;
        if( __windowsFolding[__runtimes[r]['window-id']] === false )
        {
          is_unfolded = false;
        }
        ret[ret.length] =
        {
          id: __runtimes[r]['window-id'],
          uri: __runtimes[r]['uri'],
          title: __runtimes[r]['title'] || '',
          is_unfolded: is_unfolded,
          runtimes: this.getRuntimes( __runtimes[r]['window-id'] )
        }
      }
    }
    return ret;
  }

  this.getRuntimes = function(window_id)
  {
    var ret = [], r = '';
    for( r in __runtimes )
    {
      if ( __runtimes[r] && __runtimes[r]['window-id'] &&  __runtimes[r]['window-id'] == window_id )
      {
        ret[ret.length] = __runtimes[r];
      }
    }
    return ret;
  }

  this.getRuntimeIdWithURL = function(url)
  {
    var r = '';
    for( r in __runtimes )
    {
      if( __runtimes[r]['uri'] == url )
      {
        return __runtimes[r];
      }
    }
    return null;
  }

  this.getScript = function(scriptId)
  {
    return __scripts[scriptId];
  }

  this.getScriptsRuntimeId = function(scriptId)
  {
    return __scripts[scriptId] && __scripts[scriptId]['runtime-id'] || null;
  }

  this.getScriptSource = function(scriptId)
  {
    return __scripts[scriptId] && __scripts[scriptId]['script-data'] || null;
  }

  this.getScripts = function(runtime_id)
  {
    var ret=[], script = null, cur = '';
    for( cur in __scripts )
    {
      script = __scripts[cur];
      if(script['runtime-id'] == runtime_id)
      {
        ret[ret.length] = script;
      }
    }
    return ret;
  }

  this.hasBreakpoint = function(script_id, line_nr)
  {
    return (line_nr in __scripts[script_id]['breakpoints']);
  }

  this.setBreakpoint = function(script_id, line_nr)
  {
    var b_p_id = __scripts[script_id]['breakpoints'][line_nr] = getBreakpointId();
    services['ecmascript-debugger'].setBreakpoint(script_id, line_nr, b_p_id);
  }

  this.removeBreakpoint = function(script_id, line_nr)
  {
    services['ecmascript-debugger'].removeBreakpoint( __scripts[script_id]['breakpoints'][line_nr] );
    delete __scripts[script_id]['breakpoints'][line_nr];
  }

  this.getBreakpoints = function(script_id)
  {
    return __scripts[script_id] && __scripts[script_id]['breakpoints'];
  }

  this.setUnfolded = function(runtime_id, is_unfolded)
  {

    if( __runtimes[runtime_id] )
    {
      //alert(__runtimes[runtime_id]+' '+is_unfolded);
      __runtimes[runtime_id]['unfolded'] = is_unfolded;
    }
  }

  this.setWindowUnfolded = function(window_id, is_unfolded)
  {
    __windowsFolding[window_id] = is_unfolded;
  }

  this.setObserve = function(runtime_id, observe)
  {
    if( __runtimes[runtime_id] )
    {
      __runtimes[runtime_id]['observe'] = observe;
    }
  }

  this.getObserve = function(runtime_id)
  {
    return __runtimes[runtime_id] && __runtimes[runtime_id]['observe']  || false;
  }

  // this is a temporary solution as long as we don't have a concept for tabs

  var __selected_runtime_id = '';

  var __next_runtime_id_to_select = ''

  this.setSelectedRuntime = function(runtime)
  {
    var r = '';
    for( r in __runtimes )
    {
      if( __runtimes[r] == runtime )
      {
        __runtimes[r]['selected'] = true;
        __selected_runtime_id = __runtimes[r]['runtime-id'];
      }
      else
      {
        // the runtime could be registered but not jet parsed
        if( __runtimes[r] )
        {
          __runtimes[r]['selected'] = false;
        }
      }
    }
  }

  this.setSelectedScript = function( runtime, id )
  {
    var scripts = this.getScripts(runtime['runtime-id']), script = null, i = 0;
    for( ; script = scripts[i]; i++)
    {
      script.selected = script['script-id'] == id ? true : false;
    }
  }

  this.setSelectedRuntimeId = function(id)
  {
    if(__runtimes[id])
    {
      this.setSelectedRuntime(__runtimes[id]);
      views.runtimes.update();
    }
    else
    {
      __next_runtime_id_to_select = id;
    }
  }

  this.getSelectedRuntimeId = function()
  {
    return __selected_runtime_id;
  }

  messages.addListener('host-state', onHostStateChange);

}

//<commit_msg>introduced thread events per runtime ( fix for Bug 308151 )<commit_after>var runtimes = new function()
{
  var __runtimes = {};

  __windowsFolding = {};

  var self = this;

  var registerRuntime = function(id)
  {
    if( !(id in __runtimes) )
    {
      opera.postError('runtime id does not exist')
      __runtimes[id] = null;
      services['ecmascript-debugger'].getRuntime( tagManager.setCB(null, parseRuntime), id );
    }
  }

  var removeRuntime = function(id)
  {
    var sc = null ;
    /*
    TODO check for existing breakpoints before cleaning up
    for( sc in __scripts )
    {
      if( __scripts[sc]['runtime-id'] == id )
      {
        delete __scripts[sc];
      }
    }
    */
    delete __runtimes[id];
  }

  this.handleRuntimeStarted = function(xml)
  {
    parseRuntime(xml);
  }

  this.handleRuntimesReplay = function(xml)
  {
    parseRuntime(xml);
  }

  var parseRuntime = function(xml)
  {
    var r_ts = xml.getElementsByTagName('runtime'), r_t=null, i=0;
    var runtimeId = '', runtime=null, prop = '',
      children = null, child = null, j = 0;
    var cur = '';
    for ( ; r_t = r_ts[i]; i++)
    {
      runtimeId = r_t.getNodeData('runtime-id');
      if(runtimeId)
      {
        runtime={};
        children = r_t.childNodes;
        for(j=0 ; child = children[j]; j++)
        {
          runtime[child.nodeName] = child.textContent;
        }
        for( cur in __runtimes )
        {
          if( __runtimes[cur] && __runtimes[cur]['uri'] == runtime['uri'] )
          {
            if( __runtimes[cur]['unfolded'] )
            {
              runtime['unfolded'] = __runtimes[cur]['unfolded'];
            }
            delete __runtimes[cur];
          }
        }
        getTitleRuntime(runtimeId);
        __runtimes[runtimeId] = runtime;

        if(__next_runtime_id_to_select == runtimeId)
        {
          self.setSelectedRuntime(runtime);
          __next_runtime_id_to_select = '';
        }

        views.runtimes.update();
      }
    }
  }

  var getTitleRuntime = function(rt_id)
  {
    var tag = tagManager.setCB(null, parseGetTitle, [rt_id]);
    var script = "return $" + rt_id + ".document.title || ''";
    services['ecmascript-debugger'].eval(tag, rt_id, '', '', script, ['$' + rt_id, rt_id] );
  }

  var parseGetTitle = function(xml, rt_id)
  {
    if(xml.getNodeData('status') == 'completed' )
    {
      __runtimes[rt_id]['title'] = xml.getNodeData('string');
    }
    else
    {
      opera.postError('getting title has failed in runtimes getTitleRuntime');
    }
  }

  var __scripts = {};

/** checks if that script is already known from a previous runtime
  * checks first for the url and the for the script data.
  * Both checks are not really reliable.
  * TODO we need a better logic to handle this
  */
  var registerScript = function(script)
  {
    var sc = null, is_known = false;
    var new_script_id = script['script-id'];
    for( sc in __scripts )
    {
      if( ( __scripts[sc]['uri'] && ( __scripts[sc]['uri'] == script['uri'] ) )|| __scripts[sc]['script-data'] == script['script-data'] )
      {
        is_known = true;
        break;
      }
    }

    if( is_known )
    {
      var old_b_ps = __scripts[sc]['breakpoints'];
      var new_b_ps = script['breakpoints'];
      for( b_p in old_b_ps )
      {
        services['ecmascript-debugger'].setBreakpoint(new_script_id, b_p, old_b_ps[b_p]);
        new_b_ps[b_p] = old_b_ps[b_p];
      }
      delete __scripts[sc];
    }

    __scripts[new_script_id] = script;

    var a ='', b='';
    for( a in __scripts ) b+=a+'; ';

  }

/*
"<new-script>"
   "<runtime-id>" UNSIGNED "</runtime-id>"
   "<script-id>" UNSIGNED "</script-id>"
   "<script-type>"
      ( "inline" | "event" | "linked" | "timeout" | "java" | "unknown" )
   "</script-type>"
   "<script-data>" TEXT "</script-data>"
   "<uri>" TEXT "</uri>"             ; present if SCRIPT-TYPE is "linked"
 "</new-script>" ;
*/

  var breakpoint_count = 1;

  var getBreakpointId = function()
  {
    return ( breakpoint_count++ ).toString();
  }

  var script_count = 1;

  var getScriptId = function()
  {
    return ( script_count++ ).toString();
  }

  this.handle = function(new_script_event)
  {
    var script = {};
    var children = new_script_event.documentElement.childNodes, child=null, i=0;
    for ( ; child = children[i]; i++)
    {
      script[child.nodeName] = child.firstChild.nodeValue;
    }
    script['breakpoints'] = {};
    registerRuntime( script['runtime-id'] );
    registerScript( script );
    views.runtimes.update();
  }

  var thread_queues = {};
  var current_threads = {};

  var runtime_stoped_queue = [];
  var stoped_threads = {};

  var clear_thread_id = function(rt_id, thread_id)
  {
    var cur = '', i = 0;
    var thread_queue = thread_queues[rt_id];
    var current_thread = current_threads[rt_id];
    // it seems that the order of the thread-finished events can get reversed
    for(i = 0 ; cur = current_thread[i]; i++)
    {
      if( cur == thread_id )
      {
        current_thread.splice(i, 1);
        break;
      }
    }
    for(i = 0 ; cur = thread_queue[i]; i++)
    {
      if( cur == thread_id )
      {
        thread_queue.splice(i, 1);
        delete stoped_threads[rt_id][thread_id];
        return true;
      }
    }
    return false;
  }

/*

  <thread-started>
    <runtime-id>3</runtime-id>
    <thread-id>3</thread-id>
    <parent-thread-id>0</parent-thread-id>
    <thread-type>inline</thread-type>
  </thread-started>

  <thread-finished>
    <runtime-id>3</runtime-id>
    <thread-id>3</thread-id>
    <status>completed</status>
    <value type="null"/>
  </thread-finished>

  */

  this.handleThreadStarted = function(xml)
  {
    var rt_id = xml.getNodeData("runtime-id");
    var id = xml.getNodeData("thread-id");
    var parent_thread_id = xml.getNodeData("parent-thread-id");
    var thread_queue = thread_queues[rt_id] || ( thread_queues[rt_id] = [] );
    var current_thread = current_threads[rt_id] || ( current_threads[rt_id] = [] );
    thread_queue[thread_queue.length] = id;
    if( !current_thread.length ||
      ( parent_thread_id != '0' && parent_thread_id == current_thread[ current_thread.length - 1 ] ) )
    {
      current_thread[current_thread.length] = id;
    }
  }

  this.handleThreadStopedAt = function(xml)
  {
    var rt_id = xml.getNodeData("runtime-id");
    var thread_id = xml.getNodeData("thread-id");
    var current_thread = current_threads[rt_id];
    // the current thread id must be set in 'thread-started' event
    if( !stop_at.getControlsEnabled () && thread_id == current_thread[ current_thread.length - 1 ] )
    {
      stop_at.handle(xml);
    }
    else
    {
      // it is sure to assume that per runtime there can be only one <stoped-at> event
      if( ! stoped_threads[rt_id] )
      {
        stoped_threads[rt_id] = {};
      }
      stoped_threads[rt_id] = xml;
      runtime_stoped_queue[runtime_stoped_queue.length] = rt_id;
    }
  }

  this.handleThreadFinished = function(xml)
  {
      /* TODO
      status "completed" | "unhandled-exception" | "aborted" | "cancelled-by-scheduler"
      */
      var rt_id = xml.getNodeData("runtime-id");
      var thread_id = xml.getNodeData("thread-id");
      clear_thread_id(rt_id, thread_id);
      if( !stop_at.getControlsEnabled () && runtime_stoped_queue.length )
      {
        stop_at.handle( stoped_threads[runtime_stoped_queue.shift()] );
      }
    }

    // messages.post('host-state', {state: 'ready'});
    // fires when stop_at releases the control to the host
    // if there is already a <thread-stoped> event in the queue
    // it has to be handled here
    var onHostStateChange = function(msg)
    {
      if( !stop_at.getControlsEnabled() && runtime_stoped_queue.length )
      {
        stop_at.handle( stoped_threads[runtime_stoped_queue.shift()] );
      }
    }

  /*
  <runtime-stopped>
  <runtime-id>1</runtime-id>
</runtime-stopped>

*/
  this.handleRuntimeStoped = function(xml)
  {
    var rt_id = xml.getNodeData('runtime-id');
    if(rt_id)
    {
      removeRuntime(rt_id);
      views.runtimes.update();
      var script_id = views.js_source.getCurrentScriptId();
      if( script_id  && ( self.getScriptsRuntimeId(script_id) == rt_id ) )
      {
        views.js_source.clearView();
      }
    }
  }

  // windows means runtime containers here to stay in sync with the xml protocol

  this.getWindows = function()
  {
    var ret = [], r = '', is_unfolded = true;
    for( r in __runtimes )
    {
      if( __runtimes[r]['html-frame-path'] && __runtimes[r]['html-frame-path'].indexOf('[') == -1 )
      {
        is_unfolded = true;
        if( __windowsFolding[__runtimes[r]['window-id']] === false )
        {
          is_unfolded = false;
        }
        ret[ret.length] =
        {
          id: __runtimes[r]['window-id'],
          uri: __runtimes[r]['uri'],
          title: __runtimes[r]['title'] || '',
          is_unfolded: is_unfolded,
          runtimes: this.getRuntimes( __runtimes[r]['window-id'] )
        }
      }
    }
    return ret;
  }

  this.getRuntimes = function(window_id)
  {
    var ret = [], r = '';
    for( r in __runtimes )
    {
      if ( __runtimes[r] && __runtimes[r]['window-id'] &&  __runtimes[r]['window-id'] == window_id )
      {
        ret[ret.length] = __runtimes[r];
      }
    }
    return ret;
  }

  this.getRuntimeIdWithURL = function(url)
  {
    var r = '';
    for( r in __runtimes )
    {
      if( __runtimes[r]['uri'] == url )
      {
        return __runtimes[r];
      }
    }
    return null;
  }

  this.getScript = function(scriptId)
  {
    return __scripts[scriptId];
  }

  this.getScriptsRuntimeId = function(scriptId)
  {
    return __scripts[scriptId] && __scripts[scriptId]['runtime-id'] || null;
  }

  this.getScriptSource = function(scriptId)
  {
    return __scripts[scriptId] && __scripts[scriptId]['script-data'] || null;
  }

  this.getScripts = function(runtime_id)
  {
    var ret=[], script = null, cur = '';
    for( cur in __scripts )
    {
      script = __scripts[cur];
      if(script['runtime-id'] == runtime_id)
      {
        ret[ret.length] = script;
      }
    }
    return ret;
  }

  this.hasBreakpoint = function(script_id, line_nr)
  {
    return (line_nr in __scripts[script_id]['breakpoints']);
  }

  this.setBreakpoint = function(script_id, line_nr)
  {
    var b_p_id = __scripts[script_id]['breakpoints'][line_nr] = getBreakpointId();
    services['ecmascript-debugger'].setBreakpoint(script_id, line_nr, b_p_id);
  }

  this.removeBreakpoint = function(script_id, line_nr)
  {
    services['ecmascript-debugger'].removeBreakpoint( __scripts[script_id]['breakpoints'][line_nr] );
    delete __scripts[script_id]['breakpoints'][line_nr];
  }

  this.getBreakpoints = function(script_id)
  {
    return __scripts[script_id] && __scripts[script_id]['breakpoints'];
  }

  this.setUnfolded = function(runtime_id, is_unfolded)
  {

    if( __runtimes[runtime_id] )
    {
      //alert(__runtimes[runtime_id]+' '+is_unfolded);
      __runtimes[runtime_id]['unfolded'] = is_unfolded;
    }
  }

  this.setWindowUnfolded = function(window_id, is_unfolded)
  {
    __windowsFolding[window_id] = is_unfolded;
  }

  this.setObserve = function(runtime_id, observe)
  {
    if( __runtimes[runtime_id] )
    {
      __runtimes[runtime_id]['observe'] = observe;
    }
  }

  this.getObserve = function(runtime_id)
  {
    return __runtimes[runtime_id] && __runtimes[runtime_id]['observe']  || false;
  }

  // this is a temporary solution as long as we don't have a concept for tabs

  var __selected_runtime_id = '';

  var __next_runtime_id_to_select = ''

  this.setSelectedRuntime = function(runtime)
  {
    var r = '';
    for( r in __runtimes )
    {
      if( __runtimes[r] == runtime )
      {
        __runtimes[r]['selected'] = true;
        __selected_runtime_id = __runtimes[r]['runtime-id'];
      }
      else
      {
        // the runtime could be registered but not jet parsed
        if( __runtimes[r] )
        {
          __runtimes[r]['selected'] = false;
        }
      }
    }
  }

  this.setSelectedScript = function( runtime, id )
  {
    var scripts = this.getScripts(runtime['runtime-id']), script = null, i = 0;
    for( ; script = scripts[i]; i++)
    {
      script.selected = script['script-id'] == id ? true : false;
    }
  }

  this.setSelectedRuntimeId = function(id)
  {
    if(__runtimes[id])
    {
      this.setSelectedRuntime(__runtimes[id]);
      views.runtimes.update();
    }
    else
    {
      __next_runtime_id_to_select = id;
    }
  }

  this.getSelectedRuntimeId = function()
  {
    return __selected_runtime_id;
  }

  messages.addListener('host-state', onHostStateChange);

}

//<|endoftext|>


<commit_before>/**
 * Created by LiuyinC on 10/25/15.
 */
function main(rawData) {
  var aggData = aggregate(rawData);
  draw(rawData, aggData);
}

function aggregate(data) {
  var result = [
    {
      key: '1',
      value: [
        {
          key: 'female',
          value: [
            {
              key: 'survived',
              value: 0
            },
            {
              key: 'total',
              value: 0
            }
          ]
        },
        {
          key: 'male',
          value: [
            {
              key: 'survived',
              value: 0
            },
            {
              key: 'total',
              value: 0
            }
          ]
        }
      ]
    },
    {
      key: '2',
      value: [
        {
          key: 'female',
          value: [
            {
              key: 'survived',
              value: 0
            },
            {
              key: 'total',
              value: 0
            }
          ]
        },
        {
          key: 'male',
          value: [
            {
              key: 'survived',
              value: 0
            },
            {
              key: 'total',
              value: 0
            }
          ]
        }
      ]
    },
    {
      key: '3',
      value: [
        {
          key: 'female',
          value: [
            {
              key: 'survived',
              value: 0
            },
            {
              key: 'total',
              value: 0
            }
          ]
        },
        {
          key: 'male',
          value: [
            {
              key: 'survived',
              value: 0
            },
            {
              key: 'total',
              value: 0
            }
          ]
        }
      ]
    }
  ];

  data.forEach(function(d) {
    var pclass = result.filter(function(pclass) {
      return pclass.key == d.Pclass;
    })[0];
    var sex = pclass.value.filter(function(sex) {
      return sex.key == d.Sex;
    })[0];
    var total = sex.value.filter(function(e) {
      return e.key == 'total';
    })[0];
    var survived = sex.value.filter(function(e) {
      return e.key == 'survived';
    })[0];
    total.value ++;
    survived.value += +d.Survived;
  });

  return result;
}

function draw(rawData, aggData) {
  var width = 1000;
  var barHeight = 40;
  var gutter = 5;
  var height = barHeight * 6; // total 6 bars

  var x = d3.scale.linear().domain([0, rawData.length]).range([0, width]);

  var chart = d3.select(".chart").attr("width", width).attr("height", height);

  var pclass = chart.selectAll("g")
    .data(aggData)
    .enter()
    .append("g")
    .attr("transform", function(d, i) { return "translate(0," + i * d.value.length * barHeight + ")"; });

  var sex = pclass.selectAll('g')
    .data(function(d) { return d.value; })
    .enter()
    .append('g')
    .attr('transform', function(d, i) { return 'translate(0,' + i * barHeight + ')'; })
    ;

  sex.append("rect")
    .attr("width", function(d) {
      var totalCount = d.value.filter(function(e) {
        return e.key == 'total';
      })[0].value;
      return x(totalCount);
    })
    .attr("height", barHeight - gutter)
    .attr('class', 'total');

  sex.append("rect")
    .attr("width", function(d) {
      var survivedCount = d.value.filter(function(e) {
        return e.key == 'survived';
      })[0].value;
      return x(survivedCount);
    })
    .attr('class', 'survived')
    .attr("height", barHeight - gutter);

  sex.append("text")
    .attr("dx", function(d) {
      var total = d.value.filter(function(e) {
        return e.key == 'total';
      })[0].value;
      return x(total + 5);
    })
    .attr('text-anchor', 'start')
    .attr("y", barHeight / 2)
    .attr("dy", ".35em")
    .text(function(d) {
      var total = d.value.filter(function(e) {
        return e.key == 'total';
      })[0].value;
      var survived = d.value.filter(function(e) {
        return e.key == 'survived';
      })[0].value;
      var text = survived.toString() + '/' + total.toString();
      return text;
    })
}

d3.csv('data/train.csv', main);<commit_msg>added comments and label<commit_after>/**
 * Created by LiuyinC on 10/25/15.
 */
function main(rawData) {
  var aggData = aggregate(rawData);
  draw(rawData, aggData);
}

/**
 * get statistics of the data
 * @param data
 * @returns {*[]}
 */
function aggregate(data) {
  var result = [
    {
      key: '1',
      value: [
        {
          key: 'female',
          value: [
            {
              key: 'survived',
              value: 0
            },
            {
              key: 'total',
              value: 0
            }
          ]
        },
        {
          key: 'male',
          value: [
            {
              key: 'survived',
              value: 0
            },
            {
              key: 'total',
              value: 0
            }
          ]
        }
      ]
    },
    {
      key: '2',
      value: [
        {
          key: 'female',
          value: [
            {
              key: 'survived',
              value: 0
            },
            {
              key: 'total',
              value: 0
            }
          ]
        },
        {
          key: 'male',
          value: [
            {
              key: 'survived',
              value: 0
            },
            {
              key: 'total',
              value: 0
            }
          ]
        }
      ]
    },
    {
      key: '3',
      value: [
        {
          key: 'female',
          value: [
            {
              key: 'survived',
              value: 0
            },
            {
              key: 'total',
              value: 0
            }
          ]
        },
        {
          key: 'male',
          value: [
            {
              key: 'survived',
              value: 0
            },
            {
              key: 'total',
              value: 0
            }
          ]
        }
      ]
    }
  ];

  data.forEach(function(d) {
    // get passenger class in result
    var pclass = result.filter(function(pclass) {
      return pclass.key == d.Pclass;
    })[0];
    // get gender in the above Pclass
    var sex = pclass.value.filter(function(sex) {
      return sex.key == d.Sex;
    })[0];
    // get total count in the above gender
    var total = sex.value.filter(function(e) {
      return e.key == 'total';
    })[0];
    // get survived count in the above gender
    var survived = sex.value.filter(function(e) {
      return e.key == 'survived';
    })[0];
    // increment the total count and survived count
    total.value ++;
    survived.value += +d.Survived;
  });

  return result;
}

function draw(rawData, aggData) {
  var width = 1000;
  var barHeight = 40;
  var gutter = 5;
  var height = barHeight * 6; // total 6 bars

  var x = d3.scale.linear().domain([0, rawData.length]).range([0, width]);

  var chart = d3.select(".chart").attr("width", width).attr("height", height);

  var pclass = chart.selectAll("g")
    .data(aggData)
    .enter()
    .append("g")
    .attr("transform", function(d, i) { return "translate(0," + i * d.value.length * barHeight + ")"; });

  var pclassLabel = pclass.append("text")
    .text(function(d) { return "Passenger class " + d.key; })
    .attr('transform', function() { return 'translate(0, ' + barHeight + ')'; });

  var sex = pclass.selectAll('g')
    .data(function(d) { return d.value; })
    .enter()
    .append('g')
    .attr('transform', function(d, i) { return 'translate(' + this.parentNode.children[0].offsetWidth + ', ' + i * barHeight + ')'; });

  sex.append("rect")
    .attr("width", function(d) {
      var totalCount = d.value.filter(function(e) {
        return e.key == 'total';
      })[0].value;
      return x(totalCount);
    })
    .attr("height", barHeight - gutter)
    .attr('class', 'total');

  sex.append("rect")
    .attr("width", function(d) {
      var survivedCount = d.value.filter(function(e) {
        return e.key == 'survived';
      })[0].value;
      return x(survivedCount);
    })
    .attr('class', 'survived')
    .attr("height", barHeight - gutter);

  sex.append("text")
    .attr("dx", function(d) {
      var total = d.value.filter(function(e) {
        return e.key == 'total';
      })[0].value;
      return x(total + 5);
    })
    .attr('text-anchor', 'start')
    .attr("y", barHeight / 2)
    .attr("dy", ".35em")
    .text(function(d) {
      var total = d.value.filter(function(e) {
        return e.key == 'total';
      })[0].value;
      var survived = d.value.filter(function(e) {
        return e.key == 'survived';
      })[0].value;
      var text = survived.toString() + '/' + total.toString();
      return text;
    });
}

d3.csv('data/train.csv', main);<|endoftext|>