<commit_before>Activities = new Mongo.Collection('activities');

var ActivitiesSchema = new SimpleSchema({
  residentIds: {
    type: [String],
    autoform: {
      options: function() {
        // Get list of homes, sorted alphabetically
        const homes = Homes.find({}, {sort: {name: 1}}).fetch();

        // Create an array residents grouped by home
        const residentsSelectOptions = _.map(homes, function (home) {
          // Get home ID
          const homeId = home._id;

          // do not show departed residents
          const departed = false;

          // do not show residents who are on hiatus
          const onHiatus = false;

          // Sort by first name in alphabetical order
          const sort = {firstName: 1}

          // Get a list of residents for current home
          const homeResidents = Residents.find({ homeId, departed, onHiatus }, {sort}).fetch();

          // Create an object containing a home and its residents
          const homeGroup = {
            optgroup: home.name,
            options: _.map(homeResidents, function (resident) {
              // Create an object containing the resident name and ID
              const residentObject = {
                value: resident._id,
                label: resident.fullName()
              };

              return residentObject;
           })
          }

          return homeGroup;
        });

        return residentsSelectOptions;
      }
    }
  },
  activityTypeId: {
    type: String,
    autoform: {
      options: function() {
        // Get all activity types from db
        var activityTypes = ActivityTypes.find().fetch();

        // Create an options array of activity types with label and value pairs
        var activityTypesOptions = _.map(activityTypes, function(activityType) {
          return {
            label: activityType.name,
            value: activityType._id
          };
        });

        return activityTypesOptions;
      }
    }
  },
  activityDate: {
    type: Date,
    min: function () {
      // Only allow activities to be recorded for the past seven days
      return moment().subtract(7, "days").toDate();

    },
    max: function () {
      // Do not allow activity dates in the future
      return new Date();
    },
    autoform: {
      afFieldInput: {
        type: "bootstrap-datepicker"
      }
    }
  },
  duration: {
    type: Number,
  },
  facilitatorRoleId: {
    type: String,
    autoform: {
      options: function() {
        // Get all roles, except admin, from db
        var roles = Meteor.roles.find({name: {$not: "admin"}}).fetch();

        // Create an options array of roles with label (name) and value (id) pairs
        var rolesOptions = _.map(roles, function(role) {
          // Don't return the admin role
          // Return role name and ID object
          return {
            label: role.name,
            value: role._id
          };
        });

        return rolesOptions;
      }
    }
  }
});

// Add i18n tags
ActivitiesSchema.i18n("activities");

// Attach schema to collection
Activities.attachSchema(ActivitiesSchema);

Activities.helpers({
  residentNames: function () {
    // Get the Resident ID;
    var residentIds = this.residentIds;

    // Get Resident(s) from Residents collection, by ID(s)
    var residents = Residents.find({'_id': {$in: residentIds}}).fetch();

    // Create an array of resident names
    var residentNamesArray = _.map(residents, function (resident) {
      return resident.firstName;
    });

    // Return the resident name(s), joining with ", "
    return residentNamesArray.join(", ");
  },
  activityType: function () {
    // Get the Activity Type ID
    var activityTypeId = this.activityTypeId;

    // Get Activity Type from Activity Types collection, by ID
    var activityType = ActivityTypes.findOne(activityTypeId);

    // Return the Activity Type name
    return activityType.name;
  },
  activityDateFormatted: function () {
    // Get activity date
    var activityDate = this.activityDate;

    // Format activity date
    var activityDateFormatted = moment(activityDate).format("D.M.YYYY");

    return activityDateFormatted;
  },
  facilitatorRole: function () {
    // Get the facilitator Role ID
    var facilitatorRoleId = this.facilitatorRoleId;

    // Get Role from Roles collection, by ID
    var facilitatorRole = Meteor.roles.findOne(facilitatorRoleId);

    // Return the Role name
    return facilitatorRole.name;
  },
  timeAgo: function () {
    var activityDate = this.activityDate;

    var timeAgo = moment(activityDate).fromNow();

    return timeAgo;
  }
});

Activities.allow({
  insert: function () {
    return true;
  },
  update: function () {
    // Get current user ID
    var currentUserId = Meteor.userId();

    // Check if current user has Admin role
    var currentUserIsAdmin = Roles.userIsInRole(currentUserId, ["admin"]);

    // Only show edit column for users with Admin role
    if (currentUserIsAdmin) {
      return true;
    } else {
      return false;
    }
  },
  remove: function () {
    // Get current user ID
    var currentUserId = Meteor.userId();

    // Check if current user has Admin role
    var currentUserIsAdmin = Roles.userIsInRole(currentUserId, ["admin"]);

    // Only show edit column for users with Admin role
    if (currentUserIsAdmin) {
      return true;
    } else {
      return false;
    }
  }
});
<commit_msg>Add activityDate field index<commit_after>Activities = new Mongo.Collection('activities');

Meteor.startup(function () {
  // Make sure activityDate field is indexed for performance
  Activities._ensureIndex({'activityDate': 1});
});

var ActivitiesSchema = new SimpleSchema({
  residentIds: {
    type: [String],
    autoform: {
      options: function() {
        // Get list of homes, sorted alphabetically
        const homes = Homes.find({}, {sort: {name: 1}}).fetch();

        // Create an array residents grouped by home
        const residentsSelectOptions = _.map(homes, function (home) {
          // Get home ID
          const homeId = home._id;

          // do not show departed residents
          const departed = false;

          // do not show residents who are on hiatus
          const onHiatus = false;

          // Sort by first name in alphabetical order
          const sort = {firstName: 1}

          // Get a list of residents for current home
          const homeResidents = Residents.find({ homeId, departed, onHiatus }, {sort}).fetch();

          // Create an object containing a home and its residents
          const homeGroup = {
            optgroup: home.name,
            options: _.map(homeResidents, function (resident) {
              // Create an object containing the resident name and ID
              const residentObject = {
                value: resident._id,
                label: resident.fullName()
              };

              return residentObject;
           })
          }

          return homeGroup;
        });

        return residentsSelectOptions;
      }
    }
  },
  activityTypeId: {
    type: String,
    autoform: {
      options: function() {
        // Get all activity types from db
        var activityTypes = ActivityTypes.find().fetch();

        // Create an options array of activity types with label and value pairs
        var activityTypesOptions = _.map(activityTypes, function(activityType) {
          return {
            label: activityType.name,
            value: activityType._id
          };
        });

        return activityTypesOptions;
      }
    }
  },
  activityDate: {
    type: Date,
    min: function () {
      // Only allow activities to be recorded for the past seven days
      return moment().subtract(7, "days").toDate();

    },
    max: function () {
      // Do not allow activity dates in the future
      return new Date();
    },
    autoform: {
      afFieldInput: {
        type: "bootstrap-datepicker"
      }
    }
  },
  duration: {
    type: Number,
  },
  facilitatorRoleId: {
    type: String,
    autoform: {
      options: function() {
        // Get all roles, except admin, from db
        var roles = Meteor.roles.find({name: {$not: "admin"}}).fetch();

        // Create an options array of roles with label (name) and value (id) pairs
        var rolesOptions = _.map(roles, function(role) {
          // Don't return the admin role
          // Return role name and ID object
          return {
            label: role.name,
            value: role._id
          };
        });

        return rolesOptions;
      }
    }
  }
});

// Add i18n tags
ActivitiesSchema.i18n("activities");

// Attach schema to collection
Activities.attachSchema(ActivitiesSchema);

Activities.helpers({
  residentNames: function () {
    // Get the Resident ID;
    var residentIds = this.residentIds;

    // Get Resident(s) from Residents collection, by ID(s)
    var residents = Residents.find({'_id': {$in: residentIds}}).fetch();

    // Create an array of resident names
    var residentNamesArray = _.map(residents, function (resident) {
      return resident.firstName;
    });

    // Return the resident name(s), joining with ", "
    return residentNamesArray.join(", ");
  },
  activityType: function () {
    // Get the Activity Type ID
    var activityTypeId = this.activityTypeId;

    // Get Activity Type from Activity Types collection, by ID
    var activityType = ActivityTypes.findOne(activityTypeId);

    // Return the Activity Type name
    return activityType.name;
  },
  activityDateFormatted: function () {
    // Get activity date
    var activityDate = this.activityDate;

    // Format activity date
    var activityDateFormatted = moment(activityDate).format("D.M.YYYY");

    return activityDateFormatted;
  },
  facilitatorRole: function () {
    // Get the facilitator Role ID
    var facilitatorRoleId = this.facilitatorRoleId;

    // Get Role from Roles collection, by ID
    var facilitatorRole = Meteor.roles.findOne(facilitatorRoleId);

    // Return the Role name
    return facilitatorRole.name;
  },
  timeAgo: function () {
    var activityDate = this.activityDate;

    var timeAgo = moment(activityDate).fromNow();

    return timeAgo;
  }
});

Activities.allow({
  insert: function () {
    return true;
  },
  update: function () {
    // Get current user ID
    var currentUserId = Meteor.userId();

    // Check if current user has Admin role
    var currentUserIsAdmin = Roles.userIsInRole(currentUserId, ["admin"]);

    // Only show edit column for users with Admin role
    if (currentUserIsAdmin) {
      return true;
    } else {
      return false;
    }
  },
  remove: function () {
    // Get current user ID
    var currentUserId = Meteor.userId();

    // Check if current user has Admin role
    var currentUserIsAdmin = Roles.userIsInRole(currentUserId, ["admin"]);

    // Only show edit column for users with Admin role
    if (currentUserIsAdmin) {
      return true;
    } else {
      return false;
    }
  }
});
<|endoftext|>


<commit_before>/**---------------------------------------------------------------------------------------------------------------------
 * tgi-spec/lib/tgi-spec.source.js
 **/
var Spec = function () {
  this.scripts = []; // array of scripts
  this.nodes = []; // array of Spec.Node's
  this.testsCreated = 0;
  this.testsPending = 0;
  this.testsFailed = 0;
};
Spec.prototype.test = function (testSource, testName, testScript) {
  this.scripts.push({testSource: testSource, testName: testName, testScript: testScript});
};
Spec.prototype.runTests = function (callback) {
  this.testFunction = callback;
  // Load the scripts
  for (var i = 0; i < this.scripts.length; i++) {
    var script = this.scripts[i];
    this.testFunction({log: 'loading test script: ' + script.testName});
    script.testScript(callback);
  }
  this.completionCheck();
};
Spec.prototype.completionCheck = function (force) {
  var spec = this;
  if (!spec.testsPending || force) {
    spec.testFunction({
      done: true,
      testsCreated: spec.testsCreated,
      testsPending: spec.testsPending,
      testsFailed: spec.testsFailed
    });
  } else {
    // Give time for async to finish
    spec.watchdog = spec.watchdog || setTimeout(function () {
      spec.completionCheck(true); // force completion
    }, 500);
  }
};
Spec.prototype.githubMarkdown = function () {
  var spec = this;
  var text = '';
  var i, j, k;
  var arrayOfLines;
  var prettyCode;
  var leadingSpaces = 0;
  var line,line2;
  for (i = 0; i < spec.nodes.length; i++) {
    var node = spec.nodes[i];
    if (i)
      text += '\n';
    switch (node.type) {
      case 'h':
        text += '#### ' + node.text;
        break;
      case 'e':
        arrayOfLines = node.test.testFunction.toString().match(/[^\r\n]+/g);
        prettyCode = '';
        for (j = 1; j < arrayOfLines.length - 1; j++) {
          line = arrayOfLines[j];
          if (j == 1) {
            leadingSpaces=0;
            for (k = 0; k < line.length && line[k] == ' '; k++) {
              leadingSpaces++;
            }
          }
          for (k = 0; k < line.length && k < leadingSpaces && line[k] == ' '; k++) {
            // eat spaces
          }
          line2 = '';
          for (; k < line.length; k++) {
            line2 += line[k];
            // eat spaces
          }
          if (prettyCode.length)
            prettyCode += line2 + '\n';
          prettyCode += line2;
        }
        text += 'EXAMPLE  ' + node.text + ':';
        text += '\n```javascript' +
        '\n' + prettyCode +
        '\n```';
        //if (true) {
          text += '\n
' +
          JSON.stringify(node.test) +
          '
';
        //}
        break;
      default:
      case 'p':
        text += '
' + node.text + '
';
        break;
    }
  }
  return text;
};
/**
 * Spec.Node object for each piece of spec types as follows:
 * h - heading
 * p - paragraph
 * e - example (test)
 **/
Spec.Node = function (args) {
  args = args || {};
  this.type = args.type || null;
  if (!this.type || ((this.type != 'h') && (this.type != 'p') && (this.type != 'e'))) {
    throw new Error('Spec.Node type must be h, p or e');
  }
};
/**
 * Spec.Test for each example
 */
Spec.Test = function (spec, expectedValue, testFunction) {
  var test = this;
  var testExpectedValue;
  var testReturnValue;
  spec.testsCreated++;
  spec.testsPending++;
  test.expectedValue = expectedValue;
  test.testFunction = testFunction;
  test.testThrown = false;
  test.testAsync = false;
  try {
    var returnValue = test.testFunction(function (callbackReturns) {
      spec.testsPending--;
      if (typeof expectedValue !== 'undefined' && expectedValue.async) {
        console.log('shizzle = ' + JSON.stringify(callbackReturns));
        console.log('test.expectedValue.expectedValue = ' + JSON.stringify(test.expectedValue.expectedValue));
        if (test.expectedValue.expectedValue !== callbackReturns) {
          spec.testsFailed++;
        }
      }
    });
    if (typeof expectedValue !== 'undefined') {
      test.testAsync = expectedValue.async;
      testExpectedValue = expectedValue instanceof Error ? expectedValue.toString() : expectedValue;
      testReturnValue = returnValue instanceof Error ? returnValue.toString() : returnValue;
      if (!test.testAsync && testExpectedValue !== testReturnValue) {
        spec.testsFailed++;
      }
    }
  } catch (e) {
    test.testThrown = true;
    if (typeof expectedValue === 'undefined' || !(expectedValue instanceof Error) || e.toString() !== expectedValue.toString()) {
      spec.testsFailed++;
    }
  }
  if (!test.testAsync)
    spec.testsPending--;
};
/**
 * Create a heading node
 **/
Spec.prototype.heading = function (text, func) {
  if (typeof text !== 'string' || text === '') {
    throw new Error('Spec.heading requires text argument');
  }
  this.testFunction({log: 'heading: ' + text});
  var node = new Spec.Node({type: 'h'});
  node.text = text;
  this.nodes.push(node);
  if (func) func();
  return node;
};
/**
 * Create a paragraph node
 **/
Spec.prototype.paragraph = function (text) {
  if (typeof text !== 'string' || text === '') {
    throw new Error('Spec.paragraph requires text argument');
  }
  this.testFunction({log: 'paragraph: ' + text});
  var node = new Spec.Node({type: 'p'});
  node.text = text;
  this.nodes.push(node);
  return node;
};
/**
 * Create a example node
 **/
Spec.prototype.example = function (text, results, testFunction) {
  if (typeof text !== 'string' || text === '') {
    throw new Error('Spec.example requires text argument');
  }
  this.testFunction({log: 'example: ' + text});
  if (typeof testFunction !== 'function') {
    throw new Error('Spec.example 3rd arg is function code or undefined');
  }
  var node = new Spec.Node({type: 'e'});
  node.text = text;
  node.test = new Spec.Test(this, results, testFunction);
  this.nodes.push(node);
  return node;
};
/**
 *
 **/
//Spec.prototype.show = function () {
//};
/**
 *
 **/
Spec.prototype.asyncResults = function (arg) {
  return {async: true, expectedValue: arg};
};
<commit_msg>close to fin<commit_after>/**---------------------------------------------------------------------------------------------------------------------
 * tgi-spec/lib/tgi-spec.source.js
 **/
var Spec = function () {
  this.scripts = []; // array of scripts
  this.nodes = []; // array of Spec.Node's
  this.testsCreated = 0;
  this.testsPending = 0;
  this.testsFailed = 0;
};
Spec.prototype.test = function (testSource, testName, testScript) {
  this.scripts.push({testSource: testSource, testName: testName, testScript: testScript});
};
Spec.prototype.runTests = function (callback) {
  this.testFunction = callback;
  // Load the scripts
  for (var i = 0; i < this.scripts.length; i++) {
    var script = this.scripts[i];
    this.testFunction({log: 'loading test script: ' + script.testName});
    script.testScript(callback);
  }
  this.completionCheck();
};
Spec.prototype.completionCheck = function (force) {
  var spec = this;
  if (!spec.testsPending || force) {
    spec.testFunction({
      done: true,
      testsCreated: spec.testsCreated,
      testsPending: spec.testsPending,
      testsFailed: spec.testsFailed
    });
  } else {
    // Give time for async to finish
    spec.watchdog = spec.watchdog || setTimeout(function () {
      spec.completionCheck(true); // force completion
    }, 500);
  }
};
Spec.prototype.githubMarkdown = function () {
  var spec = this;
  var text = '';
  var i, j, k;
  var arrayOfLines;
  var prettyCode;
  var leadingSpaces = 0;
  var line, line2;
  for (i = 0; i < spec.nodes.length; i++) {
    var node = spec.nodes[i];
    if (i)
      text += '\n';
    switch (node.type) {
      case 'h':
        text += '#### ' + node.text;
        break;
      case 'e':
        text += getCodeChunk();
        break;
      default:
      case 'p':
        text += '
' + node.text + '
';
        break;
    }
  }
  return text;

  function getCodeChunk() {
    var text = '';

    arrayOfLines = node.test.testFunction.toString().match(/[^\r\n]+/g);
    prettyCode = '';
    for (j = 1; j < arrayOfLines.length - 1; j++) {
      line = arrayOfLines[j];
      if (j == 1) {
        leadingSpaces = 0;
        for (k = 0; k < line.length && line[k] == ' '; k++) {
          leadingSpaces++;
        }
      }
      for (k = 0; k < line.length && k < leadingSpaces && line[k] == ' '; k++) {
        // eat spaces
      }
      line2 = '';
      for (; k < line.length; k++) {
        line2 += line[k];
        // eat spaces
      }
      if (prettyCode.length)
        prettyCode += line2 + '\n';
      prettyCode += line2;
    }
    text += 'EXAMPLE  ' + node.text + ':';
    text += '\n```javascript' +
    '\n' + prettyCode +
    '\n```';
    //if (true) {
    text += '\n
' +
    JSON.stringify(node.test) +
    '
';
    //}

    return text;
  }

};
/**
 * Spec.Node object for each piece of spec types as follows:
 * h - heading
 * p - paragraph
 * e - example (test)
 **/
Spec.Node = function (args) {
  args = args || {};
  this.type = args.type || null;
  if (!this.type || ((this.type != 'h') && (this.type != 'p') && (this.type != 'e'))) {
    throw new Error('Spec.Node type must be h, p or e');
  }
};
/**
 * Spec.Test for each example
 */
Spec.Test = function (spec, expectedValue, testFunction) {
  var test = this;
  var testExpectedValue;
  var testReturnValue;
  spec.testsCreated++;
  spec.testsPending++;
  test.expectedValue = expectedValue;
  test.testFunction = testFunction;
  test.testThrown = false;
  test.testAsync = false;
  try {
    var returnValue = test.testFunction(function (callbackReturns) {
      spec.testsPending--;
      if (typeof expectedValue !== 'undefined' && expectedValue.async) {
        console.log('shizzle = ' + JSON.stringify(callbackReturns));
        console.log('test.expectedValue.expectedValue = ' + JSON.stringify(test.expectedValue.expectedValue));
        if (test.expectedValue.expectedValue !== callbackReturns) {
          spec.testsFailed++;
        }
      }
    });
    if (typeof expectedValue !== 'undefined') {
      test.testAsync = expectedValue.async;
      testExpectedValue = expectedValue instanceof Error ? expectedValue.toString() : expectedValue;
      testReturnValue = returnValue instanceof Error ? returnValue.toString() : returnValue;
      if (!test.testAsync && testExpectedValue !== testReturnValue) {
        spec.testsFailed++;
      }
    }
  } catch (e) {
    test.testThrown = true;
    if (typeof expectedValue === 'undefined' || !(expectedValue instanceof Error) || e.toString() !== expectedValue.toString()) {
      spec.testsFailed++;
    }
  }
  if (!test.testAsync)
    spec.testsPending--;
};
/**
 * Create a heading node
 **/
Spec.prototype.heading = function (text, func) {
  if (typeof text !== 'string' || text === '') {
    throw new Error('Spec.heading requires text argument');
  }
  this.testFunction({log: 'heading: ' + text});
  var node = new Spec.Node({type: 'h'});
  node.text = text;
  this.nodes.push(node);
  if (func) func();
  return node;
};
/**
 * Create a paragraph node
 **/
Spec.prototype.paragraph = function (text) {
  if (typeof text !== 'string' || text === '') {
    throw new Error('Spec.paragraph requires text argument');
  }
  this.testFunction({log: 'paragraph: ' + text});
  var node = new Spec.Node({type: 'p'});
  node.text = text;
  this.nodes.push(node);
  return node;
};
/**
 * Create a example node
 **/
Spec.prototype.example = function (text, results, testFunction) {
  if (typeof text !== 'string' || text === '') {
    throw new Error('Spec.example requires text argument');
  }
  this.testFunction({log: 'example: ' + text});
  if (typeof testFunction !== 'function') {
    throw new Error('Spec.example 3rd arg is function code or undefined');
  }
  var node = new Spec.Node({type: 'e'});
  node.text = text;
  node.test = new Spec.Test(this, results, testFunction);
  this.nodes.push(node);
  return node;
};
/**
 *
 **/
//Spec.prototype.show = function () {
//};
/**
 *
 **/
Spec.prototype.asyncResults = function (arg) {
  return {async: true, expectedValue: arg};
};
<|endoftext|>


<commit_before>app.controller("userlistController", function ($scope, $rootScope, userlistFactory, userFactory) {
	$scope.selected = 1;
	$scope.administratorLoggedIn = false;
	$scope.moderatorLoggedIn = false;
	$scope.changeToWhichRoles = null;

	userFactory.update(function (data) {
		$scope.administratorLoggedIn = userFactory.isAdmin();
		$scope.moderatorLoggedIn = userFactory.isModerator();
	});

	$scope.changeRole = function(userID, role){
		userlistFactory.changeRole(userID, role);
	};

	$scope.activateUser = function(username) {
		userlistFactory.activateUser(username);
		$scope.fetchUserlists();
	};
	$scope.fetchUserlists = function() {
		$rootScope.updateHeader();
		userlistFactory.getActiveUsers().then(function(data) {
				$scope.activeUsers = data;
			}, function(error) {
				$scope.error = error;
			});
		userlistFactory.getSuspendedUsers().then(function(data) {
				$scope.suspendedUsers = data;
			}, function(error) {
				$scope.error = error;
			});
	};
	$scope.getRoleFromUser = function(user) {
		if (user.Admin == 1) {
			if (user.Deleteable == 1) {
				return "Moderator";
			} else {
				return "Administrator";
			}
		}
		return "User";
	};
	$scope.getDropdownForUser = function(user) {
		if (user.Admin == 1 && user.Deleteable == 0) {
			return ["Administrator"];
		}
		if ($scope.administratorLoggedIn)
			return ["Administrator", "Moderator", "User"];
		if ($scope.moderatorLoggedIn)
			return ["Moderator", "User"];
		return ["User"];
	};
	$scope.fetchUserlists();
});;
<commit_msg>Remove unused variable<commit_after>app.controller("userlistController", function ($scope, $rootScope, userlistFactory, userFactory) {
	$scope.selected = 1;
	$scope.administratorLoggedIn = false;
	$scope.moderatorLoggedIn = false;

	userFactory.update(function (data) {
		$scope.administratorLoggedIn = userFactory.isAdmin();
		$scope.moderatorLoggedIn = userFactory.isModerator();
	});

	$scope.changeRole = function(userID, role) {
		userlistFactory.changeRole(userID, role);
	};

	$scope.activateUser = function(username) {
		userlistFactory.activateUser(username);
		$scope.fetchUserlists();
	};
	$scope.fetchUserlists = function() {
		$rootScope.updateHeader();
		userlistFactory.getActiveUsers().then(function(data) {
				$scope.activeUsers = data;
			}, function(error) {
				$scope.error = error;
			});
		userlistFactory.getSuspendedUsers().then(function(data) {
				$scope.suspendedUsers = data;
			}, function(error) {
				$scope.error = error;
			});
	};
	$scope.getRoleFromUser = function(user) {
		if (user.Admin == 1) {
			if (user.Deleteable == 1) {
				return "Moderator";
			} else {
				return "Administrator";
			}
		}
		return "User";
	};
	$scope.getDropdownForUser = function(user) {
		if (user.Admin == 1 && user.Deleteable == 0) {
			return ["Administrator"];
		}
		if ($scope.administratorLoggedIn)
			return ["Administrator", "Moderator", "User"];
		if ($scope.moderatorLoggedIn)
			return ["Moderator", "User"];
		return ["User"];
	};
	$scope.fetchUserlists();
});;
<|endoftext|>


<commit_before>////////////////////////////////////////////////
/*   Provided Code - Please Don't Edit   */
////////////////////////////////////////////////
'use strict';

function getInput() {
    console.log("Please choose either 'rock', 'paper', or 'scissors'.")
    return prompt();
}
function randomPlay() {
    var randomNumber = Math.random();
    if (randomNumber < 0.33) {
        return "rock";
    } else if (randomNumber < 0.66) {
        return "paper";
    } else {
        return "scissors";
    }
}
////////////////////////////////////////////////
/*           Write Your Code Below            */
////////////////////////////////////////////////

function getPlayerMove(move) {
    // Write an expression that operates on a variable called `move`
    // If a `move` has a value, your expression should evaluate to that value.
    // However, if `move` is not specified / is null, your expression should equal `getInput()`.
    return var move || getInput();
}

function getComputerMove(move) {
    // Write an expression that operates on a variable called `move`
    // If a `move` has a value, your expression should evaluate to that value.
    // However, if `move` is not specified / is null, your expression should equal `randomPlay()`.
    return var move || randomPlay();
}

function getWinner(playerMove,computerMove) {
    var winner;
    // Write code that will set winner to either 'player', 'computer', or 'tie' based on the values of playerMove and computerMove.
    // Assume that the only values playerMove and computerMove can have are 'rock', 'paper', and 'scissors'.
    // The rules of the game are that 'rock' beats 'scissors', 'scissors' beats 'paper', and 'paper' beats 'rock'.
    /* YOUR CODE HERE */
    return winner;
}

function playToFive() {
    console.log("Let's play Rock, Paper, Scissors");
    var playerWins = 0;
    var computerWins = 0;
    // Write code that plays 'Rock, Paper, Scissors' until either the player or the computer has won five times.
    /* YOUR CODE HERE */
    return [playerWins, computerWins];
}

<commit_msg>updated file with unit 4.5 homework<commit_after>////////////////////////////////////////////////
/*   Provided Code - Please Don't Edit   */
////////////////////////////////////////////////
'use strict';

function getInput() {
    console.log("Please choose either 'rock', 'paper', or 'scissors'.")
    return prompt();
}
function randomPlay() {
    var randomNumber = Math.random();
    if (randomNumber < 0.33) {
        return "rock";
    } else if (randomNumber < 0.66) {
        return "paper";
    } else {
        return "scissors";
    }
}
////////////////////////////////////////////////
/*           Write Your Code Below            */
////////////////////////////////////////////////

function getPlayerMove(move) {
    // Write an expression that operates on a variable called `move`
    // If a `move` has a value, your expression should evaluate to that value.
    // However, if `move` is not specified / is null, your expression should equal `getInput()`.
    return var move || getInput();
}

function getComputerMove(move) {
    // Write an expression that operates on a variable called `move`
    // If a `move` has a value, your expression should evaluate to that value.
    // However, if `move` is not specified / is null, your expression should equal `randomPlay()`.
    return var move || randomPlay();
}

function getWinner(playerMove,computerMove) {
    var winner;
    if (playerMove === "rock" ){
        switch(computerMove){
        case "rock":
        winner = "tie";
        break;
        case "paper":
        winner = "computer";
        break;
        case "scissors":
        winner = "player";
        break;
        }
    }else if (playerMove === "paper" ){
        switch(computerMove){
        case "paper":
        winner = "tie";
        break;
        case "scissors":
        winner = "computer";
        break;
        case "rock":
        winner = "player";
        break;
        }
    }else if (playerMove === "scissors" ){
        switch(computerMove){
        case "scissors":
        winner = "tie";
        break;
        case "rock":
        winner = "computer";
        break;
        case "paper":
        winner = "player";
        break;}
    return winner;
}
}

function playToFive() {
    console.log("Let's play Rock, Paper, Scissors");
    var playerWins = 0;
    var computerWins = 0;
    // Write code that plays 'Rock, Paper, Scissors' until either the player or the computer has won five times.
    /* YOUR CODE HERE */
    return [playerWins, computerWins];
}

<|endoftext|>


<commit_before>
class DisqusManager {

    constructor ( disqus_shortname = '//https-github-com-danilobabovic') {

        this.disqus_shortname = disqus_shortname
        this.loaded = false
        this.lang = "en";
        this.enabled = true
    }

    loadDisqusScrpt ( _disqus_identifier, newUrl, newTitle ) {

        var disqus_shortname = this.disqus_shortname
        var disqus_identifier = _disqus_identifier

        var disqus_url = newUrl

        console.log (

            "loadDisqusScrpt",
            "idisqus_identifier=",
            "disqus_identifier=",
            disqus_identifier,
            "newUrl=",
            newUrl,
            newTitle
        )

        var disqus_config = function () {
    	  this.language = "en";
    	};

        const onLoadDSQ = ( ) => {

            this.loaded = true
            reset ( disqus_identifier, newUrl, newTitle )
        }
        /* * * DON'T EDIT BELOW THIS LINE * * */
        (function() {
            var dsq = document.createElement('script');
            dsq.type = 'text/javascript';
            dsq.async = true;
            dsq.src = disqus_shortname + '.disqus.com/embed.js';
            (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
            dsq.onload = onLoadDSQ
        })();
    }

    reset ( disqus_identifier, newUrl, newTitle ) {

        console.log ( "reset", disqus_identifier, newUrl, newTitle )
        DISQUS.reset({
            reload: true,
            config: function () {
                this.page.identifier = disqus_identifier;
                this.page.url = newUrl;
                this.page.title = newTitle;
                this.language = this.lang;
            }
        });
    }

    insert ( disqus_identifier, newUrl, newTitle ) {

        if (this.enabled == false) return

        if ( this.loaded == false ) {

            this.loadDisqusScrpt ( disqus_identifier, newUrl, newTitle )

        } else {

            this.reset ( disqus_identifier, newUrl, newTitle )

        }
    }
}
<commit_msg>disqus improvement<commit_after>
class DisqusManager {

    constructor ( disqus_shortname = '//https-github-com-danilobabovic') {

        this.disqus_shortname = disqus_shortname
        this.loaded = false
        this.lang = "en";
        this.enabled = true
    }

    loadDisqusScrpt ( _disqus_identifier, newUrl, newTitle ) {

        var disqus_shortname = this.disqus_shortname
        var disqus_identifier = _disqus_identifier

        var disqus_url = newUrl

        console.log (

            "loadDisqusScrpt",
            "idisqus_identifier=",
            "disqus_identifier=",
            disqus_identifier,
            "newUrl=",
            newUrl,
            newTitle
        )

        var disqus_config = function () {
    	  this.language = "en";
    	};

        const onLoadDSQ = ( ) => {

            this.loaded = true
            this.reset ( disqus_identifier, newUrl, newTitle )
        }
        /* * * DON'T EDIT BELOW THIS LINE * * */
        (function() {
            var dsq = document.createElement('script');
            dsq.type = 'text/javascript';
            dsq.async = true;
            dsq.src = disqus_shortname + '.disqus.com/embed.js';
            (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
            dsq.onload = onLoadDSQ
        })();
    }

    reset ( disqus_identifier, newUrl, newTitle ) {

        console.log ( "reset", disqus_identifier, newUrl, newTitle )
        DISQUS.reset({
            reload: true,
            config: function () {
                this.page.identifier = disqus_identifier;
                this.page.url = newUrl;
                this.page.title = newTitle;
                this.language = this.lang;
            }
        });
    }

    insert ( disqus_identifier, newUrl, newTitle ) {

        if (this.enabled == false) return

        if ( this.loaded == false ) {

            this.loadDisqusScrpt ( disqus_identifier, newUrl, newTitle )

        } else {

            this.reset ( disqus_identifier, newUrl, newTitle )

        }
    }
}
<|endoftext|>


<commit_before>////////////////////////////////////////////////
/*   Provided Code - Please Don't Edit   */
////////////////////////////////////////////////
'use strict';

function getInput() {
    console.log("Please choose either 'rock', 'paper', or 'scissors'.");
    return prompt();
} // closes getInput
function randomPlay() {
    var randomNumber = Math.random();
    if (randomNumber < 0.33) {
        return "rock";
    } else if (randomNumber < 0.66) {
        return "paper";
    } else {
        return "scissors";
    }
} // closes randomPlay
////////////////////////////////////////////////
/*           Game Play Instructions           */
////////////////////////////////////////////////

function getPlayerMove(move) {
    if (move !== null) {
        return move;
    } else {
        return getInput();
    }
} // closes getPlayerMove

function getComputerMove(move) {
    if (move !== null) {
        return move;
    } else {
        return randomPlay();
    }
} // closes getComputerMove

function getWinner(playerMove, computerMove) {
    // Write code that will set winner to either 'player', 'computer', or 'tie' based on the values of playerMove and computerMove.
    // Assume that the only values playerMove and computerMove can have are 'rock', 'paper', and 'scissors'.
    // The rules of the game are that 'rock' beats 'scissors', 'scissors' beats 'paper', and 'paper' beats 'rock'.
    var winner;
    if (playerMove === "rock") {
        switch (computerMove) {
            case "rock":
                winner = "tie";
                break;
            case "paper":
                winner = "computer";
                break;
            case "scissors":
                winner = "player";
                break;
            default:
                winner = "Someone didn't pick!";
        } //closes switch
    } else if (playerMove === "paper") {
         switch (computerMove) {
            case "rock":
                winner = "player";
                break;
            case "paper":
                winner = "tie";
                break;
            case "scissors":
                winner = "computer";
                break;
            default:
                winner = "Someone didn't pick!";
        } // closes switch
    } else if (playerMove === "scissors") {
         switch (computerMove) {
            case "rock":
                winner = "computer";
                break;
            case "paper":
                winner = "player";
                break;
            case "scissors":
                winner = "tie";
                break;
            default:
                winner = "Someone didn't pick!";
        } // closes switch
    } // closes else if
    return winner;
} // closes getWinner

////////////////////////////////////////////////
/*           Play to Five                     */
////////////////////////////////////////////////

function playToFive() {
    // Write code that plays 'Rock, Paper, Scissors' until either the player or the computer has won five times.
    console.log("Let's play Rock, Paper, Scissors");
    var playerWins = 0;
    var computerWins = 0;

    while (playerWins < 5 && computerWins < 5) {
        var player = getInput();
        var computer = randomPlay();
        var roundWinner = getWinner(getPlayerMove(player), getComputerMove(computer));
        if (roundWinner === "player") {
            playerWins += 1;
            console.log('Player wins!');
        } else if (roundWinner === "computer") {
            computerWins += 1;
            console.log('Computer wins!');
        } else if (roundWinner === "tie") {
            console.log("It's a tie");
        } else {
            console.log("Invalid player input. Please select rock, paper or scissors");
        }// closes if...else
    } // closes while loop
    return ("Game over! You won " + playerWins + " times. The computer won " + computerWins + " times.");
} // closes PlayToFive

////////////////////////////////////////////////
/*           Play to X                    */
////////////////////////////////////////////////

function bestOf() {
    console.log("How many wins ends the game?");
    return prompt();
} // closes bestOf

function playToX() {
    // Write code that plays 'Rock, Paper, Scissors' until either the player or the computer has won five times.
    console.log("Let's play Rock, Paper, Scissors");
    var endGame = parseInt(bestOf());
    var playerWins = 0;
    var computerWins = 0;

    while (playerWins < endGame && computerWins < endGame) {
        var player = getInput();
        var computer = randomPlay();
        var roundWinner = getWinner(getPlayerMove(player), getComputerMove(computer));
        if (roundWinner === "player") {
            playerWins += 1;
            console.log('You selected ' + player + '. Computer selected ' + computer + '. Player wins!');
        } else if (roundWinner === "computer") {
            computerWins += 1;
            console.log('You selected ' + player + '. Computer selected ' + computer + '. Computer wins!');
        } else if (roundWinner === "tie") {
            console.log("It's a tie");
        } else {
            console.log("Invalid player input. Please select rock, paper or scissors");
        } // closes if...else
    } // closes while loop

    console.log("Game over! You won " + playerWins + " times. The computer won " + computerWins + " times.");
} // closes playToX

////////////////////////////////////////////////
/*          Play Rock Paper Scissors          */
////////////////////////////////////////////////

playToX();<commit_msg>Shortened up the getPlayerMove and getComputerMove functions<commit_after>////////////////////////////////////////////////
/*   Provided Code - Please Don't Edit   */
////////////////////////////////////////////////
'use strict';

function getInput() {
    console.log("Please choose either 'rock', 'paper', or 'scissors'.");
    return prompt();
} // closes getInput
function randomPlay() {
    var randomNumber = Math.random();
    if (randomNumber < 0.33) {
        return "rock";
    } else if (randomNumber < 0.66) {
        return "paper";
    } else {
        return "scissors";
    }
} // closes randomPlay
////////////////////////////////////////////////
/*           Game Play Instructions           */
////////////////////////////////////////////////

function getPlayerMove(move) {
    return move || getInput();
} // closes getPlayerMove

function getComputerMove(move) {
    return move || randomPlay();
} // closes getComputerMove

function getWinner(playerMove, computerMove) {
    // Write code that will set winner to either 'player', 'computer', or 'tie' based on the values of playerMove and computerMove.
    // Assume that the only values playerMove and computerMove can have are 'rock', 'paper', and 'scissors'.
    // The rules of the game are that 'rock' beats 'scissors', 'scissors' beats 'paper', and 'paper' beats 'rock'.
    var winner;
    if (playerMove === "rock") {
        switch (computerMove) {
            case "rock":
                winner = "tie";
                break;
            case "paper":
                winner = "computer";
                break;
            case "scissors":
                winner = "player";
                break;
            default:
                winner = "Someone didn't pick!";
        } //closes switch
    } else if (playerMove === "paper") {
         switch (computerMove) {
            case "rock":
                winner = "player";
                break;
            case "paper":
                winner = "tie";
                break;
            case "scissors":
                winner = "computer";
                break;
            default:
                winner = "Someone didn't pick!";
        } // closes switch
    } else if (playerMove === "scissors") {
         switch (computerMove) {
            case "rock":
                winner = "computer";
                break;
            case "paper":
                winner = "player";
                break;
            case "scissors":
                winner = "tie";
                break;
            default:
                winner = "Someone didn't pick!";
        } // closes switch
    } // closes else if
    return winner;
} // closes getWinner

////////////////////////////////////////////////
/*           Play to Five                     */
////////////////////////////////////////////////

function playToFive() {
    // Write code that plays 'Rock, Paper, Scissors' until either the player or the computer has won five times.
    console.log("Let's play Rock, Paper, Scissors");
    var playerWins = 0;
    var computerWins = 0;

    while (playerWins < 5 && computerWins < 5) {
        var player = getInput();
        var computer = randomPlay();
        var roundWinner = getWinner(getPlayerMove(player), getComputerMove(computer));
        if (roundWinner === "player") {
            playerWins += 1;
            console.log('Player wins!');
        } else if (roundWinner === "computer") {
            computerWins += 1;
            console.log('Computer wins!');
        } else if (roundWinner === "tie") {
            console.log("It's a tie");
        } else {
            console.log("Invalid player input. Please select rock, paper or scissors");
        }// closes if...else
    } // closes while loop
    return ("Game over! You won " + playerWins + " times. The computer won " + computerWins + " times.");
} // closes PlayToFive

////////////////////////////////////////////////
/*           Play to X                    */
////////////////////////////////////////////////

function bestOf() {
    console.log("How many wins ends the game?");
    return prompt();
} // closes bestOf

function playToX() {
    // Write code that plays 'Rock, Paper, Scissors' until either the player or the computer has won five times.
    console.log("Let's play Rock, Paper, Scissors");
    var endGame = parseInt(bestOf());
    var playerWins = 0;
    var computerWins = 0;

    while (playerWins < endGame && computerWins < endGame) {
        var player = getInput();
        var computer = randomPlay();
        var roundWinner = getWinner(getPlayerMove(player), getComputerMove(computer));
        if (roundWinner === "player") {
            playerWins += 1;
            console.log('You selected ' + player + '. Computer selected ' + computer + '. Player wins!');
        } else if (roundWinner === "computer") {
            computerWins += 1;
            console.log('You selected ' + player + '. Computer selected ' + computer + '. Computer wins!');
        } else if (roundWinner === "tie") {
            console.log("It's a tie");
        } else {
            console.log("Invalid player input. Please select rock, paper or scissors");
        } // closes if...else
    } // closes while loop

    console.log("Game over! You won " + playerWins + " times. The computer won " + computerWins + " times.");
} // closes playToX

////////////////////////////////////////////////
/*          Play Rock Paper Scissors          */
////////////////////////////////////////////////

playToX();<|endoftext|>


<commit_before>define(["js/data/DataSource", "js/core/Base", "js/data/Model", "underscore", "flow", "JSON", "js/data/Collection"], function (DataSource, Base, Model, _, flow, JSON, Collection) {

    var rIdExtractor = /http.+\/([^/]+)$/;

    var RestDataProcessor = DataSource.Processor.inherit('src.data.RestDataSource.RestDataProcessor', {
        _composeSubModel: function (model, action, options) {
            // TODO: add href
            return {
                id: model.$.id
            }
        }
    });

    var RestDataSource = DataSource.inherit("js.data.RestDataSource", {

        defaults: {
            endPoint: null,
            gateway: null,
            parsePayloadOnCreate: true,
            parsePayloadOnUpdate: true
        },

        ctor: function(){
            this.callBase();
        },
        $defaultProcessorFactory: RestDataProcessor,
        initialize: function () {

            if (!this.$.endPoint) {
                this.log("No end-point for RestDataSource defined","warn");
            }

            if (!this.$.gateway) {
                this.$.gateway = this.$.endPoint;
            }

        },

        _validateConfiguration: function() {
            if (!this.$dataSourceConfiguration) {
                throw "No DataSourceConfiguration specified";
            }
        },

        initializeFormatProcessors: function () {

            var jsonProcessor = new DataSource.JsonFormatProcessor();
            jsonProcessor.regex = /json/;

            this.$formatProcessors.push(jsonProcessor);

        },

        /***
         * creates the context as RestContext
         *
         * @param properties
         * @param parentContext
         * @return {js.core.RestDataSource.RestContext}
         */
        createContext: function (properties, parentContext) {
            return new RestDataSource.RestContext(this, properties, parentContext);
        },

        /***
         * global query parameter for each REST action
         * @action {String} Rest action [GET, PUT, DELETE, POST]
         * @return {Object}
         */
        getQueryParameter: function (action) {
            return {};
        },

        getPathComponentsForModel: function (model) {

            if (model) {
                var config = this.$dataSourceConfiguration.getConfigurationForModelClass(model.factory);
                if (config) {

                    var path = config.$.path;

                    if (path) {
                        var ret = [path];

                        if (!model.isNew()) {
                            ret.push(model.$.id);
                        }

                        return ret;
                    }
                }
            }

            return null;
        },

        _buildUriForModel: function(model){
            // map model to url
            var modelPathComponents = this.getPathComponentsForModel(model);

            if (!modelPathComponents) {
                callback("path for model unknown", null, options);
                return;
            }

            // build uri
            var uri = [this.$.gateway];
            uri = uri.concat(model.$context.getPathComponents());
            uri = uri.concat(modelPathComponents);

            if (this.$.suffix) {
                uri[uri.length - 1] = uri[uri.length - 1] + "." + this.$.suffix;
            }

            return uri.join("/");
        },
        /**
         *
         * @param model
         * @param options
         * @param callback function(err, model, options)
         */
        loadModel: function (model, options, callback) {
            var self = this;

            // create url
            var url = this._buildUriForModel(model);

            // get queryParameter
            var params = _.defaults(model.$context.getQueryParameter(),
                this.getQueryParameter(RestDataSource.METHOD.GET));

            if(options.noCache){
                params.timestamp = (new Date().getTime());
            }

            flow()
                .seq("xhr", function(cb) {
                    // send request
                    self.$stage.$applicationContext.ajax(url, {
                        type: RestDataSource.METHOD.GET,
                        queryParameter: params
                    }, cb);
                })
                .seq(function(cb) {
                    var xhr = this.vars.xhr;

                    if (xhr.status === 200 || xhr.status === 304) {
                        // find processor that matches the content-type
                        self._parseModelPayload(xhr, model, options);

                        cb(null, model, options);

                    } else {
                        // TODO: better error handling
                        cb("Got status code " + xhr.status + " for '" + url + "'", xhr, null, options);
                    }
                })
                .exec(function(err) {
                    if (callback) {
                        callback(err, model, options);
                    }
                });

        },

        _parseModelPayload: function(xhr, model, options){
            var contentType = xhr.getResponseHeader("Content-Type");

            var formatProcessor = this.getFormatProcessorForContentType(contentType);

            if (!formatProcessor) {
                throw new Error("No formatProcessor for content type '" + contentType + "' found", null, options);
            }

            // deserialize data with format processor
            var data = formatProcessor.deserialize(xhr.responses.text);

            var processor = this.getProcessorForModel(model, options);

            // parse data inside processor
            data = processor.parse(model, data, DataSource.ACTION.LOAD, options);

            // parse data inside model
            data = model.parse(data);

            // set data
            model.set(data);

        },

        /***
         *
         * @param request.url
         * @param request.queryParameter
         * @param request.model
         * @param request.options
         * @param xhr
         * @param callback
         */
        handleSaveError: function (request, xhr, callback) {
            if (callback) {
                callback({
                    status: xhr.status,
                    statusText: xhr.statusText
                });
            }
        },

        /***
         *
         * @param request.url
         * @param request.queryParameter
         * @param request.model
         * @param request.options
         * @param xhr
         * @param callback
         */
        handleCreationSuccess: function (request, xhr, callback) {

            var model = request.model;

            var cb = function(err) {
                if (callback) {
                    callback(err, model, request.options);
                }
            };

            var self = this;

            try { // get location header
                var location = xhr.getResponseHeader('Location');

                if (location) {

                    if(self.$.parsePayloadOnCreate){
                        self._parseModelPayload(xhr, model, request.options);
                    }

                    // extract id
                    var id = this.extractIdFromLocation(location, request);

                    if (id || id === 0) {
                        model.set('id', id);

                        var schema = model.schema, schemaType;
                        for(var schemaKey in schema){
                            if(schema.hasOwnProperty(schemaKey)){
                                schemaType = schema[schemaKey];
                                if(schemaType.classof && schemaType.classof(Collection)){
                                    var contextForChildren = model.getContextForChild(schemaType);
                                    model.set(schemaKey, contextForChildren.createCollection(schemaType, null));
                                }

                            }
                        }

                        model.$context.addEntityToCache(model);

                        // TODO: ask processor if i should call save again to put content
                        cb(null);
                    } else {
                        cb("Id couldn't be extracted");
                    }

                } else {
                    cb('Location header not found');
                }

            } catch (e) {
                cb(e || true);
            }
        },

        /***
         *
         * @param request.url
         * @param request.queryParameter
         * @param request.model
         * @param request.options
         * @param xhr
         * @param callback
         */
        handleUpdateSuccess: function (request, xhr, callback) {

            var model = request.model;

            if(this.$.parsePayloadOnUpdate){
                this._parseModelPayload(xhr, model, request.options);
            }

            if (callback) {
                callback(null, model, request.options);
            }
        },

        extractIdFromLocation: function(location, request) {
            var param = rIdExtractor.exec(location);

            if (param) {
                return param[1];
            }

            return null;
        },

        _saveModel: function (model, options, callback) {

            var action = DataSource.ACTION.UPDATE,
                method = RestDataSource.METHOD.PUT;

            if (model._status() === Model.STATE.NEW) {
                action = DataSource.ACTION.CREATE;
                method = RestDataSource.METHOD.POST;
            }

            var processor = this.getProcessorForModel(model, options);
            var formatProcessor = this.getFormatProcessor(action);
            var self = this;

            // call save of the processor to save sub models
            flow()
                .seq(function(cb) {
                    processor.saveSubModels(model, options, cb)
                })
                .seq(function(cb) {
                    // create url
                    var url = self._buildUriForModel(model);

                    // get queryParameter
                    var params = _.defaults(model.$context.getQueryParameter(),
                        self.getQueryParameter(method));

                    // TODO: create hook, which can modify url and queryParameter

                    var data = processor.compose(model, action, options);

                    // format payload
                    var payload = formatProcessor.serialize(data);

                    // send request
                    self.$stage.$applicationContext.ajax(url, {
                        type: method,
                        queryParameter: params,
                        data: payload,
                        contentType: formatProcessor.getContentType()
                    }, function (err, xhr) {

                        var request = {
                            url: url,
                            queryParameter: params,
                            model: model,
                            options: options
                        };

                        if (!err && action === DataSource.ACTION.CREATE && xhr.status === 201) {
                            self.handleCreationSuccess(request, xhr, cb)
                        } else if (!err && action === DataSource.ACTION.UPDATE && xhr.status === 200) {
                            self.handleUpdateSuccess(request, xhr, cb);
                        }
                        else {
                            // error handling
                            self.handleSaveError(request, xhr, cb);
                        }

                    });
                })
                .exec(function(err){
                    callback && callback(err, model, options);
                })

        },
        extractListMetaData: function (list, payload, options) {
            return payload;
        },

        extractListData: function (list, payload, options) {
            for (var key in payload) {
                if (payload.hasOwnProperty(key)) {
                    if (_.isArray(payload[key])) {
                        return payload[key];
                    }
                }
            }
        },
        loadCollectionPage: function (page, options, callback) {

            var rootCollection = page.getRootCollection();
            var config =  this.$dataSourceConfiguration.getConfigurationForModelClass(rootCollection.$modelFactory);

            if(!config){
                throw new Error("Couldnt find path config for " + rootCollection.$modelFactory.prototype.constructor.name);
            }
            var modelPathComponents = [config.$.path];

            if (!modelPathComponents) {
                callback("path for model unknown", null, options);
                return;
            }

            // build uri
            var uri = [this.$.gateway];
            uri = uri.concat(rootCollection.$context.getPathComponents());
            uri = uri.concat(modelPathComponents);

            if (this.$.suffix) {
                uri[uri.length - 1] = uri[uri.length - 1] + "." + this.$.suffix;
            }

            var params = {};

            _.defaults(params, (options || {}).params);

            if (page.$limit) {
                params.limit = page.$limit;
            }

            if (page.$offset) {
                params.offset = page.$offset;
            }

            if(options.noCache){
                params.timestamp = (new Date()).getTime();
            }
            params.fullData = options.fullData || false;

            // get queryParameter
            params = _.defaults(params, page.$collection.getQueryParameters(RestDataSource.METHOD.GET), rootCollection.$context.getQueryParameter(), this.getQueryParameter(RestDataSource.METHOD.GET));

            for(var key in params){
                if(params.hasOwnProperty(key)){
                    if(_.isObject(params[key])){
                        params[key] = JSON.stringify(params[key]);
                    }
                }
            }

            params = this._translateQueryObject(params);

            // todo: add hook
            var sortParameters = page.$collection.getSortParameters(RestDataSource.METHOD.GET);
            if(sortParameters){
                params["sort"] = JSON.stringify(sortParameters);
            }

            // create url
            var url = uri.join("/");

            var self = this;

            // send request
            this.$stage.$applicationContext.ajax(url, {
                type: RestDataSource.METHOD.GET,
                queryParameter: params
            }, function (err, xhr) {
                if (!err && (xhr.status == 200 || xhr.status == 304)) {
                    // find formatProcessor that matches the content-type
                    var contentType = xhr.getResponseHeader("Content-Type");
                    var formatProcessor = self.getFormatProcessorForContentType(contentType);

                    if (!formatProcessor) {
                        callback("No formatProcessor for content type '" + contentType + "' found", null, options);
                        return;
                    }

                    try {
                        // deserialize data with formatProcessor
                        var payload = formatProcessor.deserialize(xhr.responses.text);

                        // extract meta data
                        var metaData = self.extractListMetaData(page, payload, options);

                        if (metaData && metaData.hasOwnProperty('count')) {
                            // set itemsCount in collection for page calculation
                            page.$collection.set('$itemsCount', metaData.count);
                        }

                        // extract data from list result
                        var data = self.extractListData(page, payload, options);

                        var processor = self.getProcessorForCollection(page);

                        data = processor.parseCollection(page.getRootCollection(), data, DataSource.ACTION.LOAD, options);

                        page.add(data);

                        callback(null, page, options)

                    } catch (e) {
                        self.log(e, 'error');
                        callback(e, page, options);
                    }

                } else {
                    // TODO: better error handling
                    err = err || "wrong status code";
                    callback(err, page, options);
                }
            });
        },
        removeModel: function (model, options, callback) {
            callback = callback || function(){

            };

            // create url
            var url = this._buildUriForModel(model);

            var method = RestDataSource.METHOD.DELETE;

            // get queryParameter
            var params = _.defaults(model.$context.getQueryParameter(),
                this.getQueryParameter(method));

            this.$stage.$applicationContext.ajax(url, {
                type: method,
                queryParameter: params
            }, function (err, xhr) {
                if (!err && (xhr.status == 200 || xhr.status == 304)) {
                    callback(null,model);
                } else {
                    // TODO: better error handling
                    err = err || "wrong status code";
                    callback(err, model);
                }
            });
        },
        getFormatProcessorForContentType: function(contentType) {
            for (var i = 0; i < this.$formatProcessors.length; i++) {
                var processorEntry = this.$formatProcessors[i];
                if (processorEntry.regex.test(contentType)) {
                    return processorEntry;
                }
            }

            return null;
        }

    });

    RestDataSource.RestContext = DataSource.Context.inherit("js.data.RestDataSource.Context", {
        ctor: function(dataSource, properties, parentContext){
            this.$contextModel = properties;
            this.callBase(dataSource, properties, parentContext);
        },

        createContextCacheId: function (contextModel) {
            return contextModel.constructor.name + "_" + contextModel.$.id;
        },

        getPathComponents: function(){

            var path = [];

            if (this.$parent) {
                path = this.$parent.getPathComponents();
            } else {
                return [];
            }

            if (!this.$contextModel) {
                throw new Error("ContextModel missing for non-root-Context");
            }

            var configuration = this.$dataSource.getConfigurationForModelClass(this.$contextModel.factory);
            path.push(configuration.$.path, this.$contextModel.$.id);

            return path;

        },

        createCollection: function (factory, options, type) {
            options = options || {};
            _.defaults(options, {
                pageSize: this.$dataSource.$.collectionPageSize || 100
            });

            return this.callBase(factory, options, type);
        },

        getQueryParameter: function () {
            return {};
        }
    });

    RestDataSource.METHOD = {
        GET: 'GET',
        POST: 'POST',
        PUT: 'PUT',
        DELETE: 'DELETE'
    };

    // TODO: implement XmlProcessor
    RestDataSource.XmlFormatProcessor = DataSource.FormatProcessor.inherit("js.data.RestDataSource.XmlFormatProcessor", {
        serialize: function (data) {
            throw "not implemented";
        },
        deserialize: function (responses) {
            throw "not implemented";
        }
    });

    return RestDataSource;
});<commit_msg>added href for referenced models<commit_after>define(["js/data/DataSource", "js/core/Base", "js/data/Model", "underscore", "flow", "JSON", "js/data/Collection"], function (DataSource, Base, Model, _, flow, JSON, Collection) {

    var rIdExtractor = /http.+\/([^/]+)$/;

    var RestDataProcessor = DataSource.Processor.inherit('src.data.RestDataSource.RestDataProcessor', {
        _composeSubModel: function (model, action, options) {

            // TODO: fix href for submodels

            var ret = {
                    id: model.$.id
                },
                configuration = this.$dataSource.getConfigurationForModelClass(model.factory);

            if (options.baseUri && configuration) {
                ret.href = options.baseUri + "/" + configuration.$.path + "/" + model.$.id
            }

            return ret
        }
    });

    var RestDataSource = DataSource.inherit("js.data.RestDataSource", {

        defaults: {
            endPoint: null,
            gateway: null,
            parsePayloadOnCreate: true,
            parsePayloadOnUpdate: true
        },

        ctor: function () {
            this.callBase();
        },
        $defaultProcessorFactory: RestDataProcessor,
        initialize: function () {

            if (!this.$.endPoint) {
                this.log("No end-point for RestDataSource defined", "warn");
            }

            if (!this.$.gateway) {
                this.$.gateway = this.$.endPoint;
            }

        },

        _validateConfiguration: function () {
            if (!this.$dataSourceConfiguration) {
                throw "No DataSourceConfiguration specified";
            }
        },

        initializeFormatProcessors: function () {

            var jsonProcessor = new DataSource.JsonFormatProcessor();
            jsonProcessor.regex = /json/;

            this.$formatProcessors.push(jsonProcessor);

        },

        /***
         * creates the context as RestContext
         *
         * @param properties
         * @param parentContext
         * @return {js.core.RestDataSource.RestContext}
         */
        createContext: function (properties, parentContext) {
            return new RestDataSource.RestContext(this, properties, parentContext);
        },

        /***
         * global query parameter for each REST action
         * @action {String} Rest action [GET, PUT, DELETE, POST]
         * @return {Object}
         */
        getQueryParameter: function (action) {
            return {};
        },

        getPathComponentsForModel: function (model) {

            if (model) {
                var config = this.$dataSourceConfiguration.getConfigurationForModelClass(model.factory);
                if (config) {

                    var path = config.$.path;

                    if (path) {
                        var ret = [path];

                        if (!model.isNew()) {
                            ret.push(model.$.id);
                        }

                        return ret;
                    }
                }
            }

            return null;
        },

        _buildUriForModel: function (model) {
            // map model to url
            var modelPathComponents = this.getPathComponentsForModel(model);

            if (!modelPathComponents) {
                callback("path for model unknown", null, options);
                return;
            }

            // build uri
            var uri = [this.$.gateway];
            uri = uri.concat(model.$context.getPathComponents());
            uri = uri.concat(modelPathComponents);

            if (this.$.suffix) {
                uri[uri.length - 1] = uri[uri.length - 1] + "." + this.$.suffix;
            }

            return uri.join("/");
        },
        /**
         *
         * @param model
         * @param options
         * @param callback function(err, model, options)
         */
        loadModel: function (model, options, callback) {
            var self = this;

            // create url
            var url = this._buildUriForModel(model);

            // get queryParameter
            var params = _.defaults(model.$context.getQueryParameter(),
                this.getQueryParameter(RestDataSource.METHOD.GET));

            if (options.noCache) {
                params.timestamp = (new Date().getTime());
            }

            flow()
                .seq("xhr", function (cb) {
                    // send request
                    self.$stage.$applicationContext.ajax(url, {
                        type: RestDataSource.METHOD.GET,
                        queryParameter: params
                    }, cb);
                })
                .seq(function (cb) {
                    var xhr = this.vars.xhr;

                    if (xhr.status === 200 || xhr.status === 304) {
                        // find processor that matches the content-type
                        self._parseModelPayload(xhr, model, options);

                        cb(null, model, options);

                    } else {
                        // TODO: better error handling
                        cb("Got status code " + xhr.status + " for '" + url + "'", xhr, null, options);
                    }
                })
                .exec(function (err) {
                    if (callback) {
                        callback(err, model, options);
                    }
                });

        },

        _parseModelPayload: function (xhr, model, options) {
            var contentType = xhr.getResponseHeader("Content-Type");

            var formatProcessor = this.getFormatProcessorForContentType(contentType);

            if (!formatProcessor) {
                throw new Error("No formatProcessor for content type '" + contentType + "' found", null, options);
            }

            // deserialize data with format processor
            var data = formatProcessor.deserialize(xhr.responses.text);

            var processor = this.getProcessorForModel(model, options);

            // parse data inside processor
            data = processor.parse(model, data, DataSource.ACTION.LOAD, options);

            // parse data inside model
            data = model.parse(data);

            // set data
            model.set(data);

        },

        /***
         *
         * @param request.url
         * @param request.queryParameter
         * @param request.model
         * @param request.options
         * @param xhr
         * @param callback
         */
        handleSaveError: function (request, xhr, callback) {
            if (callback) {
                callback({
                    status: xhr.status,
                    statusText: xhr.statusText
                });
            }
        },

        /***
         *
         * @param request.url
         * @param request.queryParameter
         * @param request.model
         * @param request.options
         * @param xhr
         * @param callback
         */
        handleCreationSuccess: function (request, xhr, callback) {

            var model = request.model;

            var cb = function (err) {
                if (callback) {
                    callback(err, model, request.options);
                }
            };

            var self = this;

            try { // get location header
                var location = xhr.getResponseHeader('Location');

                if (location) {

                    if (self.$.parsePayloadOnCreate) {
                        self._parseModelPayload(xhr, model, request.options);
                    }

                    // extract id
                    var id = this.extractIdFromLocation(location, request);

                    if (id || id === 0) {
                        model.set('id', id);

                        var schema = model.schema, schemaType;
                        for (var schemaKey in schema) {
                            if (schema.hasOwnProperty(schemaKey)) {
                                schemaType = schema[schemaKey];
                                if (schemaType.classof && schemaType.classof(Collection)) {
                                    var contextForChildren = model.getContextForChild(schemaType);
                                    model.set(schemaKey, contextForChildren.createCollection(schemaType, null));
                                }

                            }
                        }

                        model.$context.addEntityToCache(model);

                        // TODO: ask processor if i should call save again to put content
                        cb(null);
                    } else {
                        cb("Id couldn't be extracted");
                    }

                } else {
                    cb('Location header not found');
                }

            } catch (e) {
                cb(e || true);
            }
        },

        /***
         *
         * @param request.url
         * @param request.queryParameter
         * @param request.model
         * @param request.options
         * @param xhr
         * @param callback
         */
        handleUpdateSuccess: function (request, xhr, callback) {

            var model = request.model;

            if (this.$.parsePayloadOnUpdate) {
                this._parseModelPayload(xhr, model, request.options);
            }

            if (callback) {
                callback(null, model, request.options);
            }
        },

        extractIdFromLocation: function (location, request) {
            var param = rIdExtractor.exec(location);

            if (param) {
                return param[1];
            }

            return null;
        },

        _saveModel: function (model, options, callback) {

            var action = DataSource.ACTION.UPDATE,
                method = RestDataSource.METHOD.PUT;

            if (model._status() === Model.STATE.NEW) {
                action = DataSource.ACTION.CREATE;
                method = RestDataSource.METHOD.POST;
            }

            var processor = this.getProcessorForModel(model, options);
            var formatProcessor = this.getFormatProcessor(action);
            var self = this;

            // call save of the processor to save sub models
            flow()
                .seq(function (cb) {
                    processor.saveSubModels(model, options, cb)
                })
                .seq(function (cb) {
                    // create url
                    var url = self._buildUriForModel(model);

                    // get queryParameter
                    var params = _.defaults(model.$context.getQueryParameter(),
                        self.getQueryParameter(method));

                    // TODO: create hook, which can modify url and queryParameter

                    var data = processor.compose(model, action, options);

                    // format payload
                    var payload = formatProcessor.serialize(data);

                    // send request
                    self.$stage.$applicationContext.ajax(url, {
                        type: method,
                        queryParameter: params,
                        data: payload,
                        contentType: formatProcessor.getContentType()
                    }, function (err, xhr) {

                        var request = {
                            url: url,
                            queryParameter: params,
                            model: model,
                            options: options
                        };

                        if (!err && action === DataSource.ACTION.CREATE && xhr.status === 201) {
                            self.handleCreationSuccess(request, xhr, cb)
                        } else if (!err && action === DataSource.ACTION.UPDATE && xhr.status === 200) {
                            self.handleUpdateSuccess(request, xhr, cb);
                        }
                        else {
                            // error handling
                            self.handleSaveError(request, xhr, cb);
                        }

                    });
                })
                .exec(function (err) {
                    callback && callback(err, model, options);
                })

        },
        extractListMetaData: function (list, payload, options) {
            return payload;
        },

        extractListData: function (list, payload, options) {
            for (var key in payload) {
                if (payload.hasOwnProperty(key)) {
                    if (_.isArray(payload[key])) {
                        return payload[key];
                    }
                }
            }
        },
        loadCollectionPage: function (page, options, callback) {

            var rootCollection = page.getRootCollection();
            var config = this.$dataSourceConfiguration.getConfigurationForModelClass(rootCollection.$modelFactory);

            if (!config) {
                throw new Error("Couldnt find path config for " + rootCollection.$modelFactory.prototype.constructor.name);
            }
            var modelPathComponents = [config.$.path];

            if (!modelPathComponents) {
                callback("path for model unknown", null, options);
                return;
            }

            // build uri
            var uri = [this.$.gateway];
            uri = uri.concat(rootCollection.$context.getPathComponents());
            uri = uri.concat(modelPathComponents);

            if (this.$.suffix) {
                uri[uri.length - 1] = uri[uri.length - 1] + "." + this.$.suffix;
            }

            var params = {};

            _.defaults(params, (options || {}).params);

            if (page.$limit) {
                params.limit = page.$limit;
            }

            if (page.$offset) {
                params.offset = page.$offset;
            }

            if (options.noCache) {
                params.timestamp = (new Date()).getTime();
            }
            params.fullData = options.fullData || false;

            // get queryParameter
            params = _.defaults(params, page.$collection.getQueryParameters(RestDataSource.METHOD.GET), rootCollection.$context.getQueryParameter(), this.getQueryParameter(RestDataSource.METHOD.GET));

            for(var key in params){
                if(params.hasOwnProperty(key)){
                    if(_.isObject(params[key])){
                        params[key] = JSON.stringify(params[key]);
                    }
                }
            }

            params = this._translateQueryObject(params);

            // todo: add hook
            var sortParameters = page.$collection.getSortParameters(RestDataSource.METHOD.GET);
            if (sortParameters) {
                params["sort"] = JSON.stringify(sortParameters);
            }

            // create url
            var url = uri.join("/");

            var self = this;

            // send request
            this.$stage.$applicationContext.ajax(url, {
                type: RestDataSource.METHOD.GET,
                queryParameter: params
            }, function (err, xhr) {
                if (!err && (xhr.status == 200 || xhr.status == 304)) {
                    // find formatProcessor that matches the content-type
                    var contentType = xhr.getResponseHeader("Content-Type");
                    var formatProcessor = self.getFormatProcessorForContentType(contentType);

                    if (!formatProcessor) {
                        callback("No formatProcessor for content type '" + contentType + "' found", null, options);
                        return;
                    }

                    try {
                        // deserialize data with formatProcessor
                        var payload = formatProcessor.deserialize(xhr.responses.text);

                        // extract meta data
                        var metaData = self.extractListMetaData(page, payload, options);

                        if (metaData && metaData.hasOwnProperty('count')) {
                            // set itemsCount in collection for page calculation
                            page.$collection.set('$itemsCount', metaData.count);
                        }

                        // extract data from list result
                        var data = self.extractListData(page, payload, options);

                        var processor = self.getProcessorForCollection(page);

                        data = processor.parseCollection(page.getRootCollection(), data, DataSource.ACTION.LOAD, options);

                        page.add(data);

                        callback(null, page, options)

                    } catch (e) {
                        self.log(e, 'error');
                        callback(e, page, options);
                    }

                } else {
                    // TODO: better error handling
                    err = err || "wrong status code";
                    callback(err, page, options);
                }
            });
        },
        removeModel: function (model, options, callback) {
            callback = callback || function () {

            };

            // create url
            var url = this._buildUriForModel(model);

            var method = RestDataSource.METHOD.DELETE;

            // get queryParameter
            var params = _.defaults(model.$context.getQueryParameter(),
                this.getQueryParameter(method));

            this.$stage.$applicationContext.ajax(url, {
                type: method,
                queryParameter: params
            }, function (err, xhr) {
                if (!err && (xhr.status == 200 || xhr.status == 304)) {
                    callback(null, model);
                } else {
                    // TODO: better error handling
                    err = err || "wrong status code";
                    callback(err, model);
                }
            });
        },
        getFormatProcessorForContentType: function (contentType) {
            for (var i = 0; i < this.$formatProcessors.length; i++) {
                var processorEntry = this.$formatProcessors[i];
                if (processorEntry.regex.test(contentType)) {
                    return processorEntry;
                }
            }

            return null;
        }

    });

    RestDataSource.RestContext = DataSource.Context.inherit("js.data.RestDataSource.Context", {
        ctor: function (dataSource, properties, parentContext) {
            this.$contextModel = properties;
            this.callBase(dataSource, properties, parentContext);
        },

        createContextCacheId: function (contextModel) {
            return contextModel.constructor.name + "_" + contextModel.$.id;
        },

        getPathComponents: function () {

            var path = [];

            if (this.$parent) {
                path = this.$parent.getPathComponents();
            } else {
                return [];
            }

            if (!this.$contextModel) {
                throw new Error("ContextModel missing for non-root-Context");
            }

            var configuration = this.$dataSource.getConfigurationForModelClass(this.$contextModel.factory);
            path.push(configuration.$.path, this.$contextModel.$.id);

            return path;

        },

        createCollection: function (factory, options, type) {
            options = options || {};
            _.defaults(options, {
                pageSize: this.$dataSource.$.collectionPageSize || 100
            });

            return this.callBase(factory, options, type);
        },

        getQueryParameter: function () {
            return {};
        }
    });

    RestDataSource.METHOD = {
        GET: 'GET',
        POST: 'POST',
        PUT: 'PUT',
        DELETE: 'DELETE'
    };

    // TODO: implement XmlProcessor
    RestDataSource.XmlFormatProcessor = DataSource.FormatProcessor.inherit("js.data.RestDataSource.XmlFormatProcessor", {
        serialize: function (data) {
            throw "not implemented";
        },
        deserialize: function (responses) {
            throw "not implemented";
        }
    });

    return RestDataSource;
});<|endoftext|>


<commit_before>// Mapnik tile formats. Each format should provide a `render()` method and a
// regex at `find` that can be used to select the format for use with a given
// tile request. `render(tile, map, callback)` takes the following arguments:
//
// - `tile` an instance of the `Tile` class representing the single tile to be
//   rendered.
// - `map` an instantiated `node-mapnik` `Map` object. Will have the map XML
//   for the given tile request already loaded.
// - `callback` callback function to call once rendering is complete.
var Buffer = require('buffer').Buffer,
    path = require('path'),
    fs = require('fs');

var Format = {
    // Select a format. Returns the renderer function of the matching format.
    // - `format` {String} format the extension of the tile request
    select: function(format) {
        for (i in Format) {
            // don't consider this function
            if (Format[i].hasOwnProperty('find')) {
                if (format.match(Format[i].find)) {
                    return Format[i].render;
                }
            }
        }
    },

    // PNG format.
    png: {
        'render': function(tile, map, callback) {
            var format = (tile.format === 'png8') ? 'png8' : 'png';
            map.render(tile.bbox, format, function(err, buffer) {
                if (err) return callback(err, null);
                callback(null, [
                    buffer,
                    {'Content-Type': 'image/png'}
                ]);
            });
        },
        'find': /png/
    },

    // PDF format. Renders PDF to disk and then reads the file back into a
    // buffer to pass to the callback. @TODO:
    // - uses `map.render_to_file()` which is a blocking call. Update to async
    //   once supported by `node-mapnik`.
    // - skip writing to disk once possible to render to string in
    //   `node-mapnik`.
    pdf: {
        'render': function(tile, map, callback) {
            var date = +new Date();
            var hash = 'pdf-' + date + '.pdf';
            var filepath = path.join('/tmp', hash);

            map.zoom_to_box(tile.bbox);
            map.render_to_file(filepath, {
                format: 'pdf'
            });
            fs.readFile(filepath, 'binary', function(err, buffer) {
                fs.unlink(filepath, function(err) {
                    callback(null, [
                        buffer,
                        {'Content-Type': 'application/pdf'}
                    ]);
                });
            });
        },
        'find': /pdf/
    },

    // JPEG format.
    jpg: {
        'render': function(tile, map, callback) {
            var format = tile.format.match(/(jpg|jpeg)[\d]{0,2}/)
                ? tile.format.replace('jpg', 'jpeg')
                : 'jpeg';
            map.render(tile.bbox, format, function(err, buffer) {
                if (err) return callback(err, null);
                callback(null, [
                    buffer, {
                        'Content-Type': 'image/jpeg'
                    }
                ]);
            });
        },
        'find': /jpg|jpeg/
    },

    // UTF8-grid format.
    grid: {
        'render': function(tile, map, callback) {
            tile.format_options = tile.format_options || {};
            tile.format_options.fields = tile.format_options.fields || [];
            map.zoom_to_box(tile.bbox);
            try {
                var resolution = 4;

                // TODO: When updating to node 0.4.x, this function should
                // return a buffer directly, but as the Buffer api changes
                // dramatically from 0.2 to 0.4, this returns JSON atm.
                var json = map._render_grid(
                    tile.format_options.layer,
                    resolution,
                    tile.format_options.key_name,
                    Boolean(tile.format_options.data),
                    tile.format_options.fields,
                    function (err, json) {
                        if (err) return callback(err, null);
                        // We need to manually stringify the JSON as we don't want
                        // UTF-8 characters to be escaped by the default behavior.
                        var keys = new Buffer(JSON.stringify(json.keys), 'utf8');
                        var grid = new Buffer(json.grid, 'utf8');

                        var length = keys.length +
                                     grid.length +
                                     '{"grid":,"keys":}'.length;

                        if (tile.format_options.data) {
                            var data = new Buffer(JSON.stringify(json.data), 'utf8');
                            length = length + data.length + ',"data":'.length;
                        }

                        var buffer = Buffer(length, 'utf8');
                        var index = buffer.write('{"grid":', 0, 'utf8');
                        grid.copy(buffer, index, 0);
                        index += grid.length;
                        index += buffer.write(',"keys":', index, 'utf8');
                        keys.copy(buffer, index, 0);
                        index += keys.length;

                        if (tile.format_options.data) {
                            index += buffer.write(',"data":', index, 'utf8');
                            data.copy(buffer, index, 0);
                            index += data.length;
                        }

                        index += buffer.write('}', index, 'utf8');
                        callback(null, [
                            buffer,
                            { 'Content-Type': 'text/javascript; charset=utf-8' },
                            { keys: json.keys }
                        ]);
                });
            } catch (err) {
                callback(err);
            }
        },
        'find': /grid\.json/
    }
};

module.exports = Format;
<commit_msg>leverage new grid_renderer support in mapnik core if node-mapnik reports that it is available - closes #21<commit_after>// Mapnik tile formats. Each format should provide a `render()` method and a
// regex at `find` that can be used to select the format for use with a given
// tile request. `render(tile, map, callback)` takes the following arguments:
//
// - `tile` an instance of the `Tile` class representing the single tile to be
//   rendered.
// - `map` an instantiated `node-mapnik` `Map` object. Will have the map XML
//   for the given tile request already loaded.
// - `callback` callback function to call once rendering is complete.
var Buffer = require('buffer').Buffer,
    path = require('path'),
    fs = require('fs');

try { var mapnik = require('mapnik'); } catch (e) {}

var Format = {
    // Select a format. Returns the renderer function of the matching format.
    // - `format` {String} format the extension of the tile request
    select: function(format) {
        for (i in Format) {
            // don't consider this function
            if (Format[i].hasOwnProperty('find')) {
                if (format.match(Format[i].find)) {
                    return Format[i].render;
                }
            }
        }
    },

    // PNG format.
    png: {
        'render': function(tile, map, callback) {
            var format = (tile.format === 'png8') ? 'png8' : 'png';
            map.render(tile.bbox, format, function(err, buffer) {
                if (err) return callback(err, null);
                callback(null, [
                    buffer,
                    {'Content-Type': 'image/png'}
                ]);
            });
        },
        'find': /png/
    },

    // PDF format. Renders PDF to disk and then reads the file back into a
    // buffer to pass to the callback. @TODO:
    // - uses `map.render_to_file()` which is a blocking call. Update to async
    //   once supported by `node-mapnik`.
    // - skip writing to disk once possible to render to string in
    //   `node-mapnik`.
    pdf: {
        'render': function(tile, map, callback) {
            var date = +new Date();
            var hash = 'pdf-' + date + '.pdf';
            var filepath = path.join('/tmp', hash);

            map.zoom_to_box(tile.bbox);
            map.render_to_file(filepath, {
                format: 'pdf'
            });
            fs.readFile(filepath, 'binary', function(err, buffer) {
                fs.unlink(filepath, function(err) {
                    callback(null, [
                        buffer,
                        {'Content-Type': 'application/pdf'}
                    ]);
                });
            });
        },
        'find': /pdf/
    },

    // JPEG format.
    jpg: {
        'render': function(tile, map, callback) {
            var format = tile.format.match(/(jpg|jpeg)[\d]{0,2}/)
                ? tile.format.replace('jpg', 'jpeg')
                : 'jpeg';
            map.render(tile.bbox, format, function(err, buffer) {
                if (err) return callback(err, null);
                callback(null, [
                    buffer, {
                        'Content-Type': 'image/jpeg'
                    }
                ]);
            });
        },
        'find': /jpg|jpeg/
    },

    // UTF8-grid format.
    grid: {
        'render': function(tile, map, callback) {
            tile.format_options = tile.format_options || {};
            tile.format_options.fields = tile.format_options.fields || [];
            map.zoom_to_box(tile.bbox);
            try {
                var resolution = 4;

                // TODO: When updating to node 0.4.x, this function should
                // return a buffer directly, but as the Buffer api changes
                // dramatically from 0.2 to 0.4, this returns JSON atm.
                var json = map._render_grid(
                    tile.format_options.layer,
                    resolution,
                    tile.format_options.key_name,
                    Boolean(tile.format_options.data),
                    tile.format_options.fields,
                    function (err, json) {
                        if (err) return callback(err, null);

                        var buffer;

                        if (mapnik.supports.grid) {
                            // assume grid is an array of strings
                            // so we can stringify directly
                            // TODO - handle escaping
                            buffer = new Buffer(JSON.stringify(json), 'utf8');
                        }
                        else
                        {
                            // We need to manually stringify the JSON as we don't want
                            // UTF-8 characters to be escaped by the default behavior.
                            var keys = new Buffer(JSON.stringify(json.keys), 'utf8');
                            var grid = new Buffer(json.grid, 'utf8');

                            var length = keys.length +
                                         grid.length +
                                         '{"grid":,"keys":}'.length;

                            if (tile.format_options.data) {
                                var data = new Buffer(JSON.stringify(json.data), 'utf8');
                                length = length + data.length + ',"data":'.length;
                            }

                            buffer = Buffer(length, 'utf8');
                            var index = buffer.write('{"grid":', 0, 'utf8');
                            grid.copy(buffer, index, 0);
                            index += grid.length;
                            index += buffer.write(',"keys":', index, 'utf8');
                            keys.copy(buffer, index, 0);
                            index += keys.length;

                            if (tile.format_options.data) {
                                index += buffer.write(',"data":', index, 'utf8');
                                data.copy(buffer, index, 0);
                                index += data.length;
                            }

                            index += buffer.write('}', index, 'utf8');
                        }

                        callback(null, [
                            buffer,
                            { 'Content-Type': 'text/javascript; charset=utf-8' },
                            { keys: json.keys }
                        ]);
                });
            } catch (err) {
                callback(err);
            }
        },
        'find': /grid\.json/
    }
};

module.exports = Format;
<|endoftext|>