<!DOCTYPE html>
<html>

<head>
  <script src="https://github.com/js-data/js-data/releases/download/3.0.0-rc.2/js-data.js"></script>
  <script src="script.js"></script>
</head>

<body>
  <h1>Open your console to see the output</h1>
</body>

</html>
console.log('Using JSData v' + JSData.version.full);

var store = new JSData.DataStore();

var BaseEntityRecordSchema = new JSData.Schema({
  $schema: 'http://json-schema.org/draft-04/schema#',
  title: 'BaseEntity',
  description: 'Schema for BaseEntity Records.',
  type: 'object',
  properties: {
    id: {
      type: 'number',
      // This is to show you that the "public properties" are not
      // actually properties, they are ES5 getter/setter functions
      // disguised as public properties.
      get: function(originalGet) {
        console.log('Calling BaseEntityRecord getter for "id"');
        return originalGet.call(this);
      },
      set: function(value, originalSet) {
        console.log('Calling BaseEntityRecord setter for "id"');
        return originalSet.call(this, value);
      }
    },
    createdBy: {
      type: 'string'
    }
  }
});

const BaseEntityRecord = JSData.Record.extend({
  toString: function(useWrapper, includeId) {
    useWrapper = typeof useWrapper === 'boolean' ? useWrapper : true;
    includeId = typeof includeId === 'boolean' ? includeId : true;
    return (useWrapper ? '{\n' : '') +
      (includeId ? 'id: ' + this.id + ',\n' : '') +
      'createdBy: ' + this.createdBy + ',' +
      (useWrapper ? '\n}' : '');
  }
});

BaseEntityRecordSchema.apply(BaseEntityRecord.prototype);

var PersonRecord = BaseEntityRecord.extend({
  get age() {
    var now = new Date();
    return (birthDate.getMonth() <= now.getMonth()) ? now.getYear() - birthDate.getYear() - 1 : now.getYear() - birthDate.getYear();
  },
  get name() {
    return this.firstName + ' ' + this.lastName;
  },
  get birthDateAsDate() {
    return new Date(this.birthDate);
  },
  say: function() {
    return this.firstName;
  },
  toString: function() {
    return 'Person Record {\n' +
      '  id: ' + this.id + ',\n' +
      '  number: ' + this.number + ',\n' +
      '  fiscalYear: ' + this.fiscalYear + ',\n' +
      '  assetLabel: ' + this.assetLabel + ',\n' +
      '  type: ' + this.type + ',\n' +
      '  category: ' + this.category + ',\n' +
      '  frequency: ' + this.frequency + ',\n' +
      '  facility: ' + this.facility + ',\n' +
      '  ' + this.constructor.__super__.prototype.toString.call(this, false, false) + ',\n' +
      '}';
  }
});

var PersonSchema = new JSData.Schema({
  $schema: 'http://json-schema.org/draft-04/schema#',
  title: 'Person',
  description: 'Schema for a Person Record.',
  type: 'object',
  properties: {
    firstName: {
      type: 'string'
    },
    lastName: {
      type: 'string'
    },
    number: {
      type: 'number'
    },
    fiscalYear: {
      type: 'number'
    },
    type: {
      type: 'string'
    },
    category: {
      type: 'string'
    },
    frequency: {
      type: 'number'
    },
    facility: {
      type: 'string'
    },
    birthDate: {
      type: 'string'
    }
  }
});

var PersonMapper = store.defineMapper('person', {
  recordClass: PersonRecord,
  applySchema: true,
  schema: PersonSchema
});

// These properties are passed to the Record constructor.
// They are added to a hidden private map accessible only
// by the instantiated record. Properties are publically
// gettable/settable via ES5 getters and setters.
var personRecord = store.createRecord('person', {
  id: 15267,
  firstName: 'John',
  lastName: 'Doe',
  number: 1234,
  fiscalYear: 1988,
  assetLabel: 'mark',
  type: 'admin',
  category: 'foo',
  frequency: 500,
  facility: 'aspen',
  birthDate: '1/1/1980',
  createdBy: 'pouncilt'
});

console.log('personRecord:', personRecord);
console.log('personRecord instanceof JSData.Record:', personRecord instanceof JSData.Record);
console.log('personRecord.toString():', personRecord.toString());
console.log('personRecord.id:', personRecord.id);
console.log('personRecord.get("id"):', personRecord.get("id"));
// You normally wouldn't call _get yourself
console.log('personRecord._get("props.id"):', personRecord._get("props.id"));
console.log('personRecord.hasOwnProperty("id"):', personRecord.hasOwnProperty('id'));
console.log('Object.getOwnPropertyDescriptor(PersonRecord.prototype, "id"):', Object.getOwnPropertyDescriptor(PersonRecord.prototype, 'id'));
console.log('personRecord.say():', personRecord.say());
console.log('personRecord.isValid():', personRecord.isValid());
console.log('personRecord.validate():', personRecord.validate());
console.log('personRecord.birthDate:', personRecord.birthDate);
console.log('personRecord.birthDateAsDate:', personRecord.birthDateAsDate);

// This invokes the ES5 setter, and validates the value
personRecord.category = 'bar';
console.log('personRecord.category:', personRecord.category);

// You can also do this:
personRecord.set('category', 'baz');
console.log('personRecord.category:', personRecord.category);

// Or this:
personRecord.set({ category: 'beep' });
console.log('personRecord.category:', personRecord.category);

// The actual category value is accessible here:
console.log('personRecord._get("props.category"):', personRecord._get('props.category'));

try {
  console.log('\npersonRecord.category = 1234;');
  // This throws an error, because JSData uses ES5 getter/setters
  // Properties are validated when you try to set them.
  personRecord.category = 1234
} catch (err) {
  console.log('error:', err.message);
  console.log('validation errors:', JSON.stringify(err.errors, null, 2));
}