<commit_before>/* global describe, it */
var chai = require('chai');
var expect = chai.expect;
var utils = require('../dist/utils');
var path  = require('path');

describe ('utils', () => {
  describe('title casing', () => {
    it ('should correctly title-case a string', () => {
      // @TODO move these into fixtures?
      var stringsIn = ['a-doctor and a horse',
        '4 horsemen of the apocalypse!',
        '52-and a half   with extra spaces',
        'YOU do not kNow w8t you r talking ab0oooot'
      ];
      var stringsExpected = ['A Doctor And A Horse',
        '4 Horsemen Of The Apocalypse!',
        '52 And A Half   With Extra Spaces',
        'You Do Not Know W8t You R Talking Ab0oooot'
      ];
      var stringsOut = stringsIn.map(outStr => utils.titleCase(outStr));
      for (var i = 0; i < stringsOut.length; i++) {
        expect(stringsOut[i]).to.equal(stringsExpected[i]);
      }
    });
  });
  describe('getFiles', () => {
    it ('should retrieve a file list from a glob', done => {
      var glob = path.join(__dirname, 'fixtures/helpers/**/*');
      utils.getFiles(glob).then(fileList => {
        expect(Array.isArray(fileList)).to.be.true;
        fileList.map(filePath => {
          // Ham-fisted test that everything is a file, not a directory
          expect(filePath).to.contain('.');
        });
        done();
      });
    });
    it ('should should respect glob options', () => {
      var glob = path.join(__dirname, 'fixtures/helpers/**/*');
      return utils.getFiles(glob, { nodir: false }).then(fileList => {
        // This fileList should contain at least one directory
        expect(Array.isArray(fileList)).to.be.true;
        expect(fileList.filter(listEntry => {
          // Make sure there is at least one entry in the Array
          // with no "."s
          return listEntry.indexOf('.') === -1;
        })).to.have.length.of.at.least(1);

      });
    });
  });
  describe('isGlob', () => {
    it ('should correctly identify valid glob patterns', () => {
      var goodGlobs = [
        path.join(__dirname, 'fixtures/helpers/**/*'),
        ['foo', 'bar', 'baz'],
        [path.join(__dirname, 'fixtures/helpers/**/*'), 'foo'],
        'just a string'
      ];
      var badGlobs = [
        5,
        { foo: 'bar', baz: 'boof'},
        ['foo', 'bar', 5],
        '',
        []
      ];
      expect(goodGlobs.every(glob => utils.isGlob(glob))).to.be.true;
      expect(badGlobs.every(glob => utils.isGlob(glob))).to.be.false;
    });
  });
  describe('keyname', () => {
    it ('should strip leading numbers by default', () => {
      var result = utils.keyname('foo/01-bar.baz');
      expect(result).not.to.contain('01-');
    });
    it ('should strip parent directories and extensions', () => {
      var result = utils.keyname('foo/01-bar.baz');
      expect(result).not.to.contain('foo');
      expect(result).not.to.contain('baz');
    });
    it ('should accept option to retain leading numbers', () => {
      var result = utils.keyname('foo/01-bar.baz', { stripNumbers: false });
      expect(result).to.contain('01-');
    });
  });
  describe('readFiles', () => {
    it ('should read files from a glob', () => {
      var glob = path.join(__dirname, 'fixtures/helpers/*.js');
      return utils.readFiles(glob).then(allFileData => {
        expect(allFileData).to.have.length.of(3);
        expect(allFileData[0]).to.have.keys('path', 'contents');
      });
    });
    it ('should run passed function over content', done => {
      var glob = path.join(__dirname, 'fixtures/helpers/*.js');
      utils.readFiles(glob, { contentFn: (content, path) => 'foo' })
        .then(allFileData => {
          expect(allFileData).to.have.length.of(3);
          expect(allFileData[0].contents).to.equal('foo');
          done();
        });
    });
    it ('should respect passed glob options', () => {
      var glob = path.join(__dirname, 'fixtures/files/*');
      // Include dotfiles
      return utils.readFiles(glob, {
        contentFn: (content, path) => 'foo',
        globOpts: { dot: true }
      })
        .then(allFileData => {
          expect(allFileData).to.have.length(2);
        });
    });
  });
  describe('readFilesKeyed', () => {
    it ('should be able to key files by keyname', done => {
      var glob = path.join(__dirname, 'fixtures/helpers/*.js');
      utils.readFilesKeyed(glob).then(allFileData => {
        expect(allFileData).to.be.an('object');
        expect(allFileData).to.contain.keys('toFraction', 'toJSON', 'toSlug');
        done();
      });
    });
    it ('should accept an option to preserve leading numbers', done => {
      var glob = path.join(__dirname, 'fixtures/data/*.yaml');
      utils.readFilesKeyed(glob, { stripNumbers: false }).then(allFileData => {
        expect(allFileData).to.be.an('object');
        done();
      });
    });
    it ('should accept a function to derive keys', done => {
      var glob = path.join(__dirname, 'fixtures/data/*.yaml');
      utils.readFilesKeyed(glob, { keyFn: (path, options) => 'foo' + path })
        .then(allFileData => {
          expect(Object.keys(allFileData)[0]).to.contain('foo');
          done();
        });
    });
    it ('should pass contentFn through to readFiles', done => {
      var glob = path.join(__dirname, 'fixtures/data/*.yaml');
      utils.readFilesKeyed(glob, {
        keyFn: (path, options) => 'foo' + path,
        contentFn: (content, path) => 'foo'
      }).then(allFileData => {
        for (var fileKey in allFileData) {
          expect(fileKey).to.contain('foo');
          expect(allFileData[fileKey]).to.equal('foo');
        }
        done();
      });
    });
  });
  describe('dirname functions', () => {
    describe('parentDirname', () => {
      it ('should derive correct parent dirname', () => {
        var file = path.join(__dirname, 'fixtures/helpers/toFraction.js');
        var parent = utils.parentDirname(file);
        expect(parent).to.equal('fixtures');
      });
    });
    describe('localDirname', () => {
      it ('should derive correct immediate dirname', () => {
        var file = path.join(__dirname, 'fixtures/helpers/toFraction.js');
        var parent = utils.localDirname(file);
        expect(parent).to.equal('helpers');
      });
    });
  });
});
<commit_msg>Refactor for promises<commit_after>/* global describe, it */
var chai = require('chai');
var expect = chai.expect;
var utils = require('../dist/utils');
var path  = require('path');

describe ('utils', () => {
  describe('title casing', () => {
    it ('should correctly title-case a string', () => {
      // @TODO move these into fixtures?
      var stringsIn = ['a-doctor and a horse',
        '4 horsemen of the apocalypse!',
        '52-and a half   with extra spaces',
        'YOU do not kNow w8t you r talking ab0oooot'
      ];
      var stringsExpected = ['A Doctor And A Horse',
        '4 Horsemen Of The Apocalypse!',
        '52 And A Half   With Extra Spaces',
        'You Do Not Know W8t You R Talking Ab0oooot'
      ];
      var stringsOut = stringsIn.map(outStr => utils.titleCase(outStr));
      for (var i = 0; i < stringsOut.length; i++) {
        expect(stringsOut[i]).to.equal(stringsExpected[i]);
      }
    });
  });
  describe('getFiles', () => {
    it ('should retrieve a file list from a glob', () => {
      var glob = path.join(__dirname, 'fixtures/helpers/**/*');
      return utils.getFiles(glob).then(fileList => {
        expect(Array.isArray(fileList)).to.be.true;
        fileList.map(filePath => {
          // Ham-fisted test that everything is a file, not a directory
          expect(filePath).to.contain('.');
        });
      });
    });
    it ('should should respect glob options', () => {
      var glob = path.join(__dirname, 'fixtures/helpers/**/*');
      return utils.getFiles(glob, { nodir: false }).then(fileList => {
        // This fileList should contain at least one directory
        expect(Array.isArray(fileList)).to.be.true;
        expect(fileList.filter(listEntry => {
          // Make sure there is at least one entry in the Array
          // with no "."s
          return listEntry.indexOf('.') === -1;
        })).to.have.length.of.at.least(1);

      });
    });
  });
  describe('isGlob', () => {
    it ('should correctly identify valid glob patterns', () => {
      var goodGlobs = [
        path.join(__dirname, 'fixtures/helpers/**/*'),
        ['foo', 'bar', 'baz'],
        [path.join(__dirname, 'fixtures/helpers/**/*'), 'foo'],
        'just a string'
      ];
      var badGlobs = [
        5,
        { foo: 'bar', baz: 'boof'},
        ['foo', 'bar', 5],
        '',
        []
      ];
      expect(goodGlobs.every(glob => utils.isGlob(glob))).to.be.true;
      expect(badGlobs.every(glob => utils.isGlob(glob))).to.be.false;
    });
  });
  describe('keyname', () => {
    it ('should strip leading numbers by default', () => {
      var result = utils.keyname('foo/01-bar.baz');
      expect(result).not.to.contain('01-');
    });
    it ('should strip parent directories and extensions', () => {
      var result = utils.keyname('foo/01-bar.baz');
      expect(result).not.to.contain('foo');
      expect(result).not.to.contain('baz');
    });
    it ('should accept option to retain leading numbers', () => {
      var result = utils.keyname('foo/01-bar.baz', { stripNumbers: false });
      expect(result).to.contain('01-');
    });
  });
  describe('readFiles', () => {
    it ('should read files from a glob', () => {
      var glob = path.join(__dirname, 'fixtures/helpers/*.js');
      return utils.readFiles(glob).then(allFileData => {
        expect(allFileData).to.have.length.of(3);
        expect(allFileData[0]).to.have.keys('path', 'contents');
      });
    });
    it ('should run passed function over content', () => {
      var glob = path.join(__dirname, 'fixtures/helpers/*.js');
      return utils.readFiles(glob, { contentFn: (content, path) => 'foo' })
        .then(allFileData => {
          expect(allFileData).to.have.length.of(3);
          expect(allFileData[0].contents).to.equal('foo');
        });
    });
    it ('should respect passed glob options', () => {
      var glob = path.join(__dirname, 'fixtures/files/*');
      // Include dotfiles
      return utils.readFiles(glob, {
        contentFn: (content, path) => 'foo',
        globOpts: { dot: true }
      })
        .then(allFileData => {
          expect(allFileData).to.have.length(2);
        });
    });
  });
  describe('readFilesKeyed', () => {
    it ('should be able to key files by keyname', () => {
      var glob = path.join(__dirname, 'fixtures/helpers/*.js');
      return utils.readFilesKeyed(glob).then(allFileData => {
        expect(allFileData).to.be.an('object');
        expect(allFileData).to.contain.keys('toFraction', 'toJSON', 'toSlug');
      });
    });
    it ('should accept an option to preserve leading numbers', () => {
      var glob = path.join(__dirname, 'fixtures/data/*.yaml');
      return utils.readFilesKeyed(glob, { stripNumbers: false })
        .then(allFileData => {
          expect(allFileData).to.be.an('object');
        });
    });
    it ('should accept a function to derive keys', () => {
      var glob = path.join(__dirname, 'fixtures/data/*.yaml');
      return utils.readFilesKeyed(glob,
        { keyFn: (path, options) => 'foo' + path })
          .then(allFileData => {
            expect(Object.keys(allFileData)[0]).to.contain('foo');
          });
    });
    it ('should pass contentFn through to readFiles', () => {
      var glob = path.join(__dirname, 'fixtures/data/*.yaml');
      return utils.readFilesKeyed(glob, {
        keyFn: (path, options) => 'foo' + path,
        contentFn: (content, path) => 'foo'
      }).then(allFileData => {
        for (var fileKey in allFileData) {
          expect(fileKey).to.contain('foo');
          expect(allFileData[fileKey]).to.equal('foo');
        }
      });
    });
  });
  describe('dirname functions', () => {
    describe('parentDirname', () => {
      it ('should derive correct parent dirname', () => {
        var file = path.join(__dirname, 'fixtures/helpers/toFraction.js');
        var parent = utils.parentDirname(file);
        expect(parent).to.equal('fixtures');
      });
    });
    describe('localDirname', () => {
      it ('should derive correct immediate dirname', () => {
        var file = path.join(__dirname, 'fixtures/helpers/toFraction.js');
        var parent = utils.localDirname(file);
        expect(parent).to.equal('helpers');
      });
    });
  });
});
<|endoftext|>


<commit_before>jQuery.webshims.register('form-message', function($, webshims, window, document, undefined, options){
	"use strict";
	var validityMessages = webshims.validityMessages;

	var implementProperties = (options.overrideMessages || options.customMessages) ? ['customValidationMessage'] : [];

	validityMessages['en'] = $.extend(true, {
		typeMismatch: {
			email: 'Please enter an email address.',
			url: 'Please enter a URL.',
			number: 'Please enter a number.',
			date: 'Please enter a date.',
			time: 'Please enter a time.',
			range: 'Invalid input.',
			"datetime-local": 'Please enter a datetime.'
		},
		rangeUnderflow: {
			defaultMessage: 'Value must be greater than or equal to {%min}.'
		},
		rangeOverflow: {
			defaultMessage: 'Value must be less than or equal to {%max}.'
		},
		stepMismatch: 'Invalid input.',
		tooLong: 'Please enter at most {%maxlength} character(s). You entered {%valueLen}.',

		patternMismatch: 'Invalid input. {%title}',
		valueMissing: {
			defaultMessage: 'Please fill out this field.',
			checkbox: 'Please check this box if you want to proceed.'
		}
	}, (validityMessages['en'] || validityMessages['en-US'] || {}));

	['select', 'radio'].forEach(function(type){
		if(typeof validityMessages['en'].valueMissing == 'object'){
			validityMessages['en'].valueMissing[type] = 'Please select an option.';
		}
	});

	['date', 'time', 'datetime-local'].forEach(function(type){
		if(typeof validityMessages['en'].rangeUnderflow == 'object'){
			validityMessages.en.rangeUnderflow[type] = 'Value must be at or after {%min}.';
		}
	});
	['date', 'time', 'datetime-local'].forEach(function(type){
		if(typeof validityMessages['en'].rangeOverflow == 'object'){
			validityMessages.en.rangeOverflow[type] = 'Value must be at or before {%max}.';
		}
	});

	validityMessages['en-US'] = validityMessages['en-US'] || validityMessages['en'];
	validityMessages[''] = validityMessages[''] || validityMessages['en-US'];

	validityMessages['de'] = $.extend(true, {
		typeMismatch: {
			email: '{%value} ist keine zulässige E-Mail-Adresse',
			url: '{%value} ist keine zulässige Webadresse',
			number: '{%value} ist keine Nummer!',
			date: '{%value} ist kein Datum',
			time: '{%value} ist keine Uhrzeit',
			range: '{%value} ist keine Nummer!',
			"datetime-local": '{%value} ist kein Datum-Uhrzeit Format.'
		},
		rangeUnderflow: {
			defaultMessage: '{%value} ist zu niedrig. {%min} ist der unterste Wert, den Sie benutzen können.'
		},
		rangeOverflow: {
			defaultMessage: '{%value} ist zu hoch. {%max} ist der oberste Wert, den Sie benutzen können.'
		},
		stepMismatch: 'Der Wert {%value} ist in diesem Feld nicht zulässig. Hier sind nur bestimmte Werte zulässig. {%title}',
		tooLong: 'Der eingegebene Text ist zu lang! Sie haben {%valueLen} Zeichen eingegeben, dabei sind {%maxlength} das Maximum.',
		patternMismatch: '{%value} hat für dieses Eingabefeld ein falsches Format! {%title}',
		valueMissing: {
			defaultMessage: 'Bitte geben Sie einen Wert ein',
			checkbox: 'Bitte aktivieren Sie das Kästchen'
		}
	}, (validityMessages['de'] || {}));

	['select', 'radio'].forEach(function(type){
		if(typeof validityMessages['de'].valueMissing == 'object'){
			validityMessages['de'].valueMissing[type] = 'Bitte wählen Sie eine Option aus';
		}
	});

	['date', 'time', 'datetime-local'].forEach(function(type){
		if(typeof validityMessages['de'].rangeUnderflow == 'object'){
			validityMessages.de.rangeUnderflow[type] = '{%value} ist zu früh. {%min} ist die früheste Zeit, die Sie benutzen können.';
		}
	});
	['date', 'time', 'datetime-local'].forEach(function(type){
		if(typeof validityMessages['de'].rangeOverflow == 'object'){
			validityMessages.de.rangeOverflow[type] = '{%value} ist zu spät. {%max} ist die späteste Zeit, die Sie benutzen können.';
		}
	});

	var currentValidationMessage =  validityMessages[''];

	webshims.createValidationMessage = function(elem, name){
		var message = currentValidationMessage[name];
		if(message && typeof message !== 'string'){
			message = message[ $.prop(elem, 'type') ] || message[ (elem.nodeName || '').toLowerCase() ] || message[ 'defaultMessage' ];
		}
		if(message){
			['value', 'min', 'max', 'title', 'maxlength', 'label'].forEach(function(attr){
				if(message.indexOf('{%'+attr) === -1){return;}
				var val = ((attr == 'label') ? $.trim($('label[for="'+ elem.id +'"]', elem.form).text()).replace(/\*$|:$/, '') : $.attr(elem, attr)) || '';
				if(name == 'patternMismatch' && attr == 'title' && !val){
					webshims.error('no title for patternMismatch provided. Always add a title attribute.');
				}
				message = message.replace('{%'+ attr +'}', val);
				if('value' == attr){
					message = message.replace('{%valueLen}', val.length);
				}
			});
		}
		return message || '';
	};

	if(webshims.bugs.validationMessage || !Modernizr.formvalidation || webshims.bugs.bustedValidity){
		implementProperties.push('validationMessage');
	}

	webshims.activeLang({
		langObj: validityMessages,
		module: 'form-core',
		callback: function(langObj){
			currentValidationMessage = langObj;
		}
	});

	implementProperties.forEach(function(messageProp){
		webshims.defineNodeNamesProperty(['fieldset', 'output', 'button'], messageProp, {
			prop: {
				value: '',
				writeable: false
			}
		});
		['input', 'select', 'textarea'].forEach(function(nodeName){
			var desc = webshims.defineNodeNameProperty(nodeName, messageProp, {
				prop: {
					get: function(){
						var elem = this;
						var message = '';
						if(!$.prop(elem, 'willValidate')){
							return message;
						}

						var validity = $.prop(elem, 'validity') || {valid: 1};

						if(validity.valid){return message;}
						message = webshims.getContentValidationMessage(elem, validity);

						if(message){return message;}

						if(validity.customError && elem.nodeName){
							message = (Modernizr.formvalidation && !webshims.bugs.bustedValidity && desc.prop._supget) ? desc.prop._supget.call(elem) : webshims.data(elem, 'customvalidationMessage');
							if(message){return message;}
						}
						$.each(validity, function(name, prop){
							if(name == 'valid' || !prop){return;}

							message = webshims.createValidationMessage(elem, name);
							if(message){
								return false;
							}
						});
						return message || '';
					},
					writeable: false
				}
			});
		});

	});
});<commit_msg>added month type to form-message/use dot notation where possible<commit_after>jQuery.webshims.register('form-message', function($, webshims, window, document, undefined, options){
	"use strict";
	var validityMessages = webshims.validityMessages;

	var implementProperties = (options.overrideMessages || options.customMessages) ? ['customValidationMessage'] : [];

	validityMessages.en = $.extend(true, {
		typeMismatch: {
			email: 'Please enter an email address.',
			url: 'Please enter a URL.',
			number: 'Please enter a number.',
			date: 'Please enter a date.',
			time: 'Please enter a time.',
			range: 'Invalid input.',
			"datetime-local": 'Please enter a datetime.'
		},
		rangeUnderflow: {
			defaultMessage: 'Value must be greater than or equal to {%min}.'
		},
		rangeOverflow: {
			defaultMessage: 'Value must be less than or equal to {%max}.'
		},
		stepMismatch: 'Invalid input.',
		tooLong: 'Please enter at most {%maxlength} character(s). You entered {%valueLen}.',
		patternMismatch: 'Invalid input. {%title}',
		valueMissing: {
			defaultMessage: 'Please fill out this field.',
			checkbox: 'Please check this box if you want to proceed.'
		}
	}, (validityMessages.en || validityMessages['en-US'] || {}));

	['select', 'radio'].forEach(function(type){
		if(typeof validityMessages['en'].valueMissing == 'object'){
			validityMessages.en.valueMissing[type] = 'Please select an option.';
		}
	});

	['date', 'time', 'datetime-local', 'month'].forEach(function(type){
		if(typeof validityMessages.en.rangeUnderflow == 'object'){
			validityMessages.en.rangeUnderflow[type] = 'Value must be at or after {%min}.';
		}
	});
	['date', 'time', 'datetime-local', 'month'].forEach(function(type){
		if(typeof validityMessages.en.rangeOverflow == 'object'){
			validityMessages.en.rangeOverflow[type] = 'Value must be at or before {%max}.';
		}
	});

	validityMessages['en-US'] = validityMessages['en-US'] || validityMessages.en;
	validityMessages[''] = validityMessages[''] || validityMessages['en-US'];

	validityMessages.de = $.extend(true, {
		typeMismatch: {
			email: '{%value} ist keine zulässige E-Mail-Adresse',
			url: '{%value} ist keine zulässige Webadresse',
			number: '{%value} ist keine Nummer!',
			date: '{%value} ist kein Datum',
			time: '{%value} ist keine Uhrzeit',
			range: '{%value} ist keine Nummer!',
			"datetime-local": '{%value} ist kein Datum-Uhrzeit Format.'
		},
		rangeUnderflow: {
			defaultMessage: '{%value} ist zu niedrig. {%min} ist der unterste Wert, den Sie benutzen können.'
		},
		rangeOverflow: {
			defaultMessage: '{%value} ist zu hoch. {%max} ist der oberste Wert, den Sie benutzen können.'
		},
		stepMismatch: 'Der Wert {%value} ist in diesem Feld nicht zulässig. Hier sind nur bestimmte Werte zulässig. {%title}',
		tooLong: 'Der eingegebene Text ist zu lang! Sie haben {%valueLen} Zeichen eingegeben, dabei sind {%maxlength} das Maximum.',
		patternMismatch: '{%value} hat für dieses Eingabefeld ein falsches Format! {%title}',
		valueMissing: {
			defaultMessage: 'Bitte geben Sie einen Wert ein',
			checkbox: 'Bitte aktivieren Sie das Kästchen'
		}
	}, (validityMessages.de || {}));

	['select', 'radio'].forEach(function(type){
		if(typeof validityMessages.de.valueMissing == 'object'){
			validityMessages.de.valueMissing[type] = 'Bitte wählen Sie eine Option aus';
		}
	});

	['date', 'time', 'datetime-local', 'month'].forEach(function(type){
		if(typeof validityMessages.de.rangeUnderflow == 'object'){
			validityMessages.de.rangeUnderflow[type] = '{%value} ist zu früh. {%min} ist die früheste Zeit, die Sie benutzen können.';
		}
	});
	['date', 'time', 'datetime-local', 'month'].forEach(function(type){
		if(typeof validityMessages.de.rangeOverflow == 'object'){
			validityMessages.de.rangeOverflow[type] = '{%value} ist zu spät. {%max} ist die späteste Zeit, die Sie benutzen können.';
		}
	});

	var currentValidationMessage =  validityMessages[''];

	webshims.createValidationMessage = function(elem, name){
		var message = currentValidationMessage[name];
		if(message && typeof message !== 'string'){
			message = message[ $.prop(elem, 'type') ] || message[ (elem.nodeName || '').toLowerCase() ] || message[ 'defaultMessage' ];
		}
		if(message){
			['value', 'min', 'max', 'title', 'maxlength', 'label'].forEach(function(attr){
				if(message.indexOf('{%'+attr) === -1){return;}
				var val = ((attr == 'label') ? $.trim($('label[for="'+ elem.id +'"]', elem.form).text()).replace(/\*$|:$/, '') : $.attr(elem, attr)) || '';
				if(name == 'patternMismatch' && attr == 'title' && !val){
					webshims.error('no title for patternMismatch provided. Always add a title attribute.');
				}
				message = message.replace('{%'+ attr +'}', val);
				if('value' == attr){
					message = message.replace('{%valueLen}', val.length);
				}
			});
		}
		return message || '';
	};

	if(webshims.bugs.validationMessage || !Modernizr.formvalidation || webshims.bugs.bustedValidity){
		implementProperties.push('validationMessage');
	}

	webshims.activeLang({
		langObj: validityMessages,
		module: 'form-core',
		callback: function(langObj){
			currentValidationMessage = langObj;
		}
	});

	implementProperties.forEach(function(messageProp){
		webshims.defineNodeNamesProperty(['fieldset', 'output', 'button'], messageProp, {
			prop: {
				value: '',
				writeable: false
			}
		});
		['input', 'select', 'textarea'].forEach(function(nodeName){
			var desc = webshims.defineNodeNameProperty(nodeName, messageProp, {
				prop: {
					get: function(){
						var elem = this;
						var message = '';
						if(!$.prop(elem, 'willValidate')){
							return message;
						}

						var validity = $.prop(elem, 'validity') || {valid: 1};

						if(validity.valid){return message;}
						message = webshims.getContentValidationMessage(elem, validity);

						if(message){return message;}

						if(validity.customError && elem.nodeName){
							message = (Modernizr.formvalidation && !webshims.bugs.bustedValidity && desc.prop._supget) ? desc.prop._supget.call(elem) : webshims.data(elem, 'customvalidationMessage');
							if(message){return message;}
						}
						$.each(validity, function(name, prop){
							if(name == 'valid' || !prop){return;}

							message = webshims.createValidationMessage(elem, name);
							if(message){
								return false;
							}
						});
						return message || '';
					},
					writeable: false
				}
			});
		});

	});
});<|endoftext|>


<commit_before>'use strict';

const _ = require('lodash');
const mongoose = require('mongoose');

const utilsModels = require('strapi-utils').models;
const utils = require('./utils');
const relations = require('./relations');
const { findComponentByGlobalId } = require('./utils/helpers');

module.exports = ({ models, target, plugin = false }, ctx) => {
  const { instance } = ctx;

  // Parse every authenticated model.
  Object.keys(models).map(model => {
    const definition = models[model];
    definition.orm = 'mongoose';
    definition.associations = [];
    definition.globalName = _.upperFirst(_.camelCase(definition.globalId));
    definition.loadedModel = {};
    // Set the default values to model settings.
    _.defaults(definition, {
      primaryKey: '_id',
      primaryKeyType: 'string',
    });

    if (!plugin) {
      global[definition.globalName] = {};
    }

    const componentAttributes = Object.keys(definition.attributes).filter(key =>
      ['component', 'dynamiczone'].includes(definition.attributes[key].type)
    );

    const scalarAttributes = Object.keys(definition.attributes).filter(key => {
      const { type } = definition.attributes[key];
      return (
        type !== undefined &&
        type !== null &&
        type !== 'component' &&
        type !== 'dynamiczone'
      );
    });

    const relationalAttributes = Object.keys(definition.attributes).filter(
      key => {
        const { type } = definition.attributes[key];
        return type === undefined;
      }
    );

    // handle component and dynamic zone attrs
    if (componentAttributes.length > 0) {
      // create join morph collection thingy
      componentAttributes.forEach(name => {
        definition.loadedModel[name] = [
          {
            kind: String,
            ref: {
              type: mongoose.Schema.Types.ObjectId,
              refPath: `${name}.kind`,
            },
          },
        ];
      });
    }

    // handle scalar attrs
    scalarAttributes.forEach(name => {
      const attr = definition.attributes[name];

      definition.loadedModel[name] = {
        ...attr,
        ...utils(instance).convertType(attr.type),
      };
    });

    // handle relational attrs
    relationalAttributes.forEach(name => {
      buildRelation({
        definition,
        model,
        instance,
        name,
        attribute: definition.attributes[name],
      });
    });

    const schema = new instance.Schema(
      _.omitBy(definition.loadedModel, ({ type }) => type === 'virtual')
    );

    // Initialize lifecycle callbacks.
    const preLifecycle = {
      validate: 'beforeCreate',
      find: 'beforeFetchAll',
      findOne: 'beforeFetch',
      findOneAndUpdate: 'beforeUpdate',
      findOneAndRemove: 'beforeDestroy',
      remove: 'beforeDestroy',
      update: 'beforeUpdate',
      updateOne: 'beforeUpdate',
      save: 'beforeSave',
    };

    const findLifecycles = [
      'find',
      'findOne',
      'findOneAndUpdate',
      'findOneAndRemove',
    ];

    /*
        Override populate path for polymorphic association.
        It allows us to make Upload.find().populate('related')
        instead of Upload.find().populate('related.item')
      */

    const morphAssociations = definition.associations.filter(
      association => association.nature.toLowerCase().indexOf('morph') !== -1
    );

    const populateFn = createOnFetchPopulateFn({
      componentAttributes,
      morphAssociations,
      definition,
    });

    findLifecycles.forEach(key => {
      schema.pre(key, populateFn);
    });

    Object.keys(preLifecycle).forEach(key => {
      const fn = preLifecycle[key];

      if (_.isFunction(target[model.toLowerCase()][fn])) {
        schema.pre(key, function() {
          return target[model.toLowerCase()][fn](this);
        });
      }
    });

    const postLifecycle = {
      validate: 'afterCreate',
      findOneAndRemove: 'afterDestroy',
      remove: 'afterDestroy',
      update: 'afterUpdate',
      updateOne: 'afterUpdate',
      find: 'afterFetchAll',
      findOne: 'afterFetch',
      save: 'afterSave',
    };

    // Mongoose doesn't allow post 'remove' event on model.
    // See https://github.com/Automattic/mongoose/issues/3054
    Object.keys(postLifecycle).forEach(key => {
      const fn = postLifecycle[key];

      if (_.isFunction(target[model.toLowerCase()][fn])) {
        schema.post(key, function(doc, next) {
          target[model.toLowerCase()]
            [fn](this, doc)
            .then(next)
            .catch(err => {
              strapi.log.error(err);
              next(err);
            });
        });
      }
    });

    // Add virtual key to provide populate and reverse populate
    _.forEach(
      _.pickBy(definition.loadedModel, model => {
        return model.type === 'virtual';
      }),
      (value, key) => {
        schema.virtual(key.replace('_v', ''), {
          ref: value.ref,
          localField: '_id',
          foreignField: value.via,
          justOne: value.justOne || false,
        });
      }
    );

    target[model].allAttributes = _.clone(definition.attributes);

    // Use provided timestamps if the elemnets in the array are string else use default.
    const timestampsOption = _.get(definition, 'options.timestamps', true);
    if (_.isArray(timestampsOption)) {
      const [
        createAtCol = 'createdAt',
        updatedAtCol = 'updatedAt',
      ] = timestampsOption;

      schema.set('timestamps', {
        createdAt: createAtCol,
        updatedAt: updatedAtCol,
      });

      target[model].allAttributes[createAtCol] = {
        type: 'timestamp',
      };
      target[model].allAttributes[updatedAtCol] = {
        type: 'timestamp',
      };
    } else if (timestampsOption === true) {
      schema.set('timestamps', true);

      _.set(definition, 'options.timestamps', ['createdAt', 'updatedAt']);

      target[model].allAttributes.createdAt = {
        type: 'timestamp',
      };
      target[model].allAttributes.updatedAt = {
        type: 'timestamp',
      };
    }
    schema.set(
      'minimize',
      _.get(definition, 'options.minimize', false) === true
    );

    const refToStrapiRef = obj => {
      const ref = obj.ref;

      let plainData = typeof ref.toJSON === 'function' ? ref.toJSON() : ref;

      if (typeof plainData !== 'object') return ref;

      return {
        __contentType: obj.kind,
        ...ref,
      };
    };

    schema.options.toObject = schema.options.toJSON = {
      virtuals: true,
      transform: function(doc, returned) {
        // Remover $numberDecimal nested property.
        Object.keys(returned)
          .filter(key => returned[key] instanceof mongoose.Types.Decimal128)
          .forEach(key => {
            // Parse to float number.
            returned[key] = parseFloat(returned[key].toString());
          });

        morphAssociations.forEach(association => {
          if (
            Array.isArray(returned[association.alias]) &&
            returned[association.alias].length > 0
          ) {
            // Reformat data by bypassing the many-to-many relationship.
            switch (association.nature) {
              case 'oneMorphToOne':
                returned[association.alias] = refToStrapiRef(
                  returned[association.alias][0]
                );

                break;

              case 'manyMorphToMany':
              case 'manyMorphToOne':
                returned[association.alias] = returned[association.alias].map(
                  obj => refToStrapiRef(obj)
                );
                break;
              default:
            }
          }
        });

        componentAttributes.forEach(name => {
          const attribute = definition.attributes[name];
          const { type } = attribute;

          if (type === 'component') {
            if (Array.isArray(returned[name])) {
              const components = returned[name].map(el => el.ref);
              // Reformat data by bypassing the many-to-many relationship.
              returned[name] =
                attribute.repeatable === true
                  ? components
                  : _.first(components) || null;
            }
          }

          if (type === 'dynamiczone') {
            const components = returned[name].map(el => {
              return {
                __component: findComponentByGlobalId(el.kind).uid,
                ...el.ref,
              };
            });

            returned[name] = components;
          }
        });
      },
    };

    // Instantiate model.
    const Model = instance.model(
      definition.globalId,
      schema,
      definition.collectionName
    );

    Model.on('index', error => {
      if (error) {
        if (error.code === 11000) {
          strapi.log.error(
            `Unique constraint fails, make sure to update your data and restart to apply the unique constraint.\n\t- ${error.message}`
          );
        } else {
          strapi.log.error(
            `An index error happened, it wasn't applied.\n\t- ${error.message}`
          );
        }
      }
    });

    if (!plugin) {
      global[definition.globalName] = Model;
    }

    // Expose ORM functions through the `target` object.
    target[model] = _.assign(Model, target[model]);

    // Push attributes to be aware of model schema.
    target[model]._attributes = definition.attributes;
    target[model].updateRelations = relations.update;
  });
};

const createOnFetchPopulateFn = ({
  morphAssociations,
  componentAttributes,
  definition,
}) => {
  return function() {
    const populatedPaths = this.getPopulatedPaths();

    morphAssociations.forEach(association => {
      const { alias, nature } = association;

      if (['oneToManyMorph', 'manyToManyMorph'].includes(nature)) {
        this.populate({
          path: alias,
          match: {
            [`${association.via}.${association.filter}`]: association.alias,
            [`${association.via}.kind`]: definition.globalId,
          },
          options: {
            sort: '-createdAt',
          },
        });
        return;
      }

      if (populatedPaths.includes(alias)) {
        _.set(this._mongooseOptions.populate, [alias, 'path'], `${alias}.ref`);
      }
    });

    if (definition.modelType === 'component') {
      definition.associations
        .filter(ast => ast.autoPopulate !== false)
        .forEach(ast => {
          this.populate({ path: ast.alias });
        });
    }

    componentAttributes.forEach(key => {
      this.populate({ path: `${key}.ref` });
    });
  };
};

const buildRelation = ({ definition, model, instance, attribute, name }) => {
  const { nature, verbose } =
    utilsModels.getNature(attribute, name, undefined, model.toLowerCase()) ||
    {};

  // Build associations key
  utilsModels.defineAssociations(
    model.toLowerCase(),
    definition,
    attribute,
    name
  );

  switch (verbose) {
    case 'hasOne': {
      const ref = attribute.plugin
        ? strapi.plugins[attribute.plugin].models[attribute.model].globalId
        : strapi.models[attribute.model].globalId;

      definition.loadedModel[name] = {
        type: instance.Schema.Types.ObjectId,
        ref,
      };
      break;
    }
    case 'hasMany': {
      const FK = _.find(definition.associations, {
        alias: name,
      });
      const ref = attribute.plugin
        ? strapi.plugins[attribute.plugin].models[attribute.collection].globalId
        : strapi.models[attribute.collection].globalId;

      if (FK) {
        definition.loadedModel[name] = {
          type: 'virtual',
          ref,
          via: FK.via,
          justOne: false,
        };

        // Set this info to be able to see if this field is a real database's field.
        attribute.isVirtual = true;
      } else {
        definition.loadedModel[name] = [
          {
            type: instance.Schema.Types.ObjectId,
            ref,
          },
        ];
      }
      break;
    }
    case 'belongsTo': {
      const FK = _.find(definition.associations, {
        alias: name,
      });
      const ref = attribute.plugin
        ? strapi.plugins[attribute.plugin].models[attribute.model].globalId
        : strapi.models[attribute.model].globalId;

      if (
        FK &&
        FK.nature !== 'oneToOne' &&
        FK.nature !== 'manyToOne' &&
        FK.nature !== 'oneWay' &&
        FK.nature !== 'oneToMorph'
      ) {
        definition.loadedModel[name] = {
          type: 'virtual',
          ref,
          via: FK.via,
          justOne: true,
        };

        // Set this info to be able to see if this field is a real database's field.
        attribute.isVirtual = true;
      } else {
        definition.loadedModel[name] = {
          type: instance.Schema.Types.ObjectId,
          ref,
        };
      }

      break;
    }
    case 'belongsToMany': {
      const targetModel = attribute.plugin
        ? strapi.plugins[attribute.plugin].models[attribute.collection]
        : strapi.models[attribute.collection];

      const ref = targetModel.globalId;

      if (nature === 'manyWay') {
        definition.loadedModel[name] = [
          {
            type: instance.Schema.Types.ObjectId,
            ref,
          },
        ];
      } else {
        const FK = _.find(definition.associations, {
          alias: name,
        });

        // One-side of the relationship has to be a virtual field to be bidirectional.
        if ((FK && _.isUndefined(FK.via)) || attribute.dominant !== true) {
          definition.loadedModel[name] = {
            type: 'virtual',
            ref,
            via: FK.via,
          };

          // Set this info to be able to see if this field is a real database's field.
          attribute.isVirtual = true;
        } else {
          definition.loadedModel[name] = [
            {
              type: instance.Schema.Types.ObjectId,
              ref,
            },
          ];
        }
      }
      break;
    }
    case 'morphOne': {
      const FK = _.find(definition.associations, {
        alias: name,
      });
      const ref = attribute.plugin
        ? strapi.plugins[attribute.plugin].models[attribute.model].globalId
        : strapi.models[attribute.model].globalId;

      definition.loadedModel[name] = {
        type: 'virtual',
        ref,
        via: `${FK.via}.ref`,
        justOne: true,
      };

      // Set this info to be able to see if this field is a real database's field.
      attribute.isVirtual = true;
      break;
    }
    case 'morphMany': {
      const FK = _.find(definition.associations, {
        alias: name,
      });
      const ref = attribute.plugin
        ? strapi.plugins[attribute.plugin].models[attribute.collection].globalId
        : strapi.models[attribute.collection].globalId;

      definition.loadedModel[name] = {
        type: 'virtual',
        ref,
        via: `${FK.via}.ref`,
      };

      // Set this info to be able to see if this field is a real database's field.
      attribute.isVirtual = true;
      break;
    }
    case 'belongsToMorph': {
      definition.loadedModel[name] = {
        kind: String,
        [attribute.filter]: String,
        ref: {
          type: instance.Schema.Types.ObjectId,
          refPath: `${name}.kind`,
        },
      };
      break;
    }
    case 'belongsToManyMorph': {
      definition.loadedModel[name] = [
        {
          kind: String,
          [attribute.filter]: String,
          ref: {
            type: instance.Schema.Types.ObjectId,
            refPath: `${name}.kind`,
          },
        },
      ];
      break;
    }
    default:
      break;
  }
};
<commit_msg>Do not overwritten morphAssoc populates<commit_after>'use strict';

const _ = require('lodash');
const mongoose = require('mongoose');

const utilsModels = require('strapi-utils').models;
const utils = require('./utils');
const relations = require('./relations');
const { findComponentByGlobalId } = require('./utils/helpers');

const isPolymorphicAssoc = assoc => {
  return assoc.nature.toLowerCase().indexOf('morph') !== -1;
};

module.exports = ({ models, target, plugin = false }, ctx) => {
  const { instance } = ctx;

  // Parse every authenticated model.
  Object.keys(models).map(model => {
    const definition = models[model];
    definition.orm = 'mongoose';
    definition.associations = [];
    definition.globalName = _.upperFirst(_.camelCase(definition.globalId));
    definition.loadedModel = {};
    // Set the default values to model settings.
    _.defaults(definition, {
      primaryKey: '_id',
      primaryKeyType: 'string',
    });

    if (!plugin) {
      global[definition.globalName] = {};
    }

    const componentAttributes = Object.keys(definition.attributes).filter(key =>
      ['component', 'dynamiczone'].includes(definition.attributes[key].type)
    );

    const scalarAttributes = Object.keys(definition.attributes).filter(key => {
      const { type } = definition.attributes[key];
      return (
        type !== undefined &&
        type !== null &&
        type !== 'component' &&
        type !== 'dynamiczone'
      );
    });

    const relationalAttributes = Object.keys(definition.attributes).filter(
      key => {
        const { type } = definition.attributes[key];
        return type === undefined;
      }
    );

    // handle component and dynamic zone attrs
    if (componentAttributes.length > 0) {
      // create join morph collection thingy
      componentAttributes.forEach(name => {
        definition.loadedModel[name] = [
          {
            kind: String,
            ref: {
              type: mongoose.Schema.Types.ObjectId,
              refPath: `${name}.kind`,
            },
          },
        ];
      });
    }

    // handle scalar attrs
    scalarAttributes.forEach(name => {
      const attr = definition.attributes[name];

      definition.loadedModel[name] = {
        ...attr,
        ...utils(instance).convertType(attr.type),
      };
    });

    // handle relational attrs
    relationalAttributes.forEach(name => {
      buildRelation({
        definition,
        model,
        instance,
        name,
        attribute: definition.attributes[name],
      });
    });

    const schema = new instance.Schema(
      _.omitBy(definition.loadedModel, ({ type }) => type === 'virtual')
    );

    // Initialize lifecycle callbacks.
    const preLifecycle = {
      validate: 'beforeCreate',
      find: 'beforeFetchAll',
      findOne: 'beforeFetch',
      findOneAndUpdate: 'beforeUpdate',
      findOneAndRemove: 'beforeDestroy',
      remove: 'beforeDestroy',
      update: 'beforeUpdate',
      updateOne: 'beforeUpdate',
      save: 'beforeSave',
    };

    const findLifecycles = [
      'find',
      'findOne',
      'findOneAndUpdate',
      'findOneAndRemove',
    ];

    /*
        Override populate path for polymorphic association.
        It allows us to make Upload.find().populate('related')
        instead of Upload.find().populate('related.item')
      */

    const morphAssociations = definition.associations.filter(
      isPolymorphicAssoc
    );

    const populateFn = createOnFetchPopulateFn({
      componentAttributes,
      morphAssociations,
      definition,
    });

    findLifecycles.forEach(key => {
      schema.pre(key, populateFn);
    });

    Object.keys(preLifecycle).forEach(key => {
      const fn = preLifecycle[key];

      if (_.isFunction(target[model.toLowerCase()][fn])) {
        schema.pre(key, function() {
          return target[model.toLowerCase()][fn](this);
        });
      }
    });

    const postLifecycle = {
      validate: 'afterCreate',
      findOneAndRemove: 'afterDestroy',
      remove: 'afterDestroy',
      update: 'afterUpdate',
      updateOne: 'afterUpdate',
      find: 'afterFetchAll',
      findOne: 'afterFetch',
      save: 'afterSave',
    };

    // Mongoose doesn't allow post 'remove' event on model.
    // See https://github.com/Automattic/mongoose/issues/3054
    Object.keys(postLifecycle).forEach(key => {
      const fn = postLifecycle[key];

      if (_.isFunction(target[model.toLowerCase()][fn])) {
        schema.post(key, function(doc, next) {
          target[model.toLowerCase()]
            [fn](this, doc)
            .then(next)
            .catch(err => {
              strapi.log.error(err);
              next(err);
            });
        });
      }
    });

    // Add virtual key to provide populate and reverse populate
    _.forEach(
      _.pickBy(definition.loadedModel, model => {
        return model.type === 'virtual';
      }),
      (value, key) => {
        schema.virtual(key.replace('_v', ''), {
          ref: value.ref,
          localField: '_id',
          foreignField: value.via,
          justOne: value.justOne || false,
        });
      }
    );

    target[model].allAttributes = _.clone(definition.attributes);

    // Use provided timestamps if the elemnets in the array are string else use default.
    const timestampsOption = _.get(definition, 'options.timestamps', true);
    if (_.isArray(timestampsOption)) {
      const [
        createAtCol = 'createdAt',
        updatedAtCol = 'updatedAt',
      ] = timestampsOption;

      schema.set('timestamps', {
        createdAt: createAtCol,
        updatedAt: updatedAtCol,
      });

      target[model].allAttributes[createAtCol] = {
        type: 'timestamp',
      };
      target[model].allAttributes[updatedAtCol] = {
        type: 'timestamp',
      };
    } else if (timestampsOption === true) {
      schema.set('timestamps', true);

      _.set(definition, 'options.timestamps', ['createdAt', 'updatedAt']);

      target[model].allAttributes.createdAt = {
        type: 'timestamp',
      };
      target[model].allAttributes.updatedAt = {
        type: 'timestamp',
      };
    }
    schema.set(
      'minimize',
      _.get(definition, 'options.minimize', false) === true
    );

    const refToStrapiRef = obj => {
      const ref = obj.ref;

      let plainData = typeof ref.toJSON === 'function' ? ref.toJSON() : ref;

      if (typeof plainData !== 'object') return ref;

      return {
        __contentType: obj.kind,
        ...ref,
      };
    };

    schema.options.toObject = schema.options.toJSON = {
      virtuals: true,
      transform: function(doc, returned) {
        // Remover $numberDecimal nested property.
        Object.keys(returned)
          .filter(key => returned[key] instanceof mongoose.Types.Decimal128)
          .forEach(key => {
            // Parse to float number.
            returned[key] = parseFloat(returned[key].toString());
          });

        morphAssociations.forEach(association => {
          if (
            Array.isArray(returned[association.alias]) &&
            returned[association.alias].length > 0
          ) {
            // Reformat data by bypassing the many-to-many relationship.
            switch (association.nature) {
              case 'oneMorphToOne':
                returned[association.alias] = refToStrapiRef(
                  returned[association.alias][0]
                );

                break;

              case 'manyMorphToMany':
              case 'manyMorphToOne':
                returned[association.alias] = returned[association.alias].map(
                  obj => refToStrapiRef(obj)
                );
                break;
              default:
            }
          }
        });

        componentAttributes.forEach(name => {
          const attribute = definition.attributes[name];
          const { type } = attribute;

          if (type === 'component') {
            if (Array.isArray(returned[name])) {
              const components = returned[name].map(el => el.ref);
              // Reformat data by bypassing the many-to-many relationship.
              returned[name] =
                attribute.repeatable === true
                  ? components
                  : _.first(components) || null;
            }
          }

          if (type === 'dynamiczone') {
            const components = returned[name].map(el => {
              return {
                __component: findComponentByGlobalId(el.kind).uid,
                ...el.ref,
              };
            });

            returned[name] = components;
          }
        });
      },
    };

    // Instantiate model.
    const Model = instance.model(
      definition.globalId,
      schema,
      definition.collectionName
    );

    Model.on('index', error => {
      if (error) {
        if (error.code === 11000) {
          strapi.log.error(
            `Unique constraint fails, make sure to update your data and restart to apply the unique constraint.\n\t- ${error.message}`
          );
        } else {
          strapi.log.error(
            `An index error happened, it wasn't applied.\n\t- ${error.message}`
          );
        }
      }
    });

    if (!plugin) {
      global[definition.globalName] = Model;
    }

    // Expose ORM functions through the `target` object.
    target[model] = _.assign(Model, target[model]);

    // Push attributes to be aware of model schema.
    target[model]._attributes = definition.attributes;
    target[model].updateRelations = relations.update;
  });
};

const createOnFetchPopulateFn = ({
  morphAssociations,
  componentAttributes,
  definition,
}) => {
  return function() {
    const populatedPaths = this.getPopulatedPaths();

    morphAssociations.forEach(association => {
      const { alias, nature } = association;

      if (['oneToManyMorph', 'manyToManyMorph'].includes(nature)) {
        this.populate({
          path: alias,
          match: {
            [`${association.via}.${association.filter}`]: association.alias,
            [`${association.via}.kind`]: definition.globalId,
          },
          options: {
            sort: '-createdAt',
          },
        });
        return;
      }

      if (populatedPaths.includes(alias)) {
        _.set(this._mongooseOptions.populate, [alias, 'path'], `${alias}.ref`);
      }
    });

    if (definition.modelType === 'component') {
      definition.associations
        .filter(assoc => !isPolymorphicAssoc(assoc))
        .filter(ast => ast.autoPopulate !== false)
        .forEach(ast => {
          this.populate({ path: ast.alias });
        });
    }

    componentAttributes.forEach(key => {
      this.populate({ path: `${key}.ref` });
    });
  };
};

const buildRelation = ({ definition, model, instance, attribute, name }) => {
  const { nature, verbose } =
    utilsModels.getNature(attribute, name, undefined, model.toLowerCase()) ||
    {};

  // Build associations key
  utilsModels.defineAssociations(
    model.toLowerCase(),
    definition,
    attribute,
    name
  );

  switch (verbose) {
    case 'hasOne': {
      const ref = attribute.plugin
        ? strapi.plugins[attribute.plugin].models[attribute.model].globalId
        : strapi.models[attribute.model].globalId;

      definition.loadedModel[name] = {
        type: instance.Schema.Types.ObjectId,
        ref,
      };
      break;
    }
    case 'hasMany': {
      const FK = _.find(definition.associations, {
        alias: name,
      });
      const ref = attribute.plugin
        ? strapi.plugins[attribute.plugin].models[attribute.collection].globalId
        : strapi.models[attribute.collection].globalId;

      if (FK) {
        definition.loadedModel[name] = {
          type: 'virtual',
          ref,
          via: FK.via,
          justOne: false,
        };

        // Set this info to be able to see if this field is a real database's field.
        attribute.isVirtual = true;
      } else {
        definition.loadedModel[name] = [
          {
            type: instance.Schema.Types.ObjectId,
            ref,
          },
        ];
      }
      break;
    }
    case 'belongsTo': {
      const FK = _.find(definition.associations, {
        alias: name,
      });
      const ref = attribute.plugin
        ? strapi.plugins[attribute.plugin].models[attribute.model].globalId
        : strapi.models[attribute.model].globalId;

      if (
        FK &&
        FK.nature !== 'oneToOne' &&
        FK.nature !== 'manyToOne' &&
        FK.nature !== 'oneWay' &&
        FK.nature !== 'oneToMorph'
      ) {
        definition.loadedModel[name] = {
          type: 'virtual',
          ref,
          via: FK.via,
          justOne: true,
        };

        // Set this info to be able to see if this field is a real database's field.
        attribute.isVirtual = true;
      } else {
        definition.loadedModel[name] = {
          type: instance.Schema.Types.ObjectId,
          ref,
        };
      }

      break;
    }
    case 'belongsToMany': {
      const targetModel = attribute.plugin
        ? strapi.plugins[attribute.plugin].models[attribute.collection]
        : strapi.models[attribute.collection];

      const ref = targetModel.globalId;

      if (nature === 'manyWay') {
        definition.loadedModel[name] = [
          {
            type: instance.Schema.Types.ObjectId,
            ref,
          },
        ];
      } else {
        const FK = _.find(definition.associations, {
          alias: name,
        });

        // One-side of the relationship has to be a virtual field to be bidirectional.
        if ((FK && _.isUndefined(FK.via)) || attribute.dominant !== true) {
          definition.loadedModel[name] = {
            type: 'virtual',
            ref,
            via: FK.via,
          };

          // Set this info to be able to see if this field is a real database's field.
          attribute.isVirtual = true;
        } else {
          definition.loadedModel[name] = [
            {
              type: instance.Schema.Types.ObjectId,
              ref,
            },
          ];
        }
      }
      break;
    }
    case 'morphOne': {
      const FK = _.find(definition.associations, {
        alias: name,
      });
      const ref = attribute.plugin
        ? strapi.plugins[attribute.plugin].models[attribute.model].globalId
        : strapi.models[attribute.model].globalId;

      definition.loadedModel[name] = {
        type: 'virtual',
        ref,
        via: `${FK.via}.ref`,
        justOne: true,
      };

      // Set this info to be able to see if this field is a real database's field.
      attribute.isVirtual = true;
      break;
    }
    case 'morphMany': {
      const FK = _.find(definition.associations, {
        alias: name,
      });
      const ref = attribute.plugin
        ? strapi.plugins[attribute.plugin].models[attribute.collection].globalId
        : strapi.models[attribute.collection].globalId;

      definition.loadedModel[name] = {
        type: 'virtual',
        ref,
        via: `${FK.via}.ref`,
      };

      // Set this info to be able to see if this field is a real database's field.
      attribute.isVirtual = true;
      break;
    }
    case 'belongsToMorph': {
      definition.loadedModel[name] = {
        kind: String,
        [attribute.filter]: String,
        ref: {
          type: instance.Schema.Types.ObjectId,
          refPath: `${name}.kind`,
        },
      };
      break;
    }
    case 'belongsToManyMorph': {
      definition.loadedModel[name] = [
        {
          kind: String,
          [attribute.filter]: String,
          ref: {
            type: instance.Schema.Types.ObjectId,
            refPath: `${name}.kind`,
          },
        },
      ];
      break;
    }
    default:
      break;
  }
};
<|endoftext|>


<commit_before>angular.module('app').factory('apiRestangular', function(Restangular, config){

    /*
     Return Config for Way API
     */
    return Restangular.withConfig(function (RestangularConfigurer) {
        RestangularConfigurer.setBaseUrl(config.apiUrl);
        RestangularProvider.setDefaultHeaders({token: config.token});
    });

});<commit_msg>Oups, my bad<commit_after>angular.module('app').factory('apiRestangular', function(Restangular, config){

    /*
     Return Config for Way API
     */
    return Restangular.withConfig(function (RestangularConfigurer) {
        RestangularConfigurer.setBaseUrl(config.apiUrl);
        RestangularConfigurer.setDefaultHeaders({token: config.token});
    });

});<|endoftext|>


<commit_before>import React, { Component } from 'react';
import PropTypes from 'prop-types';
import Iframe from 'react-iframe'

import Typography from '@material-ui/core/Typography';
import { withStyles } from '@material-ui/core/styles';

import PageHead from './page-head';
import PageBody from './page-body';

const appStyles = theme => ({
  iframe: {
    width: '100%',
    height: '-webkit-fill-available'
  }
})

class Notebook extends Component {

   render = () => {
    const {classes, fileId} = this.props;
    const baseUrl =  `${window.location.origin}/api/v1`;
    return (

        <PageHead>
          <Typography  color="inherit" gutterBottom variant="display1">
            Notebook
          </Typography>
        </PageHead>
        <PageBody>
          <Iframe className={classes.iframe} url={`${baseUrl}/notebooks/${fileId}/html`}/>
        </PageBody>

    );
  }
}

Notebook.propTypes = {
  fileId: PropTypes.string
}

Notebook.defaultProps = {

  fileId: null,
}

export default withStyles(appStyles)(Notebook)<commit_msg>Do not use webkit for height<commit_after>import React, { Component } from 'react';
import PropTypes from 'prop-types';
import Iframe from 'react-iframe'

import Typography from '@material-ui/core/Typography';

import PageHead from './page-head';
import PageBody from './page-body';

export default class Notebook extends Component {
   render = () => {
    const {fileId} = this.props;
    const baseUrl =  `${window.location.origin}/api/v1`;
    return (

        <PageHead>
          <Typography  color="inherit" gutterBottom variant="display1">
            Notebook
          </Typography>
        </PageHead>
        <PageBody>
          <Iframe id='iframe' url={`${baseUrl}/notebooks/${fileId}/html`}
          width='100%'
          onLoad={function(){
            var frame = document.getElementById('iframe');
            if (frame) {
              frame.height = frame.contentWindow.document.body.scrollHeight + 'px';
            }
          }}/>
        </PageBody>

    );
  }
}

Notebook.propTypes = {
  fileId: PropTypes.string
}

Notebook.defaultProps = {

  fileId: null,
}
<|endoftext|>


<commit_before>(function () {

    function errorBreak() {
        throw "WebGL error!";
    };

    function startCapturing(context) {
        context.captureFrame = true;
        console.log("WebGL Inspector: beginning frame capture...");
    };

    function stopCapturing(context) {
        context.captureFrame = false;
        console.log("WebGL Inspector: ending frame capture");

        context.stream.markFrame(null); // mark end

        // Fire off callback (if present)
        if (context.captureCallback) {
            context.captureCallback(context, context.stream);
        }
    };

    function frameSeparator(context) {
        context.frameNumber++;

        // Start or stop capturing
        if (context.captureFrame) {
            if (context.captureFrameEnd == context.frameNumber) {
                stopCapturing(context);
            }
        } else {
            if (context.captureFrameStart == context.frameNumber) {
                startCapturing(context);
            }
        }

        if (context.captureFrame) {
            context.stream.markFrame(context.frameNumber);
        }

        // When this timeout gets called we can be pretty sure we are done with the current frame
        setTimeout(function () {
            context.inFrame = false;
        }, 0);
    };

    function wrapFunction(context, functionName, realFunction) {
        return function () {

            // Generate stack trace
            var stack = printStackTrace();
            // ignore garbage
            stack = stack.slice(3);
            // Fix up our type
            stack[0] = stack[0].replace("[object Object].", "gl.");

            if (context.inFrame == false) {
                // First call of a new frame!
                context.inFrame = true;
                frameSeparator(context);
            }

            var resourceCapture = context.stream.resourceCaptures[functionName];
            if (resourceCapture) {
                resourceCapture(stack, arguments);
            }

            var call = null;
            if (context.captureFrame) {
                call = context.stream.recorders[functionName](stack, arguments);
            }

            var result = realFunction.apply(context.innerContext, arguments);

            // Get error state after real call - if we don't do this here, tracing/capture calls could mess things up
            var error = context.innerContext.getError();

            if (resourceCapture) {
                resourceCapture(stack, arguments, result);
            }

            if (context.captureFrame) {
                call.complete(result, error);
            }

            if (error != context.NO_ERROR) {
                context.errorMap[error] = true;

                if (context.options.breakOnError) {
                    // TODO: backtrace?
                    errorBreak();
                }
            }

            // If this is the frame separator then handle it
            if (functionName == context.options.frameSeparator) {
                frameSeparator(context);
            }

            return result;
        };
    };

    var Context = function (canvas, innerContext, options) {
        // options: {
        //     breakOnError: bool
        //     frameSeparator: 'clear'/'finish'/'flush' etc
        // }
        options = options || {
            breakOnError: false,
            frameSeparator: null
        };

        this.options = options;
        this.canvas = canvas;
        this.innerContext = innerContext;
        this.isWrapped = true;

        this.frameNumber = 0;
        this.inFrame = false;

        this.stream = null;

        // Function to call when capture completes
        this.captureCallback = null;
        // Frame range to capture (inclusive) - if inside a capture window, captureFrame == true
        this.captureFrameStart = null;
        this.captureFrameEnd = null;
        this.captureFrame = false;

        this.errorMap = {};

        // Clone all properties in context
        for (var propertyName in innerContext) {
            if (typeof innerContext[propertyName] == 'function') {
                // Functions
                this[propertyName] = wrapFunction(this, propertyName, innerContext[propertyName]);
            } else {
                // Enums/constants/etc
                this[propertyName] = innerContext[propertyName];
            }
        }

        // Rewrite getError so that it uses our version instead
        this.getError = function () {
            for (var error in this.errorMap) {
                if (this.errorMap[error]) {
                    this.errorMap[error] = false;
                    return error;
                }
            }
            return this.NO_ERROR;
        };

        this.ignoreErrors = innerContext.ignoreErrors = function () {
            while (this.getError() != this.NO_ERROR);
        };

        this.stream = new gli.Stream(this);

        // TODO: hook resize events?
    };

    Context.prototype.capture = function (callback) {
        this.captureCallback = callback;
        this.captureFrameStart = this.frameNumber + 1;
        this.captureFrameEnd = this.captureFrameStart + 1;
        this.captureFrame = false;
    };

    gli.Context = Context;
})();
<commit_msg>only capture stacks when capturing (greatly reduces perf hit)<commit_after>(function () {

    function errorBreak() {
        throw "WebGL error!";
    };

    function startCapturing(context) {
        context.captureFrame = true;
        console.log("WebGL Inspector: beginning frame capture...");
    };

    function stopCapturing(context) {
        context.captureFrame = false;
        console.log("WebGL Inspector: ending frame capture");

        context.stream.markFrame(null); // mark end

        // Fire off callback (if present)
        if (context.captureCallback) {
            context.captureCallback(context, context.stream);
        }
    };

    function frameSeparator(context) {
        context.frameNumber++;

        // Start or stop capturing
        if (context.captureFrame) {
            if (context.captureFrameEnd == context.frameNumber) {
                stopCapturing(context);
            }
        } else {
            if (context.captureFrameStart == context.frameNumber) {
                startCapturing(context);
            }
        }

        if (context.captureFrame) {
            context.stream.markFrame(context.frameNumber);
        }

        // When this timeout gets called we can be pretty sure we are done with the current frame
        setTimeout(function () {
            context.inFrame = false;
        }, 0);
    };

    function wrapFunction(context, functionName, realFunction) {
        return function () {

            function generateStack() {
                // Generate stack trace
                var stack = printStackTrace();
                // ignore garbage
                stack = stack.slice(4);
                // Fix up our type
                stack[0] = stack[0].replace("[object Object].", "gl.");
            };

            if (context.inFrame == false) {
                // First call of a new frame!
                context.inFrame = true;
                frameSeparator(context);
            }

            var resourceCapture = context.stream.resourceCaptures[functionName];
            if (resourceCapture) {
                resourceCapture(generateStack(), arguments);
            }

            var call = null;
            if (context.captureFrame) {
                call = context.stream.recorders[functionName](generateStack(), arguments);
            }

            var result = realFunction.apply(context.innerContext, arguments);

            // Get error state after real call - if we don't do this here, tracing/capture calls could mess things up
            var error = context.innerContext.getError();

            if (resourceCapture) {
                resourceCapture(generateStack(), arguments, result);
            }

            if (context.captureFrame) {
                call.complete(result, error);
            }

            if (error != context.NO_ERROR) {
                context.errorMap[error] = true;

                if (context.options.breakOnError) {
                    // TODO: backtrace?
                    errorBreak();
                }
            }

            // If this is the frame separator then handle it
            if (functionName == context.options.frameSeparator) {
                frameSeparator(context);
            }

            return result;
        };
    };

    var Context = function (canvas, innerContext, options) {
        // options: {
        //     breakOnError: bool
        //     frameSeparator: 'clear'/'finish'/'flush' etc
        // }
        options = options || {
            breakOnError: false,
            frameSeparator: null
        };

        this.options = options;
        this.canvas = canvas;
        this.innerContext = innerContext;
        this.isWrapped = true;

        this.frameNumber = 0;
        this.inFrame = false;

        this.stream = null;

        // Function to call when capture completes
        this.captureCallback = null;
        // Frame range to capture (inclusive) - if inside a capture window, captureFrame == true
        this.captureFrameStart = null;
        this.captureFrameEnd = null;
        this.captureFrame = false;

        this.errorMap = {};

        // Clone all properties in context
        for (var propertyName in innerContext) {
            if (typeof innerContext[propertyName] == 'function') {
                // Functions
                this[propertyName] = wrapFunction(this, propertyName, innerContext[propertyName]);
            } else {
                // Enums/constants/etc
                this[propertyName] = innerContext[propertyName];
            }
        }

        // Rewrite getError so that it uses our version instead
        this.getError = function () {
            for (var error in this.errorMap) {
                if (this.errorMap[error]) {
                    this.errorMap[error] = false;
                    return error;
                }
            }
            return this.NO_ERROR;
        };

        this.ignoreErrors = innerContext.ignoreErrors = function () {
            while (this.getError() != this.NO_ERROR);
        };

        this.stream = new gli.Stream(this);

        // TODO: hook resize events?
    };

    Context.prototype.capture = function (callback) {
        this.captureCallback = callback;
        this.captureFrameStart = this.frameNumber + 1;
        this.captureFrameEnd = this.captureFrameStart + 1;
        this.captureFrame = false;
    };

    gli.Context = Context;
})();
<|endoftext|>


<commit_before>'use strict';

/*!
 * Snakeskin
 * https://github.com/SnakeskinTpl/Snakeskin
 *
 * Released under the MIT license
 * https://github.com/SnakeskinTpl/Snakeskin/blob/master/LICENSE
 */

const
	$C = require('collection.js/compiled'),
	snakeskin = global.Snakeskin = require('../snakeskin');

const
	path = require('path'),
	fs = require('fs'),
	assert = require('assert'),
	glob = require('glob');

const
	from = path.join(__dirname, 'tests'),
	to = path.join(__dirname, 'tmp'),
	error = path.join(__dirname, '../error.tmp');

exports.from = from;
exports.to = to;
exports.error = error;

let prfx = -1;
exports.run = function (params) {
	const
		options = JSON.stringify(params, null, 2),
		debug = params.debug = {};

	prfx++;
	$C(glob.sync(path.join(from, '*/*.ss'))).forEach(exec);

	function exec(file) {
		let
			txt,
			fileName,
			cat,
			chunkSrc,
			relativeSrc,
			nms;

		let
			results,
			tests;

		try {
			txt = $C(
				fs
					.readFileSync(file, 'utf8')
					.replace(/^\/\*![\s\S]*?\*\//, '')
					.split(/^===.*/m)

			).map((el) => el.trim());

			fileName = path.basename(file);
			cat = path.basename(path.dirname(file));
			chunkSrc = path.join(to, `${fileName}_${prfx}.${cat}.js`);
			relativeSrc = path.relative(process.cwd(), file);
			nms = [cat, path.basename(file, '.ss')];

			console.log(`\n###### ${nms.join('.')} :: ${options}\n`);

			const
				testRgxp = /^\[\[(.*)]]===+$/gm;

			results = txt[0].split(/^\[\[.*]]===+$/m).slice(1);
			tests = [];

			while (testRgxp.exec(txt[0])) {
				tests.push(RegExp.$1);
			}

		} catch (err) {
			log(`File: ${relativeSrc || file}\nError: ${err.message}`, 'error');
			return;
		}

		const obj = {
			tpl: txt[1],
			id: path.basename(file, '.ss'),
			js: []
		};

		/* eslint-disable no-unused-vars */
		let tpl;
		/* eslint-enable no-unused-vars */

		try {
			const
				start = Date.now(),
				res = snakeskin.compile(txt[1], params, {file});

			log(`${relativeSrc} ${Date.now() - start}ms`);
			fs.writeFileSync(chunkSrc, res);
			tpl = $C(require(chunkSrc)).get(nms);

		} catch (err) {
			fs.writeFileSync(
				error,
				`File: ${file}\n\n${err.message + (debug.code ? `\n\nCode:\n\n${debug.code}` : '')}`
			);

			throw err;
		}

		$C(tests).forEach((el, i) => {
			const
				p = String(el).split(' ; ');

			let res = '';
			try {
				results[i] = (results[i] || '').trim();
				obj.js.push(
					`equal(${p[0]}(${p.slice(1)}).trim(), '${results[i].replace(/(\\|')/g, '\\$1')}');`
				);

				res = eval(`tpl["${p[0]}"](${p.slice(1)})`);
				res = res != null ? res.trim() : '';

				assert.equal(
					res,
					results[i]
				);

			} catch (err) {
				/* eslint-disable prefer-template */
				const header =
					`File: ${relativeSrc}  (${prfx})` +
					`\nOptions:\n\n${options}` +
					`\n\nTpl: ${p[0]}`;

				log(header, 'error');
				const report =
					header +
					`\n\n${line()}` +
					`\n\nResult:\n${res}` +
					`\n\nExpected:\n${results[i]}` +
					`\n\n${line()}` +
					`\n\nTest:\n${txt[1]}` +
					`\n\nCode:\n${debug['code']}`;

				/* eslint-enable prefer-template */
				fs.writeFileSync(error, report);
				throw err;
			}
		});
	}

	function log(message, type) {
		type = type || 'log';
		console[type](new Date().toString());
		console[type](message);
		console[type](line());
	}

	function line() {
		return new Array(80).join('~');
	}
};

exports.exists = function (src) {
	try {
		fs.statSync(src);
		return true;

	} catch (err) {
		if (err.code === 'ENOENT') {
			return false;
		}

		throw err;
	}
};
<commit_msg>:art:<commit_after>'use strict';

/*!
 * Snakeskin
 * https://github.com/SnakeskinTpl/Snakeskin
 *
 * Released under the MIT license
 * https://github.com/SnakeskinTpl/Snakeskin/blob/master/LICENSE
 */

const
	$C = require('collection.js/compiled'),
	snakeskin = global.Snakeskin = require('../snakeskin');

const
	path = require('path'),
	fs = require('fs'),
	assert = require('assert'),
	glob = require('glob');

const
	from = exports.from = path.join(__dirname, 'tests'),
	to = exports.to = path.join(__dirname, 'tmp'),
	error = exports.error = path.join(__dirname, '../error.tmp');

let prfx = -1;
exports.run = function (params) {
	const
		options = JSON.stringify(params, null, 2),
		debug = params.debug = {};

	prfx++;
	$C(glob.sync(path.join(from, '*/*.ss'))).forEach(exec);

	function exec(file) {
		let
			txt,
			filename,
			dirname;

		let
			chunkSrc,
			relativeSrc,
			nms;

		let
			results,
			tests;

		try {
			txt = $C(
				fs
					.readFileSync(file, 'utf8')
					.replace(/^\/\*![\s\S]*?\*\//, '')
					.split(/^===.*/m)

			).map((el) => el.trim());

			filename = path.basename(file);
			dirname = path.basename(path.dirname(file));

			chunkSrc = path.join(to, `${filename}_${prfx}.${dirname}.js`);
			relativeSrc = path.relative(process.cwd(), file);
			nms = [dirname, path.basename(file, '.ss')];

			console.log(
				`\n###### ${nms.join('.')} :: ${options}\n`
			);

			const
				testRgxp = /^\[\[(.*)]]===+$/gm;

			results = txt[0].split(/^\[\[.*]]===+$/m).slice(1);
			tests = [];

			while (testRgxp.exec(txt[0])) {
				tests.push(RegExp.$1);
			}

		} catch (err) {
			log(`File: ${relativeSrc ? relativeSrc : file}\nError: ${err.message}`, 'error');
			return;
		}

		const obj = {
			tpl: txt[1],
			id: path.basename(file, '.ss'),
			js: []
		};

		/* eslint-disable no-unused-vars */
		let tpl;
		/* eslint-enable no-unused-vars */

		try {
			const
				start = Date.now(),
				res = snakeskin.compile(txt[1], params, {file});

			log(`${relativeSrc} ${Date.now() - start}ms`);
			fs.writeFileSync(chunkSrc, res);
			tpl = $C(require(chunkSrc)).get(nms);

		} catch (err) {
			fs.writeFileSync(
				error,
				`File: ${file}\n\n${err.message + (debug.code ? `\n\nCode:\n\n${debug.code}` : '')}`
			);

			throw err;
		}

		$C(tests).forEach((el, i) => {
			const
				p = String(el).split(' ; ');

			let res = '';
			try {
				results[i] = (results[i] || '').trim();
				obj.js.push(
					`equal(${p[0]}(${p.slice(1)}).trim(), '${results[i].replace(/(\\|')/g, '\\$1')}');`
				);

				res = eval(`tpl["${p[0]}"](${p.slice(1)})`);
				res = res != null ? res.trim() : '';

				assert.equal(
					res,
					results[i]
				);

			} catch (err) {
				/* eslint-disable prefer-template */
				const header =
					`File: ${relativeSrc}  (${prfx})` +
					`\nOptions:\n\n${options}` +
					`\n\nTpl: ${p[0]}`;

				log(header, 'error');
				const report =
					header +
					`\n\n${line()}` +
					`\n\nResult:\n${res}` +
					`\n\nExpected:\n${results[i]}` +
					`\n\n${line()}` +
					`\n\nTest:\n${txt[1]}` +
					`\n\nCode:\n${debug['code']}`;

				/* eslint-enable prefer-template */
				fs.writeFileSync(error, report);
				throw err;
			}
		});
	}

	function log(message, type) {
		type = type || 'log';
		console[type](new Date().toString());
		console[type](message);
		console[type](line());
	}

	function line() {
		return new Array(80).join('~');
	}
};

exports.exists = function (src) {
	try {
		fs.statSync(src);
		return true;

	} catch (err) {
		if (err.code === 'ENOENT') {
			return false;
		}

		throw err;
	}
};
<|endoftext|>