<commit_before>function doGet(e) {

  var app = UiApp.createApplication();

  var ss = SpreadsheetApp.openById("0AlOOZ32SnnaCdGhRakZCb3JpLWsxZU5QQkxuQ01HWHc");

  var dataSheet = ss.getSheetByName("studentDetails");

  var loggedInUser = Session.getActiveUser().getEmail();

  var studentLookuprange = ss.getRangeByName("studentLookup");

  //create arrays from rows in range using standard getRowsData function
  var classObjects = getRowsData(dataSheet, studentLookuprange);

  //identify user as known or unknown, give error message if unknown, proceed if known
  var known = false;

  for (var i=0;i<classObjects.length;i++){
    if(classObjects[i].username === loggedInUser){
     known = true;
    };
  }

  if(known === false){
   var unknownUserPanel = app.createAbsolutePanel().setSize('600','800');
   var unknownUserWarning = app.createLabel('Your email address is not currently registered with the homework system.')
     .setStyleAttribute('fontSize', '15px').setStyleAttribute('fontWeight','bold').setStyleAttribute('color', 'red');
   var unknownUserLabel = app.createLabel('Please contact ict@nexus.edu.my')
    //unknownUserLabel.setWidth('400px').setWordWrap(true);
     .setStyleAttribute('fontSize', '15px');
    unknownUserPanel.add(unknownUserWarning, 0 , 0 );
    unknownUserPanel.add(unknownUserLabel, 0 , 30);
    app.add(unknownUserPanel);
    return app;
  }

  //create empty object to index array by username
  var classObjectsIndex = {};

  //iterate through all classobjects and index each array by its first value, username
  for (var i=0;i<classObjects.length;i++){

    classObjectsIndex[classObjects[i].username] = classObjects[i];

  }

  var nameLabel = app.createLabel('Displaying homework details for: ' + loggedInUser);
    nameLabel.setStyleAttribute('fontSize','15px');
  app.add(nameLabel);

  var dataSheet = ss.getSheetByName("classStatus");

  var statusLookuprange = ss.getRangeByName("classLookup");

  //create arrays from rows in range using standard getRowsData function
  var statusObjects = getRowsData(dataSheet, statusLookuprange);

  //create empty object to index array by username
  var statusObjectsIndex = {};

  //iterate through all classobjects and index each array by its first value, username
  for (var i=0;i<statusObjects.length;i++){

    statusObjectsIndex[statusObjects[i].classcode] = statusObjects[i];

  }

  //function to get size of array by counting keys with corresponding values only
  Object.size = function(obj) {
    var size = 0, key;
    for (key in obj) {
        if (obj.hasOwnProperty(key)) size++;
    }
    return size;
  };

  // Get the size of an object
  var size = Object.size(classObjectsIndex[loggedInUser]);

  Logger.log(size);

  var flexTable = app.createFlexTable();
    flexTable.setStyleAttribute('marginTop', '10px')
    flexTable.setCellPadding(5);
    flexTable.setCellSpacing(2);

  var tableArray = [];
  var t0 = [];
  var t1 = [];
  var t2 = [];
  var color = [];
  var BGColor = [];

  for(var i = 0;i<(size-1);i++){

    var class = "class" + (i+1);
    var classCode = classObjectsIndex[loggedInUser][class];

    t0.push(statusObjectsIndex[classCode].classname);
    t1.push(statusObjectsIndex[classCode].homeworkstatus);
    t2.push(app.createAbsolutePanel().add(app.createAnchor('Calendar',statusObjectsIndex[classCode].classcalendarlink)));

    if(statusObjectsIndex[classCode].homeworkstatus == "No homework set for this class"){
      BGColor.push("#96bcfd");
      color.push("#000000");
    }else{
      BGColor.push("#eca8a3");
      color.push("#FFFFFF");
    }

  }

    tableArray.push(t0,t1,t2,color, BGColor);

//INSERT SORT

    for(var i = 0;i<(size-1);i++){

      flexTable.setText(i,0, tableArray[0][i]);
      flexTable.setText(i,1, tableArray[1][i]);
      flexTable.setWidget(i,2, tableArray[2][i]);
      flexTable.setRowStyleAttribute(i, 'color', tableArray[3][i]);
      flexTable.setRowStyleAttribute(i, 'backgroundColor', tableArray[4][i]);

    };

  app.add(flexTable);

  return app;
}

/////////////////////////////////////////////////////////////////////////////////////

// getRowsData iterates row by row in the input range and returns an array of objects.
// Each object contains all the data for a given row, indexed by its normalized column name.
// Arguments:
//   - sheet: the sheet object that contains the data to be processed
//   - range: the exact range of cells where the data is stored
//   - columnHeadersRowIndex: specifies the row number where the column names are stored.
//       This argument is optional and it defaults to the row immediately above range;
// Returns an Array of objects.
function getRowsData(sheet, range, columnHeadersRowIndex) {
  columnHeadersRowIndex = columnHeadersRowIndex || range.getRowIndex() - 1;
  var numColumns = range.getEndColumn() - range.getColumn() + 1;
  var headersRange = sheet.getRange(columnHeadersRowIndex, range.getColumn(), 1, numColumns);
  var headers = headersRange.getValues()[0];
  return getObjects(range.getValues(), normalizeHeaders(headers));
}

// getHeaderLabels returns an array of strings from with the first row.
//   - sheet: the sheet object that contains the data to be processed
//   - range: the exact range of cells where the data is stored
//   - columnHeadersRowIndex: specifies the row number where the column names are stored.
//       This argument is optional and it defaults to the row immediately above range;
// Returns an Array of objects.
function getHeaderLabels(sheet, range, columnHeadersRowIndex) {
  columnHeadersRowIndex = columnHeadersRowIndex || range.getRowIndex() - 1;
  var numColumns = range.getEndColumn() - range.getColumn() + 1;
  var headersRange = sheet.getRange(columnHeadersRowIndex, range.getColumn(), 1, numColumns);
  var headers = headersRange.getValues()[0];
  return headers;
}

// getColumnsData iterates column by column in the input range and returns an array of objects.
// Each object contains all the data for a given column, indexed by its normalized row name.
// Arguments:
//   - sheet: the sheet object that contains the data to be processed
//   - range: the exact range of cells where the data is stored
//   - rowHeadersColumnIndex: specifies the column number where the row names are stored.
//       This argument is optional and it defaults to the column immediately left of the range;
// Returns an Array of objects.
function getColumnsData(sheet, range, rowHeadersColumnIndex) {
  rowHeadersColumnIndex = rowHeadersColumnIndex || range.getColumnIndex() - 1;
  var headersTmp = sheet.getRange(range.getRow(), rowHeadersColumnIndex, range.getNumRows(), 1).getValues();
  var headers = normalizeHeaders(arrayTranspose(headersTmp)[0]);
  return getObjects(arrayTranspose(range.getValues()), headers);
}

// For every row of data in data, generates an object that contains the data. Names of
// object fields are defined in keys.
// Arguments:
//   - data: JavaScript 2d array
//   - keys: Array of Strings that define the property names for the objects to create
function getObjects(data, keys) {
  var objects = [];
  for (var i = 0; i < data.length; ++i) {
    var object = {};
    var hasData = false;
    for (var j = 0; j < data[i].length; ++j) {
      var cellData = data[i][j];
      if (isCellEmpty(cellData)) {
        continue;
      }
      object[keys[j]] = cellData;
      hasData = true;
    }
    if (hasData) {
      objects.push(object);
    }
  }
  return objects;
}

// Returns an Array of normalized Strings.
// Arguments:
//   - headers: Array of Strings to normalize
function normalizeHeaders(headers) {
  var keys = [];
  for (var i = 0; i < headers.length; ++i) {
    var key = normalizeHeader(headers[i]);
    if (key.length > 0) {
      keys.push(key);
    }
  }
  return keys;
}

// Normalizes a string, by removing all alphanumeric characters and using mixed case
// to separate words. The output will always start with a lower case letter.
// This function is designed to produce JavaScript object property names.
// Arguments:
//   - header: string to normalize
// Examples:
//   "First Name" -> "firstName"
//   "Market Cap (millions) -> "marketCapMillions
//   "1 number at the beginning is ignored" -> "numberAtTheBeginningIsIgnored"
function normalizeHeader(header) {
  var key = "";
  var upperCase = false;
  for (var i = 0; i < header.length; ++i) {
    var letter = header[i];
    if (letter == " " && key.length > 0) {
      upperCase = true;
      continue;
    }
    if (!isAlnum(letter)) {
      continue;
    }
    if (key.length == 0 && isDigit(letter)) {
      continue; // first character must be a letter
    }
    if (upperCase) {
      upperCase = false;
      key += letter.toUpperCase();
    } else {
      key += letter.toLowerCase();
    }
  }
  return key;
}

// Returns true if the cell where cellData was read from is empty.
// Arguments:
//   - cellData: string
function isCellEmpty(cellData) {
  return typeof(cellData) == "string" && cellData == "";
}

// Returns true if the character char is alphabetical, false otherwise.
function isAlnum(char) {
  return char >= 'A' && char <= 'Z' ||
    char >= 'a' && char <= 'z' ||
    isDigit(char);
}

// Returns true if the character char is a digit, false otherwise.
function isDigit(char) {
  return char >= '0' && char <= '9';
}

// Given a JavaScript 2d Array, this function returns the transposed table.
// Arguments:
//   - data: JavaScript 2d Array
// Returns a JavaScript 2d Array
// Example: arrayTranspose([[1,2,3],[4,5,6]]) returns [[1,4],[2,5],[3,6]].
function arrayTranspose(data) {
  if (data.length == 0 || data[0].length == 0) {
    return null;
  }

  var ret = [];
  for (var i = 0; i < data[0].length; ++i) {
    ret.push([]);
  }

  for (var i = 0; i < data.length; ++i) {
    for (var j = 0; j < data[i].length; ++j) {
      ret[j][i] = data[i][j];
    }
  }

  return ret;
}
<commit_msg>FlexTable created using objects. Sorting by homeworkStatus enabled.<commit_after>function doGet(e) {

  var app                = UiApp.createApplication();
  var ss                 = SpreadsheetApp.openById("0AlOOZ32SnnaCdGhRakZCb3JpLWsxZU5QQkxuQ01HWHc");
  var dataSheet          = ss.getSheetByName("studentDetails");
  var loggedInUser       = Session.getActiveUser().getEmail();
  var studentLookuprange = ss.getRangeByName("studentLookup");

  //create arrays from rows in range using standard getRowsData function
  var classObjects       = getRowsData(dataSheet, studentLookuprange);

  //identify user as known or unknown, give error message if unknown, proceed if known
  var known = false;

  for (var i=0;i<classObjects.length;i++){
    if(classObjects[i].username === loggedInUser){
     known = true;
    };
  }

  if(known === false){
   var unknownUserPanel = app.createAbsolutePanel().setSize('600','800');
   var unknownUserWarning = app.createLabel('Your email address is not currently registered with the homework system.')
     .setStyleAttribute('fontSize', '15px').setStyleAttribute('fontWeight','bold').setStyleAttribute('color', 'red');
   var unknownUserLabel = app.createLabel('Please contact ict@nexus.edu.my')
     .setStyleAttribute('fontSize', '15px');
    unknownUserPanel.add(unknownUserWarning, 0 , 0 );
    unknownUserPanel.add(unknownUserLabel, 0 , 30);
    app.add(unknownUserPanel);
    return app;
  }

  //create empty object to index array by username
  var classObjectsIndex = {};

  //iterate through all classobjects and index each array by its first value, username
  for (var i=0;i<classObjects.length;i++){

    classObjectsIndex[classObjects[i].username] = classObjects[i];

  }

  var nameLabel = app.createLabel('Displaying homework details for: ' + loggedInUser);
    nameLabel.setStyleAttribute('fontSize','15px');
  app.add(nameLabel);

  var dataSheet = ss.getSheetByName("classStatus");
  var statusLookuprange = ss.getRangeByName("classLookup");

  //create arrays from rows in range using standard getRowsData function
  var statusObjects = getRowsData(dataSheet, statusLookuprange);

  //create empty object to index array by username
  var statusObjectsIndex = {};

  //iterate through all classobjects and index each array by its first value, username
  for (var i=0;i<statusObjects.length;i++){

    statusObjectsIndex[statusObjects[i].classcode] = statusObjects[i];

  }

  //function to get size of array by counting keys with corresponding values only
  Object.size = function(obj) {
    var size = 0, key;
    for (key in obj) {
        if (obj.hasOwnProperty(key)) size++;
    }
    return size;
  };

  // Get the size of an object
  var size = Object.size(classObjectsIndex[loggedInUser]);

  //create flexTable
  var flexTable = app.createFlexTable();
    flexTable.setStyleAttribute('marginTop', '10px')
    flexTable.setCellPadding(5);
    flexTable.setCellSpacing(2);

 //create empty table array to store rowObjects
  var tableArray =[];

//create rowObjects
  for(var i = 0; i<(size-1); i++){
    var rowObject = {};
    var class = 'class' + (i+1);
    var classCode = classObjectsIndex[loggedInUser][class];

      rowObject.className      = statusObjectsIndex[classCode].classname;
      rowObject.homeworkStatus = statusObjectsIndex[classCode].homeworkstatus;
      rowObject.link           = app.createAbsolutePanel().add(app.createAnchor('Calendar',statusObjectsIndex[classCode].classcalendarlink));

      if(statusObjectsIndex[classCode].homeworkstatus == "No homework set for this class"){
        rowObject.BGColor = "#96bcfd";
        rowObject.color   = "#000000";
      }else{
        rowObject.BGColor = "#eca8a3";
        rowObject.color   = "#FFFFFF";
      }

      tableArray.push(rowObject);
  }

//sort objects in array by homework status
  tableArray.sort(function (a, b) {
    if (a.homeworkStatus > b.homeworkStatus)
      return 1;
    if (a.homeworkStatus < b.homeworkStatus)
      return -1;
    // a must be equal to b
    return 0;
  });

//populate flextable
  for(var i = 0;i<(size-1);i++){

      flexTable.setText(i,0, tableArray[i].className);
      flexTable.setText(i,1, tableArray[i].homeworkStatus);
      flexTable.setWidget(i,2, tableArray[i].link);
      flexTable.setRowStyleAttribute(i, 'color', tableArray[i].color);
      flexTable.setRowStyleAttribute(i, 'backgroundColor', tableArray[i].BGColor);

    };

  app.add(flexTable);

  return app;
}

/////////////////////////////////////////////////////////////////////////////////////

// getRowsData iterates row by row in the input range and returns an array of objects.
// Each object contains all the data for a given row, indexed by its normalized column name.
// Arguments:
//   - sheet: the sheet object that contains the data to be processed
//   - range: the exact range of cells where the data is stored
//   - columnHeadersRowIndex: specifies the row number where the column names are stored.
//       This argument is optional and it defaults to the row immediately above range;
// Returns an Array of objects.
function getRowsData(sheet, range, columnHeadersRowIndex) {
  columnHeadersRowIndex = columnHeadersRowIndex || range.getRowIndex() - 1;
  var numColumns = range.getEndColumn() - range.getColumn() + 1;
  var headersRange = sheet.getRange(columnHeadersRowIndex, range.getColumn(), 1, numColumns);
  var headers = headersRange.getValues()[0];
  return getObjects(range.getValues(), normalizeHeaders(headers));
}

// getHeaderLabels returns an array of strings from with the first row.
//   - sheet: the sheet object that contains the data to be processed
//   - range: the exact range of cells where the data is stored
//   - columnHeadersRowIndex: specifies the row number where the column names are stored.
//       This argument is optional and it defaults to the row immediately above range;
// Returns an Array of objects.
function getHeaderLabels(sheet, range, columnHeadersRowIndex) {
  columnHeadersRowIndex = columnHeadersRowIndex || range.getRowIndex() - 1;
  var numColumns = range.getEndColumn() - range.getColumn() + 1;
  var headersRange = sheet.getRange(columnHeadersRowIndex, range.getColumn(), 1, numColumns);
  var headers = headersRange.getValues()[0];
  return headers;
}

// getColumnsData iterates column by column in the input range and returns an array of objects.
// Each object contains all the data for a given column, indexed by its normalized row name.
// Arguments:
//   - sheet: the sheet object that contains the data to be processed
//   - range: the exact range of cells where the data is stored
//   - rowHeadersColumnIndex: specifies the column number where the row names are stored.
//       This argument is optional and it defaults to the column immediately left of the range;
// Returns an Array of objects.
function getColumnsData(sheet, range, rowHeadersColumnIndex) {
  rowHeadersColumnIndex = rowHeadersColumnIndex || range.getColumnIndex() - 1;
  var headersTmp = sheet.getRange(range.getRow(), rowHeadersColumnIndex, range.getNumRows(), 1).getValues();
  var headers = normalizeHeaders(arrayTranspose(headersTmp)[0]);
  return getObjects(arrayTranspose(range.getValues()), headers);
}

// For every row of data in data, generates an object that contains the data. Names of
// object fields are defined in keys.
// Arguments:
//   - data: JavaScript 2d array
//   - keys: Array of Strings that define the property names for the objects to create
function getObjects(data, keys) {
  var objects = [];
  for (var i = 0; i < data.length; ++i) {
    var object = {};
    var hasData = false;
    for (var j = 0; j < data[i].length; ++j) {
      var cellData = data[i][j];
      if (isCellEmpty(cellData)) {
        continue;
      }
      object[keys[j]] = cellData;
      hasData = true;
    }
    if (hasData) {
      objects.push(object);
    }
  }
  return objects;
}

// Returns an Array of normalized Strings.
// Arguments:
//   - headers: Array of Strings to normalize
function normalizeHeaders(headers) {
  var keys = [];
  for (var i = 0; i < headers.length; ++i) {
    var key = normalizeHeader(headers[i]);
    if (key.length > 0) {
      keys.push(key);
    }
  }
  return keys;
}

// Normalizes a string, by removing all alphanumeric characters and using mixed case
// to separate words. The output will always start with a lower case letter.
// This function is designed to produce JavaScript object property names.
// Arguments:
//   - header: string to normalize
// Examples:
//   "First Name" -> "firstName"
//   "Market Cap (millions) -> "marketCapMillions
//   "1 number at the beginning is ignored" -> "numberAtTheBeginningIsIgnored"
function normalizeHeader(header) {
  var key = "";
  var upperCase = false;
  for (var i = 0; i < header.length; ++i) {
    var letter = header[i];
    if (letter == " " && key.length > 0) {
      upperCase = true;
      continue;
    }
    if (!isAlnum(letter)) {
      continue;
    }
    if (key.length == 0 && isDigit(letter)) {
      continue; // first character must be a letter
    }
    if (upperCase) {
      upperCase = false;
      key += letter.toUpperCase();
    } else {
      key += letter.toLowerCase();
    }
  }
  return key;
}

// Returns true if the cell where cellData was read from is empty.
// Arguments:
//   - cellData: string
function isCellEmpty(cellData) {
  return typeof(cellData) == "string" && cellData == "";
}

// Returns true if the character char is alphabetical, false otherwise.
function isAlnum(char) {
  return char >= 'A' && char <= 'Z' ||
    char >= 'a' && char <= 'z' ||
    isDigit(char);
}

// Returns true if the character char is a digit, false otherwise.
function isDigit(char) {
  return char >= '0' && char <= '9';
}

// Given a JavaScript 2d Array, this function returns the transposed table.
// Arguments:
//   - data: JavaScript 2d Array
// Returns a JavaScript 2d Array
// Example: arrayTranspose([[1,2,3],[4,5,6]]) returns [[1,4],[2,5],[3,6]].
function arrayTranspose(data) {
  if (data.length == 0 || data[0].length == 0) {
    return null;
  }

  var ret = [];
  for (var i = 0; i < data[0].length; ++i) {
    ret.push([]);
  }

  for (var i = 0; i < data.length; ++i) {
    for (var j = 0; j < data[i].length; ++j) {
      ret[j][i] = data[i][j];
    }
  }

  return ret;
}
<|endoftext|>


<commit_before>function Muppet(age, hobby) {
  this.age = age;
  this.hobby = hobby;

  this.answerNanny = function(){
	return "Everything's cool!";
  }
}

function SwedishChef(age, hobby, mood) {
  Muppet.call(this, age, hobby);
  this.mood = mood;

  this.cook = function() {
    return "Mmmm soup!";
  }
}

SwedishChef.prototype = new Muppet();

describe("About inheritance", function() {
  beforeEach(function(){
    this.muppet = new Muppet(2, "coding");
  	this.swedishChef = new SwedishChef(2, "cooking", "chillin");
  });

  it("should be able to call a method on the derived object", function() {
    expect(this.swedishChef.cook()).toEqual("Mmmm soup!");
  });

  it("should be able to call a method on the base object", function() {
    expect(this.swedishChef.answerNanny()).toEqual(FILL_ME_IN);
  });

  it("should set constructor parameters on the base object", function() {
    expect(this.swedishChef.age).toEqual(FILL_ME_IN);
    expect(this.swedishChef.hobby).toEqual(FILL_ME_IN);
  });

  it("should set constructor parameters on the derived object", function() {
    expect(this.swedishChef.mood).toEqual(FILL_ME_IN);
  });
});

// http://javascript.crockford.com/prototypal.html
Object.prototype.beget = function () {
  function F() {}
  F.prototype = this;
  return new F();
}

function Gonzo(age, hobby, trick) {
  Muppet.call(this, age, hobby);
  this.trick = trick;

  this.doTrick = function() {
    return this.trick;
  }
}

//no longer need to call the Muppet (base type) constructor
Gonzo.prototype = Muppet.prototype.beget();
//note: if you're wondering how this line affects the below tests, the answer is that it doesn't.
//however, it does do something interesting -- it makes this work:
//  var g = new Gonzo(...);
//  g instanceOf Muppet //true

describe("About Crockford's inheritance improvement", function() {
  beforeEach(function(){
    this.gonzo = new Gonzo(3, "daredevil performer", "eat a tire");
  });

  it("should be able to call a method on the derived object", function() {
    expect(this.gonzo.doTrick()).toEqual(FILL_ME_IN);
  });

  it("should be able to call a method on the base object", function() {
    expect(this.gonzo.answerNanny()).toEqual(FILL_ME_IN);
  });

  it("should set constructor parameters on the base object", function() {
    expect(this.gonzo.age).toEqual(FILL_ME_IN);
    expect(this.gonzo.hobby).toEqual(FILL_ME_IN);
  });

  it("should set constructor parameters on the derived object", function() {
    expect(this.gonzo.trick).toEqual(FILL_ME_IN);
  });
});
<commit_msg>"Fixed: AboutInheritance.js - Problem (2) : it('should be able to call a method on the base object..'"<commit_after>function Muppet(age, hobby) {
  this.age = age;
  this.hobby = hobby;

  this.answerNanny = function(){
	return "Everything's cool!";
  }
}

function SwedishChef(age, hobby, mood) {
  Muppet.call(this, age, hobby);
  this.mood = mood;

  this.cook = function() {
    return "Mmmm soup!";
  }
}

SwedishChef.prototype = new Muppet();

describe("About inheritance", function() {
  beforeEach(function(){
    this.muppet = new Muppet(2, "coding");
  	this.swedishChef = new SwedishChef(2, "cooking", "chillin");
  });

  it("should be able to call a method on the derived object", function() {
    expect(this.swedishChef.cook()).toEqual("Mmmm soup!");
  });

  it("should be able to call a method on the base object", function() {
    expect(this.swedishChef.answerNanny()).toEqual("Everything's cool!");
  });

  it("should set constructor parameters on the base object", function() {
    expect(this.swedishChef.age).toEqual(FILL_ME_IN);
    expect(this.swedishChef.hobby).toEqual(FILL_ME_IN);
  });

  it("should set constructor parameters on the derived object", function() {
    expect(this.swedishChef.mood).toEqual(FILL_ME_IN);
  });
});

// http://javascript.crockford.com/prototypal.html
Object.prototype.beget = function () {
  function F() {}
  F.prototype = this;
  return new F();
}

function Gonzo(age, hobby, trick) {
  Muppet.call(this, age, hobby);
  this.trick = trick;

  this.doTrick = function() {
    return this.trick;
  }
}

//no longer need to call the Muppet (base type) constructor
Gonzo.prototype = Muppet.prototype.beget();
//note: if you're wondering how this line affects the below tests, the answer is that it doesn't.
//however, it does do something interesting -- it makes this work:
//  var g = new Gonzo(...);
//  g instanceOf Muppet //true

describe("About Crockford's inheritance improvement", function() {
  beforeEach(function(){
    this.gonzo = new Gonzo(3, "daredevil performer", "eat a tire");
  });

  it("should be able to call a method on the derived object", function() {
    expect(this.gonzo.doTrick()).toEqual(FILL_ME_IN);
  });

  it("should be able to call a method on the base object", function() {
    expect(this.gonzo.answerNanny()).toEqual(FILL_ME_IN);
  });

  it("should set constructor parameters on the base object", function() {
    expect(this.gonzo.age).toEqual(FILL_ME_IN);
    expect(this.gonzo.hobby).toEqual(FILL_ME_IN);
  });

  it("should set constructor parameters on the derived object", function() {
    expect(this.gonzo.trick).toEqual(FILL_ME_IN);
  });
});
<|endoftext|>


<commit_before>function Muppet(age, hobby) {
  this.age = age;
  this.hobby = hobby;

  this.answerNanny = function(){
	return "Everything's cool!";
  }
}

function SwedishChef(age, hobby, mood) {
  Muppet.call(this, age, hobby);
  this.mood = mood;

  this.cook = function() {
    return "Mmmm soup!";
  }
}

SwedishChef.prototype = new Muppet();

describe("About inheritance", function() {
  beforeEach(function(){
    this.muppet = new Muppet(2, "coding");
  	this.swedishChef = new SwedishChef(2, "cooking", "chillin");
  });

  it("should be able to call a method on the derived object", function() {
    expect(this.swedishChef.cook()).toEqual(FILL_ME_IN);
  });

  it("should be able to call a method on the base object", function() {
    expect(this.swedishChef.answerNanny()).toEqual(FILL_ME_IN);
  });

  it("should set constructor parameters on the base object", function() {
    expect(this.swedishChef.age).toEqual(FILL_ME_IN);
    expect(this.swedishChef.hobby).toEqual(FILL_ME_IN);
  });

  it("should set constructor parameters on the derived object", function() {
    expect(this.swedishChef.mood).toEqual(FILL_ME_IN);
  });
});

// http://javascript.crockford.com/prototypal.html
Object.prototype.beget = function () {
  function F() {}
  F.prototype = this;
  return new F();
}

function Gonzo(age, hobby, trick) {
  Muppet.call(this, age, hobby);
  this.trick = trick;

  this.doTrick = function() {
    return this.trick;
  }
}

//no longer need to call the Muppet (base type) constructor
Gonzo.prototype = Muppet.prototype.beget();
//note: if you're wondering how this line affects the below tests, the answer is that it doesn't.
//however, it does do something interesting -- it makes this work:
//  var g = new Gonzo(...);
//  g instanceOf Muppet //true

describe("About Crockford's inheritance improvement", function() {
  beforeEach(function(){
    this.gonzo = new Gonzo(3, "daredevil performer", "eat a tire");
  });

  it("should be able to call a method on the derived object", function() {
    expect(this.gonzo.doTrick()).toEqual(FILL_ME_IN);
  });

  it("should be able to call a method on the base object", function() {
    expect(this.gonzo.answerNanny()).toEqual(FILL_ME_IN);
  });

  it("should set constructor parameters on the base object", function() {
    expect(this.gonzo.age).toEqual(FILL_ME_IN);
    expect(this.gonzo.hobby).toEqual(FILL_ME_IN);
  });

  it("should set constructor parameters on the derived object", function() {
    expect(this.gonzo.trick).toEqual(FILL_ME_IN);
  });
});
<commit_msg>Reindented file (missed this one earlier.)<commit_after>function Muppet(age, hobby) {
    this.age = age;
    this.hobby = hobby;

    this.answerNanny = function(){
        return "Everything's cool!";
    }
}

function SwedishChef(age, hobby, mood) {
    Muppet.call(this, age, hobby);
    this.mood = mood;

    this.cook = function() {
        return "Mmmm soup!";
    }
}

SwedishChef.prototype = new Muppet();

describe("About inheritance", function() {
    beforeEach(function(){
        this.muppet = new Muppet(2, "coding");
        this.swedishChef = new SwedishChef(2, "cooking", "chillin");
    });

    it("should be able to call a method on the derived object", function() {
        expect(this.swedishChef.cook()).toEqual(FILL_ME_IN);
    });

    it("should be able to call a method on the base object", function() {
        expect(this.swedishChef.answerNanny()).toEqual(FILL_ME_IN);
    });

    it("should set constructor parameters on the base object", function() {
        expect(this.swedishChef.age).toEqual(FILL_ME_IN);
        expect(this.swedishChef.hobby).toEqual(FILL_ME_IN);
    });

    it("should set constructor parameters on the derived object", function() {
        expect(this.swedishChef.mood).toEqual(FILL_ME_IN);
    });
});

// http://javascript.crockford.com/prototypal.html
Object.prototype.beget = function () {
    function F() {}
    F.prototype = this;
    return new F();
}

function Gonzo(age, hobby, trick) {
    Muppet.call(this, age, hobby);
    this.trick = trick;

    this.doTrick = function() {
        return this.trick;
    }
}

//no longer need to call the Muppet (base type) constructor
Gonzo.prototype = Muppet.prototype.beget();
//note: if you're wondering how this line affects the below tests, the answer is that it doesn't.
//however, it does do something interesting -- it makes this work:
//  var g = new Gonzo(...);
//  g instanceOf Muppet //true

describe("About Crockford's inheritance improvement", function() {
    beforeEach(function(){
        this.gonzo = new Gonzo(3, "daredevil performer", "eat a tire");
    });

    it("should be able to call a method on the derived object", function() {
        expect(this.gonzo.doTrick()).toEqual(FILL_ME_IN);
    });

    it("should be able to call a method on the base object", function() {
        expect(this.gonzo.answerNanny()).toEqual(FILL_ME_IN);
    });

    it("should set constructor parameters on the base object", function() {
        expect(this.gonzo.age).toEqual(FILL_ME_IN);
        expect(this.gonzo.hobby).toEqual(FILL_ME_IN);
    });

    it("should set constructor parameters on the derived object", function() {
        expect(this.gonzo.trick).toEqual(FILL_ME_IN);
    });
});
<|endoftext|>


<commit_before>function Muppet(age, hobby) {
  this.age = age;
  this.hobby = hobby;

  this.answerNanny = function(){
	return "Everything's cool!";
  }
}

function SwedishChef(age, hobby, mood) {
  Muppet.call(this, age, hobby);
  this.mood = mood;

  this.cook = function() {
    return "Mmmm soup!";
  }
}

SwedishChef.prototype = new Muppet();

describe("About inheritance", function() {
  beforeEach(function(){
    this.muppet = new Muppet(2, "coding");
  	this.swedishChef = new SwedishChef(2, "cooking", "chillin");
  });

  it("should be able to call a method on the derived object", function() {
    expect(this.swedishChef.cook()).toEqual("Mmmm soup!");
  });

  it("should be able to call a method on the base object", function() {
    expect(this.swedishChef.answerNanny()).toEqual("Everything's cool!");
  });

  it("should set constructor parameters on the base object", function() {
    expect(this.swedishChef.age).toEqual(2);
    expect(this.swedishChef.hobby).toEqual("cooking");
  });

  it("should set constructor parameters on the derived object", function() {
    expect(this.swedishChef.mood).toEqual("chillin");
  });
});

// http://javascript.crockford.com/prototypal.html
Object.prototype.beget = function () {
  function F() {}
  F.prototype = this;
  return new F();
}

function Gonzo(age, hobby, trick) {
  Muppet.call(this, age, hobby);
  this.trick = trick;

  this.doTrick = function() {
    return this.trick;
  }
}

//no longer need to call the Muppet (base type) constructor
Gonzo.prototype = Muppet.prototype.beget();
//note: if you're wondering how this line affects the below tests, the answer is that it doesn't.
//however, it does do something interesting -- it makes this work:
//  var g = new Gonzo(...);
//  g instanceOf Muppet //true

describe("About Crockford's inheritance improvement", function() {
  beforeEach(function(){
    this.gonzo = new Gonzo(3, "daredevil performer", "eat a tire");
  });

  it("should be able to call a method on the derived object", function() {
    expect(this.gonzo.doTrick()).toEqual(FILL_ME_IN);
  });

  it("should be able to call a method on the base object", function() {
    expect(this.gonzo.answerNanny()).toEqual(FILL_ME_IN);
  });

  it("should set constructor parameters on the base object", function() {
    expect(this.gonzo.age).toEqual(FILL_ME_IN);
    expect(this.gonzo.hobby).toEqual(FILL_ME_IN);
  });

  it("should set constructor parameters on the derived object", function() {
    expect(this.gonzo.trick).toEqual(FILL_ME_IN);
  });
});
<commit_msg>Complete Crockford's improvement AboutInheritance<commit_after>function Muppet(age, hobby) {
  this.age = age;
  this.hobby = hobby;

  this.answerNanny = function(){
	return "Everything's cool!";
  }
}

function SwedishChef(age, hobby, mood) {
  Muppet.call(this, age, hobby);
  this.mood = mood;

  this.cook = function() {
    return "Mmmm soup!";
  }
}

SwedishChef.prototype = new Muppet();

describe("About inheritance", function() {
  beforeEach(function(){
    this.muppet = new Muppet(2, "coding");
  	this.swedishChef = new SwedishChef(2, "cooking", "chillin");
  });

  it("should be able to call a method on the derived object", function() {
    expect(this.swedishChef.cook()).toEqual("Mmmm soup!");
  });

  it("should be able to call a method on the base object", function() {
    expect(this.swedishChef.answerNanny()).toEqual("Everything's cool!");
  });

  it("should set constructor parameters on the base object", function() {
    expect(this.swedishChef.age).toEqual(2);
    expect(this.swedishChef.hobby).toEqual("cooking");
  });

  it("should set constructor parameters on the derived object", function() {
    expect(this.swedishChef.mood).toEqual("chillin");
  });
});

// http://javascript.crockford.com/prototypal.html
Object.prototype.beget = function () {
  function F() {}
  F.prototype = this;
  return new F();
}

function Gonzo(age, hobby, trick) {
  Muppet.call(this, age, hobby);
  this.trick = trick;

  this.doTrick = function() {
    return this.trick;
  }
}

//no longer need to call the Muppet (base type) constructor
Gonzo.prototype = Muppet.prototype.beget();
//note: if you're wondering how this line affects the below tests, the answer is that it doesn't.
//however, it does do something interesting -- it makes this work:
//  var g = new Gonzo(...);
//  g instanceOf Muppet //true

describe("About Crockford's inheritance improvement", function() {
  beforeEach(function(){
    this.gonzo = new Gonzo(3, "daredevil performer", "eat a tire");
  });

  it("should be able to call a method on the derived object", function() {
    expect(this.gonzo.doTrick()).toEqual("eat a tire");
  });

  it("should be able to call a method on the base object", function() {
    expect(this.gonzo.answerNanny()).toEqual("Everything's cool!");
  });

  it("should set constructor parameters on the base object", function() {
    expect(this.gonzo.age).toEqual(3);
    expect(this.gonzo.hobby).toEqual("daredevil performer");
  });

  it("should set constructor parameters on the derived object", function() {
    expect(this.gonzo.trick).toEqual("eat a tire");
  });
});
<|endoftext|>


<commit_before>function Muppet(age, hobby) {
  this.age = age;
  this.hobby = hobby;

  this.answerNanny = function(){
	return "Everything's cool!";
  }
}

function SwedishChef(age, hobby, mood) {
  Muppet.call(this, age, hobby);
  this.mood = mood;

  this.cook = function() {
    return "Mmmm soup!";
  }
}

SwedishChef.prototype = new Muppet();

describe("About inheritance", function() {
  beforeEach(function(){
    this.muppet = new Muppet(2, "coding");
  	this.swedishChef = new SwedishChef(2, "cooking", "chillin");
  });

  it("should be able to call a method on the derived object", function() {
    expect(this.swedishChef.cook()).toEqual(FILL_ME_IN);
  });

  it("should be able to call a method on the base object", function() {
    expect(this.swedishChef.answerNanny()).toEqual(FILL_ME_IN);
  });

  it("should set constructor parameters on the base object", function() {
    expect(this.swedishChef.age).toEqual(FILL_ME_IN);
    expect(this.swedishChef.hobby).toEqual(FILL_ME_IN);
  });

  it("should set constructor parameters on the derived object", function() {
    expect(this.swedishChef.mood).toEqual(FILL_ME_IN);
  });
});

// http://javascript.crockford.com/prototypal.html
Object.prototype.beget = function () {
  function F() {}
  F.prototype = this;
  return new F();
}

function Gonzo(age, hobby, trick) {
  Muppet.call(this, age, hobby);
  this.trick = trick;

  this.doTrick = function() {
    return this.trick;
  }
}

//no longer need to call the Muppet (base type) constructor
Gonzo.prototype = Muppet.prototype.beget();
//note: if you're wondering how this line affects the below tests, the answer is that it doesn't.
//however, it does do something interesting -- it makes this work:
//  var g = new Gonzo(...);
//  g instanceOf Muppet //true

describe("About Crockford's inheritance improvement", function() {
  beforeEach(function(){
    this.gonzo = new Gonzo(3, "daredevil performer", "eat a tire");
  });

  it("should be able to call a method on the derived object", function() {
    expect(this.gonzo.doTrick()).toEqual(FILL_ME_IN);
  });

  it("should be able to call a method on the base object", function() {
    expect(this.gonzo.answerNanny()).toEqual(FILL_ME_IN);
  });

  it("should set constructor parameters on the base object", function() {
    expect(this.gonzo.age).toEqual(FILL_ME_IN);
    expect(this.gonzo.hobby).toEqual(FILL_ME_IN);
  });

  it("should set constructor parameters on the derived object", function() {
    expect(this.gonzo.trick).toEqual(FILL_ME_IN);
  });
});
<commit_msg>Completed the inheritance exercises for object prototyped. Good introduction to the subject<commit_after>function Muppet(age, hobby) {
  this.age = age;
  this.hobby = hobby;

  this.answerNanny = function(){
	return "Everything's cool!";
  }
}

function SwedishChef(age, hobby, mood) {
  Muppet.call(this, age, hobby);
  this.mood = mood;

  this.cook = function() {
    return "Mmmm soup!";
  }
}

SwedishChef.prototype = new Muppet();

describe("About inheritance", function() {
  beforeEach(function(){
    this.muppet = new Muppet(2, "coding");
  	this.swedishChef = new SwedishChef(2, "cooking", "chillin");
  });

  it("should be able to call a method on the derived object", function() {
    expect(this.swedishChef.cook()).toEqual("Mmmm soup!");
  });

  it("should be able to call a method on the base object", function() {
    expect(this.swedishChef.answerNanny()).toEqual("Everything's cool!");
  });

  it("should set constructor parameters on the base object", function() {
    expect(this.swedishChef.age).toEqual(2);
    expect(this.swedishChef.hobby).toEqual("cooking");
  });

  it("should set constructor parameters on the derived object", function() {
    expect(this.swedishChef.mood).toEqual("chillin");
  });
});

// http://javascript.crockford.com/prototypal.html
Object.prototype.beget = function () {
  function F() {}
  F.prototype = this;
  return new F();
}

function Gonzo(age, hobby, trick) {
  Muppet.call(this, age, hobby);
  this.trick = trick;

  this.doTrick = function() {
    return this.trick;
  }
}

//no longer need to call the Muppet (base type) constructor
Gonzo.prototype = Muppet.prototype.beget();
//note: if you're wondering how this line affects the below tests, the answer is that it doesn't.
//however, it does do something interesting -- it makes this work:
//  var g = new Gonzo(...);
//  g instanceOf Muppet //true

describe("About Crockford's inheritance improvement", function() {
  beforeEach(function(){
    this.gonzo = new Gonzo(3, "daredevil performer", "eat a tire");
  });

  it("should be able to call a method on the derived object", function() {
    expect(this.gonzo.doTrick()).toEqual("eat a tire");
  });

  it("should be able to call a method on the base object", function() {
    expect(this.gonzo.answerNanny()).toEqual("Everything's cool!");
  });

  it("should set constructor parameters on the base object", function() {
    expect(this.gonzo.age).toEqual(3);
    expect(this.gonzo.hobby).toEqual("daredevil performer");
  });

  it("should set constructor parameters on the derived object", function() {
    expect(this.gonzo.trick).toEqual("eat a tire");
  });
});
<|endoftext|>


<commit_before>/* jslint node: true, esnext: true */
"use strict";

const Interceptor = require('kronos-interceptor').Interceptor;
const parserFactory = require('./obj2string');

/**
 * This interceptor cares about the handling of the messages.
 * It will add the hops and copies the messages
 */
class LineHeaderInterceptor extends Interceptor {

	constructor(endpoint, config) {
		super(endpoint, config);
	}

	static get type() {
		return "stream-obj-to-string";
	}

	get type() {
		return LineHeaderInterceptor.type;
	}

	receive(request, oldRequest) {
		if (request.payload) {
			const streamFilter = parserFactory(this.config.config, true);
			const stream = request.payload;
			request.payload = stream.pipe(streamFilter);
		}
		return this.connected.receive(request, oldRequest);
	}
}

exports.Interceptor = LineHeaderInterceptor;
<commit_msg>fix(interceptor): Fix the name and type of the interceptor<commit_after>/* jslint node: true, esnext: true */
"use strict";

const Interceptor = require('kronos-interceptor').Interceptor;
const parserFactory = require('./obj2string');

/**
 * This interceptor cares about the handling of the messages.
 * It will add the hops and copies the messages
 */
class Stream2ObjectInterceptor extends Interceptor {

	constructor(endpoint, config) {
		super(endpoint, config);
	}

	static get name() {
		return "stream-obj-to-string";
	}

	get type() {
		return Stream2ObjectInterceptor.name;
	}

	receive(request, oldRequest) {
		if (request.payload) {
			const streamFilter = parserFactory(this.config.config, true);
			const stream = request.payload;
			request.payload = stream.pipe(streamFilter);
		}
		return this.connected.receive(request, oldRequest);
	}
}

exports.Interceptor = Stream2ObjectInterceptor;
<|endoftext|>


<commit_before>var util = Npm.require('util');
var EventEmitter = Npm.require('events').EventEmitter;
var UPDATE_OPERATIONS = generateUpdateOperationsMap();

function Invalidator(collection, options) {
  this._debug = Npm.require('debug')('sc:inval:' + collection.name);
  this._collection = collection;
  this._options = options || {};

  //query dispose options
  this._queryDisposeInterval = this._options.queryDisposeInterval || 1000 * 60; //every one minute
  this._lastDisposeTime = Date.now();

  this._selectors = [];
  this._queryInfoList = [];
  this._debug('created');
}

util.inherits(Invalidator, EventEmitter);

Invalidator.prototype.initiateQuery = function(selector, options) {
  this._debug('initiateQuery selector:%j options:%j', selector, options);
  options = options || {};

  //check for eligibility for the queryDispose
  if((Date.now() - this._lastDisposeTime) > this._queryDisposeInterval) {
    setTimeout(this._disposeEmptyQueries.bind(this), 0);
  }

  for(var lc=0; lc<this._queryInfoList.length; lc++) {
    var queryInfo = this._queryInfoList[lc];
    if(!EJSON.equals(selector, queryInfo.selector)) {
      continue;
    }

    if(!EJSON.equals(options.sort, queryInfo.options.sort)) {
      continue;
    }

    if(!EJSON.equals(options.fields, queryInfo.options.fields)) {
      continue;
    }

    if(options.limit != queryInfo.options.limit) {
      continue;
    }

    //there is an exiting query
    return queryInfo.query;
  }

  this._debug('creating new query');

  //add a query if there is no maching query
  var query = new Meteor.SmartQuery(this._collection, selector, options);
  this._queryInfoList.push({
    selector: selector,
    options: options,
    query: query
  });

  return query;
};

Invalidator.prototype._disposeEmptyQueries = function() {
  var newQueryInfoList = [];
  for(var lc=0; lc<this._queryInfoList.length; lc++) {
    var queryInfo = this._queryInfoList[lc];
    if(queryInfo.query.countObservers() > 0) {
      newQueryInfoList.push(queryInfo)
    }
  }

  this._lastDisposeTime = Date.now();
  this._queryInfoList = newQueryInfoList;
};

// expect doc to have _id
Invalidator.prototype.insert = function(doc) {
  this._debug('insert doc:%j', doc);
  var self = this;
  var processingCount = 0;

  this._queryInfoList.forEach(function(queryInfo) {
    if(queryInfo.query._selectorMatcher(doc)) {
      processingCount++;
      queryInfo.query.added(doc);
    }
  });

  //since we are sending inserts once we got the document,
  //there is no need to do any special stuff for the write-fence-support
};

Invalidator.prototype.remove = function(id) {
  this._debug('remove id:%s', id);
  var self = this;
  var processingCount = 0;
  var completedCount = 0;

  this._queryInfoList.forEach(function(queryInfo) {
    if(queryInfo.query.idExists(id)) {
      processingCount++;
      queryInfo.query.removed(id, afterRemoved);
    }
  });

  //write fence support
  if(processingCount == 0) {
    self._emitChange('remove', id, afterRemoved);
  }

  function afterRemoved() {
    if(++completedCount == processingCount) {
      self._emitChange('remove', id);
    }
  }
};

Invalidator.prototype.update = function(id, modifier, callback) {
  this._debug('update id:%s modifier:%j', id, modifier);
  var self = this;
  var collection = this._collection;
  var fields;
  var version;

  var fieldMap = Invalidator.updateModifierToFields(modifier);
  collection._collection.findOne({_id: id}, afterFound);

  var changesCount = 0;

  function afterFound(err, doc) {
    if(err) {
      self._emitChange('update', id);
    } else if(doc) {
      //invalidate queries for added if this update affect them
      self._notifyAdded(id, doc, afterChanged);

      //invalidate queries for removed if this affect them
      self._notifyRemoved(id, doc, afterChanged);

      //invalidate queries for update
      self._notifyChanges(id, doc, fieldMap, afterChanged);
    } else {
      console.warn('no such document: ' + id);
      self._emitChange('update', id);
    }

    if(callback) callback();

    function afterChanged() {
      if(++changesCount == 3) {
        self._emitChange('update', id);
      }
    }
  }
};

Invalidator.prototype.poll = function(callback) {
  this._debug('poll');
  var self = this;
  var snapshotCount = 0;
  var received = 0;

  this._queryInfoList.forEach(function(queryInfo) {
    snapshotCount++;
    queryInfo.query.snapshot(afterSnapshot);
  });

  function afterSnapshot() {
    if(++received == snapshotCount) {
      if(callback) {
        callback();
        self.emit('poll');
      }
    }
  }
};

Invalidator.prototype._emitChange = function(type, id) {
  this.emit(type + ':' + id);
};

Invalidator.prototype._onceChanged = function(type, id, callback) {
  var self = this;
  this.once(type + ':' + id, onChange);
  //we need to notify the callback, if a poll happened, assumes everything updated
  this.once('poll', onChange);

  function cleanup() {
    self.removeListener('poll', onChange);
    self.removeListener(type + ':' + id, onChange);
  }

  function onChange() {
    callback();
    cleanup();
  }
};

Invalidator.prototype._notifyAdded = function(id, doc, callback) {
  callback = callback || function() {};
  var processingCount = 0;
  var completedCount = 0;

  this._queryInfoList.forEach(function(queryInfo) {
    var query = queryInfo.query;
    if(!query.idExists(id) && query._selectorMatcher(doc)) {
      processingCount++;
      query.added(doc, afterAdded);
    }
  });

  if(processingCount == 0) {
    callback();
  }

  function afterAdded() {
    if(++completedCount == processingCount) {
      callback();
    }
  }
};

Invalidator.prototype._notifyRemoved = function(id, doc, callback) {
  callback = callback || function() {};
  var processingCount = 0;
  var completedCount = 0;

  this._queryInfoList.forEach(function(queryInfo) {
    var query = queryInfo.query;
    if(query.idExists(id) && !query._selectorMatcher(doc)) {
      processingCount++;
      query.removed(id, afterRemoved);
    }
  });

  if(processingCount == 0) {
    callback();
  }

  function afterRemoved() {
    if(++completedCount == processingCount) {
      callback();
    }
  }
};

Invalidator.prototype._notifyChanges = function(id, doc, fieldMap, callback) {
  callback = callback || function() {};
  var processingCount = 0;
  var completedCount = 0;

  var changes = _.pick(doc, _.keys(fieldMap.update));
  _.keys(fieldMap.remove).forEach(function(field) {
    changes[field] = undefined;
  });

  this._queryInfoList.forEach(function(queryInfo) {
    processingCount++;
    var query = queryInfo.query;
    if(query.idExists(id)) {
      query.changed(id, changes, afterChanged);
    }
  });

  if(processingCount == 0) {
    callback();
  }

  function afterChanged() {
    if(++completedCount == processingCount) {
      callback();
    }
  }
};

Invalidator.updateModifierToFields = function updateModifierToFields(modifier) {
  var result = {update: {}, remove: {}};
  for(var operation in modifier) {
    var action = UPDATE_OPERATIONS[operation];
    pickFields(modifier[operation], action);
  }

  function pickFields(updateCommand, action) {
    if(action == 'UPDATE_ONLY') {
      for(var key in updateCommand) {
        result.update[handleDot(key)] = 1;
      }
    } else if(action == 'REMOVE_ONLY') {
      for(var key in updateCommand) {
        result.remove[handleDot(key)] = 1;
      }
    } else if(action == 'UPDATE_AND_REMOVE') {
      for(var key in updateCommand) {
        result.update[handleDot(key)] = 1;
        result.remove[handleDot(key)] = 1;
      }
    }
  }

  function handleDot(key) {
    var dotIndex = key.indexOf('.');
    if(dotIndex >= 0) {
      return key.substring(0, dotIndex);
    } else {
      return key;
    }
  }

  //no modifiers, then direct doc update
  if(_.keys(result.update).length == 0 && _.keys(result.remove).length == 0) {
    _.keys(modifier).forEach(function(field) {
      if(field[0] != '.' && field[0] != '$') {
        result.update[field] = 1;
      }
    });
  }

  return result;
};

function generateUpdateOperationsMap() {
  var updateOnly = ['$inc', '$setOnInsert', '$set', '$addToSet', '$pop', '$pullAll', '$pull', '$pushAll', '$push', '$bit'];
  var removeOnly = ['$unset'];
  var updateAndRemove = ['$rename'];

  var map = {};
  updateOnly.forEach(function(field) {
    map[field] = 'UPDATE_ONLY';
  });

  removeOnly.forEach(function(field) {
    map[field] = 'REMOVE_ONLY';
  });

  updateAndRemove.forEach(function(field) {
    map[field] = 'UPDATE_AND_REMOVE';
  });

  return map;
};

Meteor.SmartInvalidator = Invalidator;<commit_msg>changed processingCount location<commit_after>var util = Npm.require('util');
var EventEmitter = Npm.require('events').EventEmitter;
var UPDATE_OPERATIONS = generateUpdateOperationsMap();

function Invalidator(collection, options) {
  this._debug = Npm.require('debug')('sc:inval:' + collection.name);
  this._collection = collection;
  this._options = options || {};

  //query dispose options
  this._queryDisposeInterval = this._options.queryDisposeInterval || 1000 * 60; //every one minute
  this._lastDisposeTime = Date.now();

  this._selectors = [];
  this._queryInfoList = [];
  this._debug('created');
}

util.inherits(Invalidator, EventEmitter);

Invalidator.prototype.initiateQuery = function(selector, options) {
  this._debug('initiateQuery selector:%j options:%j', selector, options);
  options = options || {};

  //check for eligibility for the queryDispose
  if((Date.now() - this._lastDisposeTime) > this._queryDisposeInterval) {
    setTimeout(this._disposeEmptyQueries.bind(this), 0);
  }

  for(var lc=0; lc<this._queryInfoList.length; lc++) {
    var queryInfo = this._queryInfoList[lc];
    if(!EJSON.equals(selector, queryInfo.selector)) {
      continue;
    }

    if(!EJSON.equals(options.sort, queryInfo.options.sort)) {
      continue;
    }

    if(!EJSON.equals(options.fields, queryInfo.options.fields)) {
      continue;
    }

    if(options.limit != queryInfo.options.limit) {
      continue;
    }

    //there is an exiting query
    return queryInfo.query;
  }

  this._debug('creating new query');

  //add a query if there is no maching query
  var query = new Meteor.SmartQuery(this._collection, selector, options);
  this._queryInfoList.push({
    selector: selector,
    options: options,
    query: query
  });

  return query;
};

Invalidator.prototype._disposeEmptyQueries = function() {
  var newQueryInfoList = [];
  for(var lc=0; lc<this._queryInfoList.length; lc++) {
    var queryInfo = this._queryInfoList[lc];
    if(queryInfo.query.countObservers() > 0) {
      newQueryInfoList.push(queryInfo)
    }
  }

  this._lastDisposeTime = Date.now();
  this._queryInfoList = newQueryInfoList;
};

// expect doc to have _id
Invalidator.prototype.insert = function(doc) {
  this._debug('insert doc:%j', doc);
  var self = this;
  var processingCount = 0;

  this._queryInfoList.forEach(function(queryInfo) {
    if(queryInfo.query._selectorMatcher(doc)) {
      processingCount++;
      queryInfo.query.added(doc);
    }
  });

  //since we are sending inserts once we got the document,
  //there is no need to do any special stuff for the write-fence-support
};

Invalidator.prototype.remove = function(id) {
  this._debug('remove id:%s', id);
  var self = this;
  var processingCount = 0;
  var completedCount = 0;

  this._queryInfoList.forEach(function(queryInfo) {
    if(queryInfo.query.idExists(id)) {
      processingCount++;
      queryInfo.query.removed(id, afterRemoved);
    }
  });

  //write fence support
  if(processingCount == 0) {
    self._emitChange('remove', id, afterRemoved);
  }

  function afterRemoved() {
    if(++completedCount == processingCount) {
      self._emitChange('remove', id);
    }
  }
};

Invalidator.prototype.update = function(id, modifier, callback) {
  this._debug('update id:%s modifier:%j', id, modifier);
  var self = this;
  var collection = this._collection;
  var fields;
  var version;

  var fieldMap = Invalidator.updateModifierToFields(modifier);
  collection._collection.findOne({_id: id}, afterFound);

  var changesCount = 0;

  function afterFound(err, doc) {
    if(err) {
      self._emitChange('update', id);
    } else if(doc) {
      //invalidate queries for added if this update affect them
      self._notifyAdded(id, doc, afterChanged);

      //invalidate queries for removed if this affect them
      self._notifyRemoved(id, doc, afterChanged);

      //invalidate queries for update
      self._notifyChanges(id, doc, fieldMap, afterChanged);
    } else {
      console.warn('no such document: ' + id);
      self._emitChange('update', id);
    }

    if(callback) callback();

    function afterChanged() {
      if(++changesCount == 3) {
        self._emitChange('update', id);
      }
    }
  }
};

Invalidator.prototype.poll = function(callback) {
  this._debug('poll');
  var self = this;
  var snapshotCount = 0;
  var received = 0;

  this._queryInfoList.forEach(function(queryInfo) {
    snapshotCount++;
    queryInfo.query.snapshot(afterSnapshot);
  });

  function afterSnapshot() {
    if(++received == snapshotCount) {
      if(callback) {
        callback();
        self.emit('poll');
      }
    }
  }
};

Invalidator.prototype._emitChange = function(type, id) {
  this.emit(type + ':' + id);
};

Invalidator.prototype._onceChanged = function(type, id, callback) {
  var self = this;
  this.once(type + ':' + id, onChange);
  //we need to notify the callback, if a poll happened, assumes everything updated
  this.once('poll', onChange);

  function cleanup() {
    self.removeListener('poll', onChange);
    self.removeListener(type + ':' + id, onChange);
  }

  function onChange() {
    callback();
    cleanup();
  }
};

Invalidator.prototype._notifyAdded = function(id, doc, callback) {
  callback = callback || function() {};
  var processingCount = 0;
  var completedCount = 0;

  this._queryInfoList.forEach(function(queryInfo) {
    var query = queryInfo.query;
    if(!query.idExists(id) && query._selectorMatcher(doc)) {
      processingCount++;
      query.added(doc, afterAdded);
    }
  });

  if(processingCount == 0) {
    callback();
  }

  function afterAdded() {
    if(++completedCount == processingCount) {
      callback();
    }
  }
};

Invalidator.prototype._notifyRemoved = function(id, doc, callback) {
  callback = callback || function() {};
  var processingCount = 0;
  var completedCount = 0;

  this._queryInfoList.forEach(function(queryInfo) {
    var query = queryInfo.query;
    if(query.idExists(id) && !query._selectorMatcher(doc)) {
      processingCount++;
      query.removed(id, afterRemoved);
    }
  });

  if(processingCount == 0) {
    callback();
  }

  function afterRemoved() {
    if(++completedCount == processingCount) {
      callback();
    }
  }
};

Invalidator.prototype._notifyChanges = function(id, doc, fieldMap, callback) {
  callback = callback || function() {};
  var processingCount = 0;
  var completedCount = 0;

  var changes = _.pick(doc, _.keys(fieldMap.update));
  _.keys(fieldMap.remove).forEach(function(field) {
    changes[field] = undefined;
  });

  this._queryInfoList.forEach(function(queryInfo) {
    var query = queryInfo.query;
    if(query.idExists(id)) {
      processingCount++;
      query.changed(id, changes, afterChanged);
    }
  });

  if(processingCount == 0) {
    callback();
  }

  function afterChanged() {
    if(++completedCount == processingCount) {
      callback();
    }
  }
};

Invalidator.updateModifierToFields = function updateModifierToFields(modifier) {
  var result = {update: {}, remove: {}};
  for(var operation in modifier) {
    var action = UPDATE_OPERATIONS[operation];
    pickFields(modifier[operation], action);
  }

  function pickFields(updateCommand, action) {
    if(action == 'UPDATE_ONLY') {
      for(var key in updateCommand) {
        result.update[handleDot(key)] = 1;
      }
    } else if(action == 'REMOVE_ONLY') {
      for(var key in updateCommand) {
        result.remove[handleDot(key)] = 1;
      }
    } else if(action == 'UPDATE_AND_REMOVE') {
      for(var key in updateCommand) {
        result.update[handleDot(key)] = 1;
        result.remove[handleDot(key)] = 1;
      }
    }
  }

  function handleDot(key) {
    var dotIndex = key.indexOf('.');
    if(dotIndex >= 0) {
      return key.substring(0, dotIndex);
    } else {
      return key;
    }
  }

  //no modifiers, then direct doc update
  if(_.keys(result.update).length == 0 && _.keys(result.remove).length == 0) {
    _.keys(modifier).forEach(function(field) {
      if(field[0] != '.' && field[0] != '$') {
        result.update[field] = 1;
      }
    });
  }

  return result;
};

function generateUpdateOperationsMap() {
  var updateOnly = ['$inc', '$setOnInsert', '$set', '$addToSet', '$pop', '$pullAll', '$pull', '$pushAll', '$push', '$bit'];
  var removeOnly = ['$unset'];
  var updateAndRemove = ['$rename'];

  var map = {};
  updateOnly.forEach(function(field) {
    map[field] = 'UPDATE_ONLY';
  });

  removeOnly.forEach(function(field) {
    map[field] = 'REMOVE_ONLY';
  });

  updateAndRemove.forEach(function(field) {
    map[field] = 'UPDATE_AND_REMOVE';
  });

  return map;
};

Meteor.SmartInvalidator = Invalidator;<|endoftext|>


<commit_before>
/*!
 * Jade - Parser
 * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
 * MIT Licensed
 */

/**
 * Module dependencies.
 */

var Lexer = require('./lexer'),
    nodes = require('./nodes'),
    filters = require('./filters'),
    doctypes = require('./doctypes'),
    selfClosing = require('./self-closing');

/**
 * Initialize `Parser` with the given input `str` and `filename`.
 *
 * @param {String} str
 * @param {String} filename
 * @api public
 */

var Parser = module.exports = function Parser(str, filename){
    this.lexer = new Lexer(str);
    this.filename = filename;
};

/**
 * Parser prototype.
 */

Parser.prototype = {

    /**
     * Output token stack for debugging.
     *
     * @api private
     */

    debug: function(){
        var tok, width = 8;
        while ((tok = this.advance).type !== 'eos') {
            var type = tok.type,
            pad = width - type.length;
            while (pad--) type += ' ';
            console.log('%d : \x1b[1m%s\x1b[0m %j',
                tok.line,
                type,
                tok.val);
        }
    },

    /**
     * Return the next token object.
     *
     * @return {Object}
     * @api private
     */

    get advance(){
        return this.lexer.advance;
    },

    /**
     * Single token lookahead.
     *
     * @return {Object}
     * @api private
     */

    get peek() {
        return this.lexer.peek;
    },

    /**
     * Instrument template lineno.
     *
     * @return {String}
     * @api private
     */

    get line() {
        return '_.lineno = ' + this.lexer.lineno + ';';
    },

    /**
     * Parse input returning a string of js for evaluation.
     *
     * @return {String}
     * @api public
     */

    parse: function(){
        var block = new nodes.Block;
        while (this.peek.type !== 'eos') {
            block.push(this.parseExpr());
        }
        console.log('ROOT:')
        console.log(require('sys').inspect(block, false, 8, true))
        return new nodes.Root(block);
    },

    /**
     * Expect the given type, or throw an exception.
     *
     * @param {String} type
     * @api private
     */

    expect: function(type){
        if (this.peek.type === type) {
            return this.advance;
        } else {
            throw new Error('expected "' + type + '", but got "' + this.peek.type + '"');
        }
    },

    /**
     *   tag
     * | id
     * | class
     * | text
     * | filter
     * | doctype
     * | comment
     * | each block
     * | code block?
     * | expr newline
     */

    parseExpr: function(){
        switch (this.peek.type) {
            case 'tag':
                return this.parseTag();
            case 'doctype':
                return this.parseDoctype();
            case 'filter':
                return this.parseFilter();
            case 'comment':
                return this.parseComment();
            case 'text':
                return "buf.push('" + interpolate(this.advance.val.replace(/'/g, "\\'")) + " ');";
            case 'each':
                return this.parseEach();
            case 'code':
                return this.parseCode();
            case 'newline':
                this.advance;
                return this.parseExpr();
            case 'id':
            case 'class':
                var tok = this.advance;
                this.lexer.defer(this.lexer.tok('tag', 'div'));
                this.lexer.defer(tok);
                return this.parseExpr();
        }
    },

    /**
     * code
     */

    parseCode: function(){
        var tok = this.advance,
            node = new nodes.Code(tok.val, tok.buffer);
        if ('indent' == this.peek.type) node.push(this.parseBlock());
        return node;
    },

    /**
     * comment
     */

    parseComment: function(){
        var tok = this.expect('comment');
        return new nodes.Comment(tok.val, tok.buffer);
    },

    /**
     * doctype
     */

    parseDoctype: function(){
        var tok = this.expect('doctype');
        return new nodes.Doctype(tok.val);
    },

    /**
     * filter text
     */

    parseFilter: function(){
        var tok = this.expect('filter');
        return new nodes.Filter(tok.val, this.parseTextBlock());
    },

    /**
     * each block
     */

    parseEach: function(){
        var tok = this.expect('each');
        return new nodes.Each(tok.code, tok.val, tok.key, this.parseBlock());
    },

    /**
     * indent (text | newline)* outdent
     */

    parseTextBlock: function(){
        var textBlock = new nodes.TextBlock;
        this.expect('indent');
        while (this.peek.type === 'text' || this.peek.type === 'newline') {
            if (this.peek.type === 'newline') {
                this.advance;
                textBlock.addLine(new nodes.Text('\n'));
            } else {
                textBlock.addLine(this.advance);
            }
        }
        this.expect('outdent');
        return textBlock;
    },

    /**
     * indent expr* outdent
     */

    parseBlock: function(){
        var block = new nodes.Block;
        this.expect('indent');
        while (this.peek.type !== 'outdent') {
            block.push(this.parseExpr());
        }
        this.expect('outdent');
        return block;
    },

    /**
     * tag (attrs | class | id)* text? code? newline* block?
     */

    parseTag: function(){
        var name = this.advance.val,
            tag = new nodes.Tag(name);

        // (attrs | class | id)*
        out:
            while (1) {
                switch (this.peek.type) {
                    case 'id':
                    case 'class':
                        var tok = this.advance;
                        tag.addAttribute(new nodes.Attribute(tok.type, tok.val));
                        continue;
                    case 'attrs':
                        var obj = this.advance.attrs,
                            names = Object.keys(obj);
                        for (var i = 0, len = names.length; i < len; ++i) {
                            var name = names[i],
                                val = obj[name];
                            tag.addAttribute(new nodes.Attribute(name, val));
                        }
                        continue;
                    default:
                        break out;
                }
            }

        // text?
        if (this.peek.type === 'text') {
            buf.push("buf.push('"
                + interpolate(this.advance.val
                    .replace(/^ /, '')
                    .replace(/'/g, "\\'"))
                + "');");
        }

        // code?
        if (this.peek.type === 'code') {
            var tok = this.advance;
            if (tok.buffer) {
                buf.push('buf.push(' + (tok.escape
                    ? 'escape(' + tok.val + ')'
                    : tok.val) + ');');
            } else {
                buf.push(tok.val + ';');
            }
        }

        // newline*
        while (this.peek.type === 'newline') this.advance;

        // block?
        if (this.peek.type === 'indent') {
            buf.push(this.parseBlock());
        }

        // Build attrs
        if (hasAttrs) {
            // Classes
            if (classes.length) {
                attrs['class'] = '"' + classes.join(' ')  + '"';
            }
            if (codeClass) {
                if (attrs['class']) {
                    attrs['class'] += ' + " " + (' + codeClass + ')';
                } else {
                    attrs['class'] = codeClass;
                }
            }

            // Attributes
            attrBuf += "' + attrs({ ";
            var keys = Object.keys(attrs);
            for (var i = 0, len = keys.length; i < len; ++i) {
                var key = keys[i],
                    val = attrs[key];
                attrBuf += "'" + key + "': " + val + (i === len - 1 ? '' : ', ');
            }
            attrBuf += " }) + '";
        } else {
            attrBuf = '';
        }

        // Build the tag
        if (selfClosing.indexOf(name) >= 0) {
            return line + [
                "buf.push('<" + name + attrBuf + (html5 ? '' : ' /' ) + ">');",
                buf.join('\n')
            ].join('\n');
        } else {
            return line + [
                "buf.push('<" + name + attrBuf + ">');",
                buf.join('\n'),
                "buf.push('</" + name + ">');"
            ].join('\n');
        }
    }
};

/**
 * Dummy iteration template.
 */

var iterate = (function(){
    var __vals = __obj__;
    if (__vals instanceof Array) {
        for (var i = 0, len = __vals.length; i < len; ++i) {
            var __key__ = i;
            var __val__ = __vals[i];
            __block__
        }
    } else if (typeof __vals === 'object') {
        var keys = Object.keys(__vals);
        for (var i = 0, len = keys.length; i < len; ++i) {
            var __key__ = keys[i];
            var __val__ = __vals[__key__];
            __block__
        }
    } else {
        var __val__ = __vals;
        __block__
    }
}).toString();

/**
 * Convert interpolation in the given string to JavaScript.
 *
 * @param {String} str
 * @return {String}
 * @api private
 */

function interpolate(str){
    return str.replace(/(\\)?[#$]{(.*?)}/g, function(str, escape, code){
        return escape
            ? str
            : "' + (" + code.replace(/\\'/g, "'") + ") + '";
    });
}<commit_msg>Refactored Parser#parseTag()<commit_after>
/*!
 * Jade - Parser
 * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
 * MIT Licensed
 */

/**
 * Module dependencies.
 */

var Lexer = require('./lexer'),
    nodes = require('./nodes'),
    filters = require('./filters'),
    doctypes = require('./doctypes'),
    selfClosing = require('./self-closing');

/**
 * Initialize `Parser` with the given input `str` and `filename`.
 *
 * @param {String} str
 * @param {String} filename
 * @api public
 */

var Parser = module.exports = function Parser(str, filename){
    this.lexer = new Lexer(str);
    this.filename = filename;
};

/**
 * Parser prototype.
 */

Parser.prototype = {

    /**
     * Output token stack for debugging.
     *
     * @api private
     */

    debug: function(){
        var tok, width = 8;
        while ((tok = this.advance).type !== 'eos') {
            var type = tok.type,
            pad = width - type.length;
            while (pad--) type += ' ';
            console.log('%d : \x1b[1m%s\x1b[0m %j',
                tok.line,
                type,
                tok.val);
        }
    },

    /**
     * Return the next token object.
     *
     * @return {Object}
     * @api private
     */

    get advance(){
        return this.lexer.advance;
    },

    /**
     * Single token lookahead.
     *
     * @return {Object}
     * @api private
     */

    get peek() {
        return this.lexer.peek;
    },

    /**
     * Instrument template lineno.
     *
     * @return {String}
     * @api private
     */

    get line() {
        return '_.lineno = ' + this.lexer.lineno + ';';
    },

    /**
     * Parse input returning a string of js for evaluation.
     *
     * @return {String}
     * @api public
     */

    parse: function(){
        var block = new nodes.Block;
        while (this.peek.type !== 'eos') {
            block.push(this.parseExpr());
        }
        console.log('ROOT:')
        console.log(require('sys').inspect(block, false, 8, true))
        return new nodes.Root(block);
    },

    /**
     * Expect the given type, or throw an exception.
     *
     * @param {String} type
     * @api private
     */

    expect: function(type){
        if (this.peek.type === type) {
            return this.advance;
        } else {
            throw new Error('expected "' + type + '", but got "' + this.peek.type + '"');
        }
    },

    /**
     *   tag
     * | id
     * | class
     * | text
     * | filter
     * | doctype
     * | comment
     * | each block
     * | code block?
     * | expr newline
     */

    parseExpr: function(){
        switch (this.peek.type) {
            case 'tag':
                return this.parseTag();
            case 'doctype':
                return this.parseDoctype();
            case 'filter':
                return this.parseFilter();
            case 'comment':
                return this.parseComment();
            case 'text':
                return "buf.push('" + interpolate(this.advance.val.replace(/'/g, "\\'")) + " ');";
            case 'each':
                return this.parseEach();
            case 'code':
                return this.parseCode();
            case 'newline':
                this.advance;
                return this.parseExpr();
            case 'id':
            case 'class':
                var tok = this.advance;
                this.lexer.defer(this.lexer.tok('tag', 'div'));
                this.lexer.defer(tok);
                return this.parseExpr();
        }
    },

    /**
     * code
     */

    parseCode: function(){
        var tok = this.advance,
            node = new nodes.Code(tok.val, tok.buffer);
        if ('indent' == this.peek.type) node.push(this.parseBlock());
        return node;
    },

    /**
     * comment
     */

    parseComment: function(){
        var tok = this.expect('comment');
        return new nodes.Comment(tok.val, tok.buffer);
    },

    /**
     * doctype
     */

    parseDoctype: function(){
        var tok = this.expect('doctype');
        return new nodes.Doctype(tok.val);
    },

    /**
     * filter text
     */

    parseFilter: function(){
        var tok = this.expect('filter');
        return new nodes.Filter(tok.val, this.parseTextBlock());
    },

    /**
     * each block
     */

    parseEach: function(){
        var tok = this.expect('each');
        return new nodes.Each(tok.code, tok.val, tok.key, this.parseBlock());
    },

    /**
     * indent (text | newline)* outdent
     */

    parseTextBlock: function(){
        var textBlock = new nodes.TextBlock;
        this.expect('indent');
        while (this.peek.type === 'text' || this.peek.type === 'newline') {
            if (this.peek.type === 'newline') {
                this.advance;
                textBlock.addLine(new nodes.Text('\n'));
            } else {
                textBlock.addLine(this.advance);
            }
        }
        this.expect('outdent');
        return textBlock;
    },

    /**
     * indent expr* outdent
     */

    parseBlock: function(){
        var block = new nodes.Block;
        this.expect('indent');
        while (this.peek.type !== 'outdent') {
            block.push(this.parseExpr());
        }
        this.expect('outdent');
        return block;
    },

    /**
     * tag (attrs | class | id)* text? code? newline* block?
     */

    parseTag: function(){
        var name = this.advance.val,
            tag = new nodes.Tag(name);

        // (attrs | class | id)*
        out:
            while (1) {
                switch (this.peek.type) {
                    case 'id':
                    case 'class':
                        var tok = this.advance;
                        tag.addAttribute(new nodes.Attribute(tok.type, tok.val));
                        continue;
                    case 'attrs':
                        var obj = this.advance.attrs,
                            names = Object.keys(obj);
                        for (var i = 0, len = names.length; i < len; ++i) {
                            var name = names[i],
                                val = obj[name];
                            tag.addAttribute(new nodes.Attribute(name, val));
                        }
                        continue;
                    default:
                        break out;
                }
            }

        // text?
        if (this.peek.type === 'text') {
            tag.text = new nodes.Text(this.advance);
        }

        // code?
        if (this.peek.type === 'code') {
            var tok = this.advance;
            tag.code = new nodes.Code(tok.val, tok.buffer);
        }

        // newline*
        while (this.peek.type === 'newline') this.advance;

        // block?
        if (this.peek.type === 'indent') {
            tag.block = this.parseBlock();
        }

        return tag;
    }
};

/**
 * Dummy iteration template.
 */

var iterate = (function(){
    var __vals = __obj__;
    if (__vals instanceof Array) {
        for (var i = 0, len = __vals.length; i < len; ++i) {
            var __key__ = i;
            var __val__ = __vals[i];
            __block__
        }
    } else if (typeof __vals === 'object') {
        var keys = Object.keys(__vals);
        for (var i = 0, len = keys.length; i < len; ++i) {
            var __key__ = keys[i];
            var __val__ = __vals[__key__];
            __block__
        }
    } else {
        var __val__ = __vals;
        __block__
    }
}).toString();

/**
 * Convert interpolation in the given string to JavaScript.
 *
 * @param {String} str
 * @return {String}
 * @api private
 */

function interpolate(str){
    return str.replace(/(\\)?[#$]{(.*?)}/g, function(str, escape, code){
        return escape
            ? str
            : "' + (" + code.replace(/\\'/g, "'") + ") + '";
    });
}<|endoftext|>