<commit_before>/*
 * ---
 *
 * description: Implements Upload functionality into the FileManager based on [FancyUpload](http://digitarald.de)
 *
 * authors: Christoph Pojer (@cpojer)
 *
 * license: MIT-style license.
 *
 * requires: [Core/*]
 *
 * provides: Filemanager.Uploader
 *
 * ...
 */

FileManager.implement({

	options: {
		resizeImages: true,
		upload: true,
		uploadAuthData: {}            // deprecated; use FileManager.propagateData instead!
	},

	hooks: {
		show: {
			upload: function() {
				this.startUpload();
			}
		},

		cleanup: {
			upload: function(){
				if (!this.options.upload  || !this.upload) return;

				if (this.upload.uploader) this.upload.uploader.set('opacity', 0).dispose();
			}
		}
	},

	onDialogOpenWhenUpload: function(){
		if (this.swf && this.swf.box) this.swf.box.setStyle('visibility', 'hidden');
	},

	onDialogCloseWhenUpload: function(){
		if (this.swf && this.swf.box) this.swf.box.setStyle('visibility', 'visible');
	},

	startUpload: function(){

		if (!this.options.upload || this.swf) return;

		var self = this;
		this.upload = {
			button: this.addMenuButton('upload').inject(this.menu, 'bottom').addEvents({
				click: function(){
					return false;
				},
				mouseenter: function(){
					this.addClass('hover');
				},
				mouseleave: function(){
					this.removeClass('hover');
					this.blur();
				},
				mousedown: function(){
					this.focus();
				}
			}),
			list: new Element('ul', {'class': 'filemanager-uploader-list'}),
			uploader: new Element('div', {opacity: 0, 'class': 'filemanager-uploader-area'}).adopt(
				new Element('h2', {text: this.language.upload}),
				new Element('div', {'class': 'filemanager-uploader'})
			)
		};
		this.upload.uploader.getElement('div').adopt(this.upload.list);

		if (this.options.resizeImages){
			var resizer = new Element('div', {'class': 'checkbox'}),
				check = (function(){
			this.toggleClass('checkboxChecked');
		}).bind(resizer);
			check();
			this.upload.label = new Element('label').adopt(
				resizer, new Element('span', {text: this.language.resizeImages})
			).addEvent('click', check).inject(this.menu);
		}

		var File = new Class({

			Extends: Swiff.Uploader.File,

			initialize: function(base, data){

				this.parent(base, data);

				//if (typeof console !== 'undefined' && console.log) console.log('Uploader: setOptions');
				this.setOptions({
					//data: Object.merge({}, base.options.data, self.options.uploadAuthData),
					url: self.options.url + (self.options.url.indexOf('?') == -1 ? '?' : '&') + Object.toQueryString(Object.merge({}, (self.options.propagateType == 'GET' ? self.options.propagateData : {}), {
						event: 'upload',
						directory: self.normalize(self.Directory),
						filter: self.options.filter,
						resize: self.options.resizeImages && resizer.hasClass('checkboxChecked') ? 1 : 0
					}))
				});
			},

			render: function(){
				if (this.invalid){
					var message = self.language.uploader.unknown, sub = {
						name: this.name,
						size: Swiff.Uploader.formatUnit(this.size, 'b')
					};

					if (self.language.uploader[this.validationError])
						message = self.language.uploader[this.validationError];

					if (this.validationError == 'sizeLimitMin')
						sub.size_min = Swiff.Uploader.formatUnit(this.base.options.fileSizeMin, 'b');
					else if (this.validationError == 'sizeLimitMax')
						sub.size_max = Swiff.Uploader.formatUnit(this.base.options.fileSizeMax, 'b');

					new FileManager.Dialog(new Element('div', {html: message.substitute(sub, /\\?\$\{([^{}]+)\}/g)}) , {language: {confirm: self.language.ok}, buttons: ['confirm']});
					return this;
				}

				this.addEvents({
					open: this.onOpen,
					remove: this.onRemove,
					requeue: this.onRequeue,
					progress: this.onProgress,
					stop: this.onStop,
					complete: this.onComplete
				});

				this.ui = {};
				this.ui.icon = new Asset.image(self.assetBasePath+'Images/Icons/' + this.extension + '.png', {
					'class': 'icon',
					onerror: function(){ new Asset.image(self.assetBasePath + 'Images/Icons/default.png').replaces(this); }
				});
				this.ui.element = new Element('li', {'class': 'file', id: 'file-' + this.id});
				this.ui.title = new Element('span', {'class': 'file-title', text: this.name});
				this.ui.size = new Element('span', {'class': 'file-size', text: Swiff.Uploader.formatUnit(this.size, 'b')});

				var file = this;
				this.ui.cancel = new Asset.image(self.assetBasePath+'Images/cancel.png', {'class': 'file-cancel', title: self.language.cancel}).addEvent('click', function(){
					file.remove();
					self.tips.hide();
					self.tips.detach(this);
				});
				self.tips.attach(this.ui.cancel);

				var progress = new Element('img', {'class': 'file-progress', src: self.assetBasePath+'Images/bar.gif'});

				this.ui.element.adopt(
					this.ui.cancel,
					progress,
					this.ui.icon,
					this.ui.title,
					this.ui.size
				).inject(self.upload.list).highlight();

				this.ui.progress = new Fx.ProgressBar(progress).set(0);

				this.base.reposition();

				return this.parent();
			},

			onOpen: function(){
				this.ui.element.addClass('file-running');
			},

			onRemove: function(){
				this.ui = this.ui.element.destroy();
			},

			onProgress: function(){
				this.ui.progress.start(this.progress.percentLoaded);
			},

			onStop: function(){
				this.remove();
			},

			onComplete: function(file_obj) {
				this.ui.progress = this.ui.progress.cancel().element.destroy();
				this.ui.cancel = this.ui.cancel.destroy();

				try
				{
					var response = JSON.decode(this.response.text);
				}
				catch(e)
				{
					if (typeof console !== 'undefined' && console.log) console.log(this.response);
				}

				if (!response)
				{
					new FileManager.Dialog(self.language.uploader.mod_security, {language: {confirm: self.language.ok}, buttons: ['confirm']});
				}

				if (!response.status)
					new FileManager.Dialog(('' + response.error).substitute(self.language, /\\?\$\{([^{}]+)\}/g) , {language: {confirm: self.language.ok}, buttons: ['confirm']});

				this.ui.element.set('tween', {duration: 2000}).highlight(response.status ? '#e6efc2' : '#f0c2c2');
				(function(){
					this.ui.element.setStyle('overflow', 'hidden').morph({
						opacity: 0,
						height: 0
					}).get('morph').chain(function(){
						this.element.destroy();
						if (!self.upload.list.getElements('li').length)
							self.upload.uploader.fade(0).get('tween').chain(function(){
								self.upload.uploader.setStyle('display', 'none');
								self.onShow = true;
								self.load(self.Directory, self._lastFileUploaded);
								// self.fillInfo();
							});
					});
				}).delay(response.status ? 1000 : 5000, this);
			}
		});

		this.getFileTypes = function() {
			var fileTypes = {};
			if (this.options.filter == 'image')
				fileTypes = {'Images (*.jpg, *.gif, *.png)': '*.jpg; *.jpeg; *.bmp; *.gif; *.png'};
			if (this.options.filter == 'video')
				fileTypes = {'Videos (*.avi, *.flv, *.mov, *.mpeg, *.mpg, *.wmv, *.mp4)': '*.avi; *.flv; *.fli; *.movie; *.mpe; *.qt; *.viv; *.mkv; *.vivo; *.mov; *.mpeg; *.mpg; *.wmv; *.mp4'};
			if (this.options.filter == 'audio')
				fileTypes = {'Audio (*.aif, *.mid, *.mp3, *.mpga, *.rm, *.wav)': '*.aif; *.aifc; *.aiff; *.aif; *.au; *.mka; *.kar; *.mid; *.midi; *.mp2; *.mp3; *.mpga; *.ra; *.ram; *.rm; *.rpm; *.snd; *.wav; *.tsi'};
			if (this.options.filter == 'text')
				fileTypes = {'Text (*.txt, *.rtf, *.rtx, *.html, *.htm, *.css, *.as, *.xml, *.tpl)': '*.txt; *.rtf; *.rtx; *.html; *.htm; *.css; *.as; *.xml; *.tpl'};
			if (this.options.filter == 'application')
				fileTypes = {'Application (*.bin, *.doc, *.exe, *.iso, *.js, *.odt, *.pdf, *.php, *.ppt, *.swf, *.rar, *.zip)': '*.ai; *.bin; *.ccad; *.class; *.cpt; *.dir; *.dms; *.drw; *.doc; *.dvi; *.dwg; *.eps; *.exe; *.gtar; *.gz; *.js; *.latex; *.lnk; *.lnk; *.oda; *.odt; *.ods; *.odp; *.odg; *.odc; *.odf; *.odb; *.odi; *.odm; *.ott; *.ots; *.otp; *.otg; *.pdf; *.php; *.pot; *.pps; *.ppt; *.ppz; *.pre; *.ps; *.rar; *.set; *.sh; *.skd; *.skm; *.smi; *.smil; *.spl; *.src; *.stl; *.swf; *.tar; *.tex; *.texi; *.texinfo; *.tsp; *.unv; *.vcd; *.vda; *.xlc; *.xll; *.xlm; *.xls; *.xlw; *.zip'};

			return fileTypes;
		};

		//if (typeof console !== 'undefined' && console.log) console.log('Uploader: SWF init');
		this.swf = new Swiff.Uploader({
			id: 'SwiffFileManagerUpload',
			path: this.assetBasePath + 'Swiff.Uploader.swf',
			queued: false,
			target: this.upload.button,
			allowDuplicates: true,
			instantStart: true,
			appendCookieData: true, // pass along any session cookie data, etc. in the request section (PHP: $_GET[])
			data: Object.merge({},
			//(self.options.propagateData  || {}),
			(self.options.uploadAuthData || (self.options.propagateType == 'POST' ? self.options.propagateData : {}))
			),
			fileClass: File,
			timeLimit: 260,
			fileSizeMax: 2600 * 2600 * 25,
			typeFilter: this.getFileTypes(),
			zIndex: this.SwiffZIndex || 9999,
			onSelectSuccess: function(){
				self.fillInfo();
				self.info.getElement('h2.filemanager-headline').setStyle('display', 'none');
				self.info.adopt(self.upload.uploader.setStyle('display', 'block'));
				self.upload.uploader.fade(1);
			},
			onComplete: function(){

			},
			onFileComplete: function(f){
				self._lastFileUploaded = f.name;
			},
			onFail: function(error) {
				if (error != 'empty') {
					$$(self.upload.button, self.upload.label).dispose();
					new FileManager.Dialog(new Element('div', {html: self.language.flash[error] || self.language.flash.flash}), {language: {confirm: self.language.ok}, buttons: ['confirm']});
				}
			}
		});
	}
});

<commit_msg>uploadAuthData is a legacy item; has been marked as deprecated. Fixed the treatment of propagateData: propagateData and uploadAuthData CO-EXIST, so existence of propagateData or uploadAuthData doesn't mean the other shouldn't be included in the request!<commit_after>/*
 * ---
 *
 * description: Implements Upload functionality into the FileManager based on [FancyUpload](http://digitarald.de)
 *
 * authors: Christoph Pojer (@cpojer)
 *
 * license: MIT-style license.
 *
 * requires: [Core/*]
 *
 * provides: Filemanager.Uploader
 *
 * ...
 */

FileManager.implement({

	options: {
		resizeImages: true,
		upload: true,
		uploadAuthData: {}            // deprecated; use FileManager.propagateData instead!
	},

	hooks: {
		show: {
			upload: function() {
				this.startUpload();
			}
		},

		cleanup: {
			upload: function(){
				if (!this.options.upload  || !this.upload) return;

				if (this.upload.uploader) this.upload.uploader.set('opacity', 0).dispose();
			}
		}
	},

	onDialogOpenWhenUpload: function(){
		if (this.swf && this.swf.box) this.swf.box.setStyle('visibility', 'hidden');
	},

	onDialogCloseWhenUpload: function(){
		if (this.swf && this.swf.box) this.swf.box.setStyle('visibility', 'visible');
	},

	startUpload: function(){

		if (!this.options.upload || this.swf) return;

		var self = this;
		this.upload = {
			button: this.addMenuButton('upload').inject(this.menu, 'bottom').addEvents({
				click: function(){
					return false;
				},
				mouseenter: function(){
					this.addClass('hover');
				},
				mouseleave: function(){
					this.removeClass('hover');
					this.blur();
				},
				mousedown: function(){
					this.focus();
				}
			}),
			list: new Element('ul', {'class': 'filemanager-uploader-list'}),
			uploader: new Element('div', {opacity: 0, 'class': 'filemanager-uploader-area'}).adopt(
				new Element('h2', {text: this.language.upload}),
				new Element('div', {'class': 'filemanager-uploader'})
			)
		};
		this.upload.uploader.getElement('div').adopt(this.upload.list);

		if (this.options.resizeImages){
			var resizer = new Element('div', {'class': 'checkbox'}),
				check = (function(){
			this.toggleClass('checkboxChecked');
		}).bind(resizer);
			check();
			this.upload.label = new Element('label').adopt(
				resizer, new Element('span', {text: this.language.resizeImages})
			).addEvent('click', check).inject(this.menu);
		}

		var File = new Class({

			Extends: Swiff.Uploader.File,

			initialize: function(base, data){

				this.parent(base, data);

				//if (typeof console !== 'undefined' && console.log) console.log('Uploader: setOptions');
				this.setOptions({
					//data: Object.merge({}, base.options.data, self.options.uploadAuthData),
					url: self.options.url + (self.options.url.indexOf('?') == -1 ? '?' : '&') + Object.toQueryString(Object.merge({}, (self.options.propagateType == 'GET' ? self.options.propagateData : {}), {
						event: 'upload',
						directory: self.normalize(self.Directory),
						filter: self.options.filter,
						resize: self.options.resizeImages && resizer.hasClass('checkboxChecked') ? 1 : 0
					}))
				});
			},

			render: function(){
				if (this.invalid){
					var message = self.language.uploader.unknown, sub = {
						name: this.name,
						size: Swiff.Uploader.formatUnit(this.size, 'b')
					};

					if (self.language.uploader[this.validationError])
						message = self.language.uploader[this.validationError];

					if (this.validationError == 'sizeLimitMin')
						sub.size_min = Swiff.Uploader.formatUnit(this.base.options.fileSizeMin, 'b');
					else if (this.validationError == 'sizeLimitMax')
						sub.size_max = Swiff.Uploader.formatUnit(this.base.options.fileSizeMax, 'b');

					new FileManager.Dialog(new Element('div', {html: message.substitute(sub, /\\?\$\{([^{}]+)\}/g)}) , {language: {confirm: self.language.ok}, buttons: ['confirm']});
					return this;
				}

				this.addEvents({
					open: this.onOpen,
					remove: this.onRemove,
					requeue: this.onRequeue,
					progress: this.onProgress,
					stop: this.onStop,
					complete: this.onComplete
				});

				this.ui = {};
				this.ui.icon = new Asset.image(self.assetBasePath+'Images/Icons/' + this.extension + '.png', {
					'class': 'icon',
					onerror: function(){ new Asset.image(self.assetBasePath + 'Images/Icons/default.png').replaces(this); }
				});
				this.ui.element = new Element('li', {'class': 'file', id: 'file-' + this.id});
				this.ui.title = new Element('span', {'class': 'file-title', text: this.name});
				this.ui.size = new Element('span', {'class': 'file-size', text: Swiff.Uploader.formatUnit(this.size, 'b')});

				var file = this;
				this.ui.cancel = new Asset.image(self.assetBasePath+'Images/cancel.png', {'class': 'file-cancel', title: self.language.cancel}).addEvent('click', function(){
					file.remove();
					self.tips.hide();
					self.tips.detach(this);
				});
				self.tips.attach(this.ui.cancel);

				var progress = new Element('img', {'class': 'file-progress', src: self.assetBasePath+'Images/bar.gif'});

				this.ui.element.adopt(
					this.ui.cancel,
					progress,
					this.ui.icon,
					this.ui.title,
					this.ui.size
				).inject(self.upload.list).highlight();

				this.ui.progress = new Fx.ProgressBar(progress).set(0);

				this.base.reposition();

				return this.parent();
			},

			onOpen: function(){
				this.ui.element.addClass('file-running');
			},

			onRemove: function(){
				this.ui = this.ui.element.destroy();
			},

			onProgress: function(){
				this.ui.progress.start(this.progress.percentLoaded);
			},

			onStop: function(){
				this.remove();
			},

			onComplete: function(file_obj) {
				this.ui.progress = this.ui.progress.cancel().element.destroy();
				this.ui.cancel = this.ui.cancel.destroy();

				try
				{
					var response = JSON.decode(this.response.text);
				}
				catch(e)
				{
					if (typeof console !== 'undefined' && console.log) console.log(this.response);
				}

				if (!response)
				{
					new FileManager.Dialog(self.language.uploader.mod_security, {language: {confirm: self.language.ok}, buttons: ['confirm']});
				}

				if (!response.status)
					new FileManager.Dialog(('' + response.error).substitute(self.language, /\\?\$\{([^{}]+)\}/g) , {language: {confirm: self.language.ok}, buttons: ['confirm']});

				this.ui.element.set('tween', {duration: 2000}).highlight(response.status ? '#e6efc2' : '#f0c2c2');
				(function(){
					this.ui.element.setStyle('overflow', 'hidden').morph({
						opacity: 0,
						height: 0
					}).get('morph').chain(function(){
						this.element.destroy();
						if (!self.upload.list.getElements('li').length)
							self.upload.uploader.fade(0).get('tween').chain(function(){
								self.upload.uploader.setStyle('display', 'none');
								self.onShow = true;
								self.load(self.Directory, self._lastFileUploaded);
								// self.fillInfo();
							});
					});
				}).delay(response.status ? 1000 : 5000, this);
			}
		});

		this.getFileTypes = function() {
			var fileTypes = {};
			if (this.options.filter == 'image')
				fileTypes = {'Images (*.jpg, *.gif, *.png)': '*.jpg; *.jpeg; *.bmp; *.gif; *.png'};
			if (this.options.filter == 'video')
				fileTypes = {'Videos (*.avi, *.flv, *.mov, *.mpeg, *.mpg, *.wmv, *.mp4)': '*.avi; *.flv; *.fli; *.movie; *.mpe; *.qt; *.viv; *.mkv; *.vivo; *.mov; *.mpeg; *.mpg; *.wmv; *.mp4'};
			if (this.options.filter == 'audio')
				fileTypes = {'Audio (*.aif, *.mid, *.mp3, *.mpga, *.rm, *.wav)': '*.aif; *.aifc; *.aiff; *.aif; *.au; *.mka; *.kar; *.mid; *.midi; *.mp2; *.mp3; *.mpga; *.ra; *.ram; *.rm; *.rpm; *.snd; *.wav; *.tsi'};
			if (this.options.filter == 'text')
				fileTypes = {'Text (*.txt, *.rtf, *.rtx, *.html, *.htm, *.css, *.as, *.xml, *.tpl)': '*.txt; *.rtf; *.rtx; *.html; *.htm; *.css; *.as; *.xml; *.tpl'};
			if (this.options.filter == 'application')
				fileTypes = {'Application (*.bin, *.doc, *.exe, *.iso, *.js, *.odt, *.pdf, *.php, *.ppt, *.swf, *.rar, *.zip)': '*.ai; *.bin; *.ccad; *.class; *.cpt; *.dir; *.dms; *.drw; *.doc; *.dvi; *.dwg; *.eps; *.exe; *.gtar; *.gz; *.js; *.latex; *.lnk; *.lnk; *.oda; *.odt; *.ods; *.odp; *.odg; *.odc; *.odf; *.odb; *.odi; *.odm; *.ott; *.ots; *.otp; *.otg; *.pdf; *.php; *.pot; *.pps; *.ppt; *.ppz; *.pre; *.ps; *.rar; *.set; *.sh; *.skd; *.skm; *.smi; *.smil; *.spl; *.src; *.stl; *.swf; *.tar; *.tex; *.texi; *.texinfo; *.tsp; *.unv; *.vcd; *.vda; *.xlc; *.xll; *.xlm; *.xls; *.xlw; *.zip'};

			return fileTypes;
		};

		//if (typeof console !== 'undefined' && console.log) console.log('Uploader: SWF init');
		this.swf = new Swiff.Uploader({
			id: 'SwiffFileManagerUpload',
			path: this.assetBasePath + 'Swiff.Uploader.swf',
			queued: false,
			target: this.upload.button,
			allowDuplicates: true,
			instantStart: true,
			appendCookieData: true, // pass along any session cookie data, etc. in the request section (PHP: $_GET[])
			data: Object.merge({},
				(self.options.propagateType == 'POST' ? self.options.propagateData : {}),
				(self.options.uploadAuthData || {})
			),
			fileClass: File,
			timeLimit: 260,
			fileSizeMax: 2600 * 2600 * 25,
			typeFilter: this.getFileTypes(),
			zIndex: this.SwiffZIndex || 9999,
			onSelectSuccess: function(){
				self.fillInfo();
				self.info.getElement('h2.filemanager-headline').setStyle('display', 'none');
				self.info.adopt(self.upload.uploader.setStyle('display', 'block'));
				self.upload.uploader.fade(1);
			},
			onComplete: function(){

			},
			onFileComplete: function(f){
				self._lastFileUploaded = f.name;
			},
			onFail: function(error) {
				if (error != 'empty') {
					$$(self.upload.button, self.upload.label).dispose();
					new FileManager.Dialog(new Element('div', {html: self.language.flash[error] || self.language.flash.flash}), {language: {confirm: self.language.ok}, buttons: ['confirm']});
				}
			}
		});
	}
});

<|endoftext|>


<commit_before>'use strict';
/**
 * @file Retrieve files from the account
 */

var gapis = require('googleapis');
var request = require('request');

/**
 * Get the google API object
 *
 * @param {Object} tokens What you already got when it comes to tokens
 * @param {Object} tokens The app's config (app id + secret !)
 * @param {Function} cb First the google api object, then the google auth object
 */
var getApi = function(tokens, config, cb) {
  var auth = new gapis.OAuth2Client(config.gdrive_id, config.gdrive_secret, config.gdrive_connect);
  gapis.discover('drive', 'v2').execute(function(err, client) {
    if(err) {
      return cb(err);
    }

    auth.credentials = {
      access_token: tokens.access_token,
      refresh_token: tokens.refresh_token
    };
    cb(client.drive, auth);
  });
};

/**
 * Use the Google refresh token to get a new accessToken,
 *
 * @param {String} refreshToken Refresh token issued by Google
 * @param {Function} cb First parameter is the error (if any), then the new accessToken (valid for one hour)
 */
var refreshAccessToken = function(refreshToken, config, cb) {

  // See https://developers.google.com/accounts/docs/OAuth2WebServer#refresh for details
  var params = {
    url: 'https://accounts.google.com/o/oauth2/token',
    form: {
      'refresh_token': refreshToken,
      'client_id': config.google_drive_id,
      'client_secret': config.google_drive_secret,
      'grant_type': 'refresh_token'
    },
    json: true
  };

  request.post(params, function (err, res) {
    if(err){
      return cb(err);
    }

    if(res.statusCode === 401){
      return cb(new Error("Access to this refresh_token has been revoked."));
    }
    if(res.statusCode !== 200) {
      return cb(new Error("Google Error: " + JSON.stringify(res.body, null, ' ')));
    }

    cb(null, res.body);
  });
};

/**
 * Process a list of changes (used as a callback)
 *
 * @param {Error} err A possible error rose during precedent execution
 * @param {Array} items An array of items of changes
 * @param {Boolean} end Is this the last delta you want to send ?
 * @param {String} startChangeId The last change processed before the new call
 * @param {Function} cb The callback
 */
var processDelta = function(err, items, end, startChangeId, cb) {
  if(err) {
    return cb(err);
  }
  var fileMap = {};
  for(var i = 0; i < items.length; i += 1) {
    var item = items[i];
    var file = item.file;
    if(file) {
      file.deleted = false;
      fileMap[item.fileId] = file;
    } else if(startChangeId) {
      fileMap[item.fileId] = {
        id: item.fileId,
        deleted: true
      };
    }
    startChangeId = file.id;
  }
  var files = [];
  for(var id in fileMap) {
    files.push(fileMap[id]);
    fileMap[id] = null;
  }
  if(items.length > 0) {
    if(end) {
      cb(null, files, startChangeId);
    } else {
      cb(null, files);
    }
  } else {
    cb(null, [], startChangeId);
  }
};

/**
 * Retrieve recursively pages of changes
 *
 * @param {GoogleApi.DriveApi} drive The drive API
 * @param {GoogleApi.Auth} auth The auth Object
 * @param {GoogleApi.Request} request The last request done (or false)
 * @param {String} startChangeId The last change processed before the new call
 * @param {Function} cb Callback, when every page is passed
 */
var retrievePageOfChanges = function(drive, auth, options, request, startChangeId, cb) {
  if(!request) {
    request = drive.changes.list(options).withAuthClient(auth);
  }
  var results = [];
  request.execute(function(err, resp) {
    if(err) {
      return cb(err);
    }
    results = resp.items;
    var nextPageToken = resp.nextPageToken;
    if (nextPageToken) {
      request = drive.changes.list({
        'pageToken': nextPageToken
      }).withAuthClient(auth);
      processDelta(null, results, false, startChangeId, cb);
      retrievePageOfChanges(drive, auth, options, request, startChangeId, cb);
    } else {
      processDelta(null, results, true, startChangeId, cb);
    }
  });
};

/**
 * Retrieve all files associated with this user account,
 *
 * @param {Object} tokens OAuth Tokens to identify the account
 * @param {String} cursor Last cursor
 * @param {Function} cb Callback. First parameter is the error (if any), then the files metadatas, then the new cursor.
 */
module.exports = function(refreshToken, startChangeId, config, cb) {
  refreshAccessToken(refreshToken, config, function(err, tokens) {
    if(err) {
      return cb(err);
    }
    getApi(tokens, config, function(drive, auth) {
      var options = { maxResults: 1000 };
      if(startChangeId) {
        options.startChangeId = startChangeId;
      }
      retrievePageOfChanges(drive, auth, options, false, startChangeId, function(err, files, lastChangeId) {
        if(err) {
          return cb(err);
        }
        for(var i = 0; i < files.length; i ++) {
          files[i].authTokens = tokens;
        }
        cb(null, files, lastChangeId);
      });
    });
  });
};
<commit_msg>Simplification ...<commit_after>'use strict';
/**
 * @file Retrieve files from the account
 */

var gapis = require('googleapis');
var request = require('request');

/**
 * Get the google API object
 *
 * @param {Object} tokens What you already got when it comes to tokens
 * @param {Object} tokens The app's config (app id + secret !)
 * @param {Function} cb First the google api object, then the google auth object
 */
var getApi = function(tokens, config, cb) {
  var auth = new gapis.OAuth2Client(config.gdrive_id, config.gdrive_secret, config.gdrive_connect);
  gapis.discover('drive', 'v2').execute(function(err, client) {
    if(err) {
      return cb(err);
    }

    auth.credentials = {
      access_token: tokens.access_token,
      refresh_token: tokens.refresh_token
    };
    cb(client.drive, auth);
  });
};

/**
 * Use the Google refresh token to get a new accessToken,
 *
 * @param {String} refreshToken Refresh token issued by Google
 * @param {Function} cb First parameter is the error (if any), then the new accessToken (valid for one hour)
 */
var refreshAccessToken = function(refreshToken, config, cb) {

  // See https://developers.google.com/accounts/docs/OAuth2WebServer#refresh for details
  var params = {
    url: 'https://accounts.google.com/o/oauth2/token',
    form: {
      'refresh_token': refreshToken,
      'client_id': config.google_drive_id,
      'client_secret': config.google_drive_secret,
      'grant_type': 'refresh_token'
    },
    json: true
  };

  request.post(params, function (err, res) {
    if(err){
      return cb(err);
    }

    if(res.statusCode === 401){
      return cb(new Error("Access to this refresh_token has been revoked."));
    }
    if(res.statusCode !== 200) {
      return cb(new Error("Google Error: " + JSON.stringify(res.body, null, ' ')));
    }

    cb(null, res.body);
  });
};

/**
 * Process a list of changes (used as a callback)
 *
 * @param {Error} err A possible error rose during precedent execution
 * @param {Array} items An array of items of changes
 * @param {Boolean} end Is this the last delta you want to send ?
 * @param {String} startChangeId The last change processed before the new call
 * @param {Function} cb The callback
 */
var processDelta = function(err, items, end, startChangeId, cb) {
  if(err) {
    return cb(err);
  }
  var fileMap = {};
  for(var i = 0; i < items.length; i += 1) {
    var item = items[i];
    var file = item.file;
    if(file) {
      file.deleted = false;
      fileMap[item.fileId] = file;
    } else if(startChangeId) {
      fileMap[item.fileId] = {
        id: item.fileId,
        deleted: true
      };
    }
    startChangeId = file.id;
  }
  var files = [];
  for(var id in fileMap) {
    files.push(fileMap[id]);
    fileMap[id] = null;
  }
  if(end) {
    cb(null, files, startChangeId);
  } else {
    cb(null, files);
  }
};

/**
 * Retrieve recursively pages of changes
 *
 * @param {GoogleApi.DriveApi} drive The drive API
 * @param {GoogleApi.Auth} auth The auth Object
 * @param {GoogleApi.Request} request The last request done (or false)
 * @param {String} startChangeId The last change processed before the new call
 * @param {Function} cb Callback, when every page is passed
 */
var retrievePageOfChanges = function(drive, auth, options, request, startChangeId, cb) {
  if(!request) {
    request = drive.changes.list(options).withAuthClient(auth);
  }
  var results = [];
  request.execute(function(err, resp) {
    if(err) {
      return cb(err);
    }
    results = resp.items;
    var nextPageToken = resp.nextPageToken;
    if (nextPageToken) {
      request = drive.changes.list({
        'pageToken': nextPageToken
      }).withAuthClient(auth);
      processDelta(null, results, false, startChangeId, cb);
      retrievePageOfChanges(drive, auth, options, request, startChangeId, cb);
    } else {
      processDelta(null, results, true, startChangeId, cb);
    }
  });
};

/**
 * Retrieve all files associated with this user account,
 *
 * @param {Object} tokens OAuth Tokens to identify the account
 * @param {String} cursor Last cursor
 * @param {Function} cb Callback. First parameter is the error (if any), then the files metadatas, then the new cursor.
 */
module.exports = function(refreshToken, startChangeId, config, cb) {
  refreshAccessToken(refreshToken, config, function(err, tokens) {
    if(err) {
      return cb(err);
    }
    getApi(tokens, config, function(drive, auth) {
      var options = { maxResults: 1000 };
      if(startChangeId) {
        options.startChangeId = startChangeId;
      }
      retrievePageOfChanges(drive, auth, options, false, startChangeId, function(err, files, lastChangeId) {
        if(err) {
          return cb(err);
        }
        for(var i = 0; i < files.length; i ++) {
          files[i].authTokens = tokens;
        }
        cb(null, files, lastChangeId);
      });
    });
  });
};
<|endoftext|>


<commit_before>import { visit, currentURL } from '@ember/test-helpers';
import { setupApplicationTest } from 'ember-qunit';
import { module, test } from 'qunit';

import { setupPolly } from 'skylines/tests/test-helpers/setup-polly';

module('Acceptance | page-not-found', function (hooks) {
  setupApplicationTest(hooks);
  setupPolly(hooks, { recordIfMissing: false });

  hooks.beforeEach(async function () {
    await visit('/foobar');
  });

  test('will keep the URL at /foobar', function (assert) {
    assert.equal(currentURL(), '/foobar');
  });

  test('will show "Page not found" error message', function (assert) {
    assert.dom('.page-header').containsText('Page not found');
  });
});
<commit_msg>tests/acceptance/page-not-found: Use Mirage.js instead of Polly.js<commit_after>import { visit, currentURL } from '@ember/test-helpers';
import { setupApplicationTest } from 'ember-qunit';
import { module, test } from 'qunit';

import { setupMirage } from 'ember-cli-mirage/test-support';

module('Acceptance | page-not-found', function (hooks) {
  setupApplicationTest(hooks);
  setupMirage(hooks);

  hooks.beforeEach(async function () {
    await visit('/foobar');
  });

  test('will keep the URL at /foobar', function (assert) {
    assert.equal(currentURL(), '/foobar');
  });

  test('will show "Page not found" error message', function (assert) {
    assert.dom('.page-header').containsText('Page not found');
  });
});
<|endoftext|>


<commit_before>import { map, merge, iterate, clone, contains } from './index';
import uri from 'valid-url';

/**
 * This function _dereferences_ a schema set into one logical schema in
 * accordance with the IETF JSON Reference Draft v3 specification:
 *
 * https://tools.ietf.org/html/draft-pbryan-zyp-json-ref-03
 *
 * It allows for one to inject a schema resolution function in order to provide
 * it with referenced schema. This allows developers to be flexible in choosing
 * there schema sources such as HTTP or in-memory resolvers.
 *
 * The derefernce function also resolves JSON Pointers according to the IETF
 * RFC6901 specification:
 *
 * https://tools.ietf.org/html/rfc6901
 *
 * It does so internaly rather than through an external resolver in order to
 * remain efficient.
 *
 * @param {Array|Object} schema A schema or array of schema to dereference.
 * @param {Object} resolve A function to resolve referenced schema by its id.
 * @return {Object} The dereferenced schema as an object.
 */
const dereference = (schema, resolve = null) => {
  // If schema is an array we dereference each schema and then merge them from
  // right-to-left.
  if (Array.isArray(schema)) {
    // first validate our arguments assumption!
    schema.forEach((s) => {
      if (typeof s !== 'object' && !Array.isArray(s))
        throw new TypeError(`expect typeof object got: ${typeof s}`);
    });

    // then dereference each schema in the array before eventually merging them
    // from right to left using a reducer function.
    return schema
      .map((s) => dereference(s, resolve))
      .reduce((accumulator, value) => merge(accumulator, value, true), {});
  }
  // If schema is not an array of json objects we expect a singlular json schema
  // be provided
  else if (typeof schema === 'object' && !Array.isArray(schema)) {
    // traverse is an internal recursive function that we bind to this lexical
    // scope in order to easily resolve to schema definitons whilst traversing
    // an objects nested properties. This is primarily for efficiency concerns.
    const traverse = (node) => {
      let resolution = {};

      if (typeof node !== 'object') return node;

      // if only one argument is provided and it is an array we must recursively
      // dereference it's individual values
      if (Array.isArray(node)) {
        return node.map((v) => traverse(v));
      }

      // if we are here, the first argument is not an array or value and we expect
      // it to be a json schema.
      iterate(node, (key, value) => {
        // Skip the following properties
        if (key === 'definitions') return;

        // If value is not an array, object, or JSON schema reference we can
        // dereference it immediately. 'typeof array' equals 'object' in JS.
        if (typeof value !== 'object' && key !== '$ref') {
          resolution[key] = value;
        }
        // If we have a schema reference we must fetch it, dereference it, then merge
        // it into the base schema object.
        else if (key === '$ref') {
          // We have two types of references - definitions which are defined
          // within the current schema and external schema references which we
          // have to query AJV for as such we must fetch the schema for the
          // reference appropriately.
          let reference = null;

          // de-reference a json uri
          if (uri.isUri(value)) {
            if (!resolve)
              throw new TypeError(
                'resolver function is required to dereference a json uri.')
;
            reference = resolve(value);

            if (!reference) throw new Error(`unable to resolve URI reference: ${value}`);

            resolution = merge(
              resolution,
              dereference(reference, resolve),
              true
            );
          }
          // de-reference a json pointer
          else if (
            value.indexOf('#') === 0 ||
            value.indexOf('/') === 0 ||
            value === ''
          ) {
            const fragments = value.split('/');

            reference = fragments.reduce((acc, token) => {
              // when root document pointer return accumulator
              if (token === '#' || token === '/' || token === '')
                return acc;

              // decode token according to spec
              const refToken = token.replace('~1', '/').replace('~0', '~');

              let refValue = null;

              // if current accumulator is array we must dereference the array
              // index
              if (Array.isArray(acc)) {
                const index = parseInt(token, 10);

                if (!acc.indexOf(index))
                  throw new Error(`could not dereference array index ${value}`);

                refValue = acc[index];
              }
              // otherwise we expect an object, validate reference token
              else {
                if (!contains(acc, refToken))
                  throw new Error(`could not dereference pointer ${value}`);

                refValue = acc[refToken];
              }

              return refValue;
            }, schema);

            resolution = merge(
              resolution,
              traverse(reference),
              true
            );
          }
          else {
            throw new Error(
              `could not dereference value as a json pointer or uri: ${value}`);
          }

          if (!reference)
            throw new ReferenceError(`could not find a reference to ${value}`);
        }
        // Otherwise the value is an array or object and we need to traverse it
        // and dereference it's properties.
        else {
          resolution[key] = traverse(value);
        }
      });

      return resolution;
    };

    return traverse(schema);
  }
  // if any other combination of arguments is provided we throw
  else {
    throw new TypeError(`expected first parameter to be object or array: ${schema}`);
  }
};

export default dereference;
<commit_msg>fix spelling mistake<commit_after>import { map, merge, iterate, clone, contains } from './index';
import uri from 'valid-url';

/**
 * This function _dereferences_ a schema set into one logical schema in
 * accordance with the IETF JSON Reference Draft v3 specification:
 *
 * https://tools.ietf.org/html/draft-pbryan-zyp-json-ref-03
 *
 * It allows for one to inject a schema resolution function in order to provide
 * it with referenced schema. This allows developers to be flexible in choosing
 * there schema sources such as HTTP or in-memory resolvers.
 *
 * The dereference function also resolves JSON Pointers according to the IETF
 * RFC6901 specification:
 *
 * https://tools.ietf.org/html/rfc6901
 *
 * It does so internaly rather than through an external resolver in order to
 * remain efficient.
 *
 * @param {Array|Object} schema A schema or array of schema to dereference.
 * @param {Object} resolve A function to resolve referenced schema by its id.
 * @return {Object} The dereferenced schema as an object.
 */
const dereference = (schema, resolve = null) => {
  // If schema is an array we dereference each schema and then merge them from
  // right-to-left.
  if (Array.isArray(schema)) {
    // first validate our arguments assumption!
    schema.forEach((s) => {
      if (typeof s !== 'object' && !Array.isArray(s))
        throw new TypeError(`expect typeof object got: ${typeof s}`);
    });

    // then dereference each schema in the array before eventually merging them
    // from right to left using a reducer function.
    return schema
      .map((s) => dereference(s, resolve))
      .reduce((accumulator, value) => merge(accumulator, value, true), {});
  }
  // If schema is not an array of json objects we expect a singlular json schema
  // be provided
  else if (typeof schema === 'object' && !Array.isArray(schema)) {
    // traverse is an internal recursive function that we bind to this lexical
    // scope in order to easily resolve to schema definitons whilst traversing
    // an objects nested properties. This is primarily for efficiency concerns.
    const traverse = (node) => {
      let resolution = {};

      if (typeof node !== 'object') return node;

      // if only one argument is provided and it is an array we must recursively
      // dereference it's individual values
      if (Array.isArray(node)) {
        return node.map((v) => traverse(v));
      }

      // if we are here, the first argument is not an array or value and we expect
      // it to be a json schema.
      iterate(node, (key, value) => {
        // Skip the following properties
        if (key === 'definitions') return;

        // If value is not an array, object, or JSON schema reference we can
        // dereference it immediately. 'typeof array' equals 'object' in JS.
        if (typeof value !== 'object' && key !== '$ref') {
          resolution[key] = value;
        }
        // If we have a schema reference we must fetch it, dereference it, then merge
        // it into the base schema object.
        else if (key === '$ref') {
          // We have two types of references - definitions which are defined
          // within the current schema and external schema references which we
          // have to query AJV for as such we must fetch the schema for the
          // reference appropriately.
          let reference = null;

          // de-reference a json uri
          if (uri.isUri(value)) {
            if (!resolve)
              throw new TypeError(
                'resolver function is required to dereference a json uri.')
;
            reference = resolve(value);

            if (!reference) throw new Error(`unable to resolve URI reference: ${value}`);

            resolution = merge(
              resolution,
              dereference(reference, resolve),
              true
            );
          }
          // de-reference a json pointer
          else if (
            value.indexOf('#') === 0 ||
            value.indexOf('/') === 0 ||
            value === ''
          ) {
            const fragments = value.split('/');

            reference = fragments.reduce((acc, token) => {
              // when root document pointer return accumulator
              if (token === '#' || token === '/' || token === '')
                return acc;

              // decode token according to spec
              const refToken = token.replace('~1', '/').replace('~0', '~');

              let refValue = null;

              // if current accumulator is array we must dereference the array
              // index
              if (Array.isArray(acc)) {
                const index = parseInt(token, 10);

                if (!acc.indexOf(index))
                  throw new Error(`could not dereference array index ${value}`);

                refValue = acc[index];
              }
              // otherwise we expect an object, validate reference token
              else {
                if (!contains(acc, refToken))
                  throw new Error(`could not dereference pointer ${value}`);

                refValue = acc[refToken];
              }

              return refValue;
            }, schema);

            resolution = merge(
              resolution,
              traverse(reference),
              true
            );
          }
          else {
            throw new Error(
              `could not dereference value as a json pointer or uri: ${value}`);
          }

          if (!reference)
            throw new ReferenceError(`could not find a reference to ${value}`);
        }
        // Otherwise the value is an array or object and we need to traverse it
        // and dereference it's properties.
        else {
          resolution[key] = traverse(value);
        }
      });

      return resolution;
    };

    return traverse(schema);
  }
  // if any other combination of arguments is provided we throw
  else {
    throw new TypeError(`expected first parameter to be object or array: ${schema}`);
  }
};

export default dereference;
<|endoftext|>


<commit_before>'use strict';

define(["app"], function(app) {

app.factory("ArrayUtils", ['Visibility', '$rootScope', function($visibility, $rootScope) {

  var insertedMsgInProgress = false;
  var msgToBeInserted = [];

  return {

    exist: function(array, object, field) {
      return this.indexOf(array, object, field) > -1;
    },

    indexOf: function(array, object, field) {
      var index = -1;
      if(array !== undefined) {
        if(field  !== undefined) {
          return this.indexOfWith(array, object, function(inArray, data) {
            return inArray[field] == data[field];
          });
        } else {
          return this.indexOfWith(array, object, function(inArray, data) {
            return inArray == data;
          });
        }
      }
      return index;
    },

    existWith: function(array, object, cond) {
      return this.indexOfWith(array, object, cond) > -1;
    },

    indexOfWith: function(array, object, cond) {
      var index = -1;
      if(array !== undefined && cond !== undefined) {
        for (var i = 0; i < array.length; i++) {
          if (cond(array[i], object) == true) {
            return i;
          }
        }
      }
      return index;
    },

    random: function(array) {
      return array[Math.floor(Math.random() * (array.length + 1))];
    },

    sortMsg: function(sortMe, newMsg) {
      if(insertedMsgInProgress == false) {
        insertedMsgInProgress = true;
        var index = -1;
        var isOldMsg = false;
        newMsg.dateAgo = moment(moment(Number(newMsg.createdAt)), "YYYYMMDD").fromNow();
        for(var i=0, len=sortMe.length; i<len; ++i ) {
          if(sortMe[i].createdAt < newMsg.createdAt && index == -1) {
            index = i;
          }
          if(sortMe[i].idProvider == newMsg.idProvider ||
            (sortMe[i].authorName == newMsg.authorName && sortMe[i].message == newMsg.message)) {
            isOldMsg = true;
            //update old msg
            if(newMsg.stared !== undefined) {
              sortMe[i].stared = newMsg.stared;
            }
            sortMe[i].iStared = newMsg.iStared;
            sortMe[i].styleRefresh = "";
          }
          //refresh time
          sortMe[i].dateAgo = moment(moment(Number(sortMe[i].createdAt)), "YYYYMMDD")
                                 .lang($rootScope.currentLanguage || 'en')
                                 .fromNow();
        }
        //insert data
        if(index > -1 && isOldMsg == false) {
          sortMe.splice(index, 0, newMsg);
          $visibility.notifyNewMessage();
        } else if(isOldMsg == false) {
          sortMe.push(newMsg);
          $visibility.notifyNewMessage();
        }
        //recursion powa
        if(msgToBeInserted.length == 0) {
          insertedMsgInProgress = false;
        } else {
          var toBeInserted = msgToBeInserted.shift();
          this.sortMsg(toBeInserted.tab, toBeInserted.msg);
        }
      } else {
        msgToBeInserted.push({
          tab: sortMe,
          msg: newMsg
        })
      }
    }

  };

}]);

return app;
});<commit_msg>fix bug : sometimes random array return bad index<commit_after>'use strict';

define(["app"], function(app) {

app.factory("ArrayUtils", ['Visibility', '$rootScope', function($visibility, $rootScope) {

  var insertedMsgInProgress = false;
  var msgToBeInserted = [];

  return {

    exist: function(array, object, field) {
      return this.indexOf(array, object, field) > -1;
    },

    indexOf: function(array, object, field) {
      var index = -1;
      if(array !== undefined) {
        if(field  !== undefined) {
          return this.indexOfWith(array, object, function(inArray, data) {
            return inArray[field] == data[field];
          });
        } else {
          return this.indexOfWith(array, object, function(inArray, data) {
            return inArray == data;
          });
        }
      }
      return index;
    },

    existWith: function(array, object, cond) {
      return this.indexOfWith(array, object, cond) > -1;
    },

    indexOfWith: function(array, object, cond) {
      var index = -1;
      if(array !== undefined && cond !== undefined) {
        for (var i = 0; i < array.length; i++) {
          if (cond(array[i], object) == true) {
            return i;
          }
        }
      }
      return index;
    },

    random: function(array) {
      var index = Math.floor(Math.random() * (array.length + 1));
      if(index > array.length - 1) {
        index = 0;
      }
      return array[index];
    },

    sortMsg: function(sortMe, newMsg) {
      if(insertedMsgInProgress == false) {
        insertedMsgInProgress = true;
        var index = -1;
        var isOldMsg = false;
        newMsg.dateAgo = moment(moment(Number(newMsg.createdAt)), "YYYYMMDD").fromNow();
        for(var i=0, len=sortMe.length; i<len; ++i ) {
          if(sortMe[i].createdAt < newMsg.createdAt && index == -1) {
            index = i;
          }
          if(sortMe[i].idProvider == newMsg.idProvider ||
            (sortMe[i].authorName == newMsg.authorName && sortMe[i].message == newMsg.message)) {
            isOldMsg = true;
            //update old msg
            if(newMsg.stared !== undefined) {
              sortMe[i].stared = newMsg.stared;
            }
            sortMe[i].iStared = newMsg.iStared;
            sortMe[i].styleRefresh = "";
          }
          //refresh time
          sortMe[i].dateAgo = moment(moment(Number(sortMe[i].createdAt)), "YYYYMMDD")
                                 .lang($rootScope.currentLanguage || 'en')
                                 .fromNow();
        }
        //insert data
        if(index > -1 && isOldMsg == false) {
          sortMe.splice(index, 0, newMsg);
          $visibility.notifyNewMessage();
        } else if(isOldMsg == false) {
          sortMe.push(newMsg);
          $visibility.notifyNewMessage();
        }
        //recursion powa
        if(msgToBeInserted.length == 0) {
          insertedMsgInProgress = false;
        } else {
          var toBeInserted = msgToBeInserted.shift();
          this.sortMsg(toBeInserted.tab, toBeInserted.msg);
        }
      } else {
        msgToBeInserted.push({
          tab: sortMe,
          msg: newMsg
        })
      }
    }

  };

}]);

return app;
});<|endoftext|>


<commit_before>var actionpack = function() {
    var isArray = function(obj) {
        return (obj && typeof obj === 'object' && obj.constructor == Array) ||
            false;
    }
    return({
        fnArr: [],
        addFn: function(fn, argArr, thisVal, isAddToStart) {
            var len;
            //Add nothing if it didn't receive a function
            if (! (typeof fn === "function")) return false;
            if (!isAddToStart) {
                this.fnArr.push({ func: fn });
                len = this.fnArr.length - 1;
            } else {
                this.fnArr.unshift({ func: fn });
                len = 0;
            };
            if (typeof argArr !== "undefined") this.fnArr[len].args = argArr;
            this.fnArr[len].thisVal = thisVal || fn;
            return this.fnArr;
        },

        runAll: function(isKeep) {
            this.fnArr.forEach(function(fn){
                fn.thisVal = fn.thisVal || fn.func;
                if (!fn.args) {
                    fn['func'].call(fn.thisVal);
                } else if (isArray(fn.args)) {
                    fn['func'].apply(fn.thisVal, fn.args);
                    return;
                } else {
                    fn['func'].call(fn.thisVal, fn.args);
                }
                return;
            });
            if (!isKeep) this.fnArr = [];
        },

        wipeAll: function() {
            this.fnArr = [];
        },

    });
};
<commit_msg>running fn sequence returns resultant vals in array<commit_after>var actionpack = function() {
    var isArray = function(obj) {
        return (obj && typeof obj === 'object' && obj.constructor == Array) ||
            false;
    }
    return({

        fnArr: [],

        addFn: function(fn, argArr, thisVal, isAddToStart) {
            var len;
            //Add nothing if it didn't receive a function
            if (! (typeof fn === "function")) return false;
            if (!isAddToStart) {
                this.fnArr.push({ func: fn });
                len = this.fnArr.length - 1;
            } else {
                this.fnArr.unshift({ func: fn });
                len = 0;
            };
            if (typeof argArr !== "undefined") this.fnArr[len].args = argArr;
            this.fnArr[len].thisVal = thisVal || fn;
            return this.fnArr;
        },

        runAll: function(isKeep) {
            var retArr = [];
            this.fnArr.forEach(function(fn){
                var retVal = "";
                fn.thisVal = fn.thisVal || fn.func;
                if (!fn.args) {
                    retVal = fn['func'].call(fn.thisVal) || "";
                } else if (isArray(fn.args)) {
                    retVal = fn['func'].apply(fn.thisVal, fn.args) || "";
                } else {
                    retVal = fn['func'].call(fn.thisVal, fn.args) || "";
                }
                retArr.push(retVal);
                return;
            });
            if (!isKeep) this.fnArr = [];
            return retArr;
        },

        wipeAll: function() {
            this.fnArr = [];
        },

    });
};
<|endoftext|>


<commit_before>import { initModule } from '../../helpers/test-config';
import { test } from 'qunit';

export default function() {
  initModule('render/components.js');

  test('Components are rendered in the correct place', t => {
    const Component = Ractive.extend({
      template: '
this is a component!
'
    });

    new Ractive({
      el: fixture,
      template: '
Here is a component:
<Component/>
(that was a component)
',
      components: { Component }
    });

    t.htmlEqual(
      fixture.innerHTML,
      '
Here is a component:

this is a component!

(that was a component)
'
    );
  });

  test('Top-level sections in components are updated correctly', t => {
    const Component = Ractive.extend({
      template: '{{#foo}}foo is truthy{{/foo}}{{^foo}}foo is falsy{{/foo}}'
    });

    const ractive = new Ractive({
      el: fixture,
      template: '<Component foo="{{foo}}"/>',
      components: { Component }
    });

    t.htmlEqual(fixture.innerHTML, 'foo is falsy');

    ractive.set('foo', true);
    t.htmlEqual(fixture.innerHTML, 'foo is truthy');
  });

  test('Element order is maintained correctly with components with multiple top-level elements', t => {
    const Test = Ractive.extend({
      template: '{{#bool}}TRUE{{/bool}}{{^bool}}FALSE{{/bool}}'
    });

    const ractive = new Ractive({
      el: fixture,
      template: '
before
 <Test bool="{{bool}}"/>
after
',
      components: { Test }
    });

    t.htmlEqual(fixture.innerHTML, '
before
 FALSE
after
');

    ractive.set('bool', true);
    t.htmlEqual(fixture.innerHTML, '
before
 TRUE
after
');

    ractive.set('bool', false);
    t.htmlEqual(fixture.innerHTML, '
before
 FALSE
after
');
  });

  test('Top-level list sections in components do not cause elements to be out of order (#412 regression)', t => {
    const Widget = Ractive.extend({
      template: '{{#numbers:o}}
{{.}}
{{/numbers}}'
    });

    new Ractive({
      el: fixture,
      template: '
Names
<Widget numbers="{{first}}"/><Widget numbers="{{second}}"/>',
      components: { Widget },
      data: {
        first: { one: 'one', two: 'two' },
        second: { three: 'three', four: 'four' }
      }
    });

    t.htmlEqual(fixture.innerHTML, '
Names

one

two

three

four
');
  });

  test('An unless section in a component should still work with an ambiguous condition should still update (#2165)', t => {
    const cmp = Ractive.extend({
      template: '{{#unless nope}}{{foo}}{{/unless}}'
    });
    const r = new Ractive({
      el: fixture,
      template: '<cmp foo="{{bar}}" />',
      data: { bar: 'yep' },
      components: { cmp }
    });

    t.htmlEqual(fixture.innerHTML, 'yep');
    r.set('bar', 'still');
    t.htmlEqual(fixture.innerHTML, 'still');
  });

  test('components should be able to resolve @index refs from their context', t => {
    const cmp = Ractive.extend({
      template: '{{@index}}',
      isolated: false
    });
    new Ractive({
      el: fixture,
      template: '{{#each list}}<cmp />{{/each}}',
      data: {
        list: [0, 0, 0]
      },
      components: { cmp }
    });

    t.htmlEqual(fixture.innerHTML, '012');
  });

  test('resetting a nested component should find the correct anchor when rendering (#2695)', t => {
    const cmp1 = Ractive.extend({
      template: '<cmp/>',
      components: {
        cmp() {
          return this.get('cmp') || 'cmp2';
        }
      },
      isolated: false
    });
    const cmp2 = Ractive.extend({ template: 'first', isolated: false });
    const cmp3 = Ractive.extend({ template: 'second', isolated: false });
    const r = new Ractive({
      el: fixture,
      template: 'the <cmp1/> place',
      components: { cmp1, cmp2, cmp3 }
    });

    t.htmlEqual(fixture.innerHTML, 'the first place');
    const cmp = r.findComponent('cmp1');
    cmp.reset({ cmp: 'cmp3' });
    t.htmlEqual(fixture.innerHTML, 'the second place');
  });

  test(`decorators, transitions, and binding flags are ignored on components`, t => {
    const cmp = Ractive.extend({
      template: 'yep'
    });
    new Ractive({
      target: fixture,
      template: '<cmp as-foo bar-in lazy />',
      components: { cmp },
      decorators: {
        foo() {
          t.ok(false, 'no decorators');
        }
      },
      transitions: {
        bar(trans) {
          t.ok(false, 'no transitions');
          trans.complete();
        }
      }
    });

    t.htmlEqual(fixture.innerHTML, 'yep');
  });

  test(`components that happen to grow a target are still managed by the component`, t => {
    fixture.innerHTML = '

';

    const cmp = Ractive.extend({
      template: 'cmp',
      on: {
        config() {
          this.target = fixture;
          this.append = true;
        }
      }
    });

    const r = new Ractive({
      target: '#move-test-target',
      template: 'root{{#unless hide}}<cmp/>{{/unless}}',
      components: { cmp }
    });

    t.htmlEqual(fixture.innerHTML, '
root
cmp');

    r.toggle('hide');

    t.htmlEqual(fixture.innerHTML, '
root
');

    r.toggle('hide');

    t.htmlEqual(fixture.innerHTML, '
root
cmp');
  });
}
<commit_msg>add an element in the external component test, for kicks<commit_after>import { initModule } from '../../helpers/test-config';
import { test } from 'qunit';

export default function() {
  initModule('render/components.js');

  test('Components are rendered in the correct place', t => {
    const Component = Ractive.extend({
      template: '
this is a component!
'
    });

    new Ractive({
      el: fixture,
      template: '
Here is a component:
<Component/>
(that was a component)
',
      components: { Component }
    });

    t.htmlEqual(
      fixture.innerHTML,
      '
Here is a component:

this is a component!

(that was a component)
'
    );
  });

  test('Top-level sections in components are updated correctly', t => {
    const Component = Ractive.extend({
      template: '{{#foo}}foo is truthy{{/foo}}{{^foo}}foo is falsy{{/foo}}'
    });

    const ractive = new Ractive({
      el: fixture,
      template: '<Component foo="{{foo}}"/>',
      components: { Component }
    });

    t.htmlEqual(fixture.innerHTML, 'foo is falsy');

    ractive.set('foo', true);
    t.htmlEqual(fixture.innerHTML, 'foo is truthy');
  });

  test('Element order is maintained correctly with components with multiple top-level elements', t => {
    const Test = Ractive.extend({
      template: '{{#bool}}TRUE{{/bool}}{{^bool}}FALSE{{/bool}}'
    });

    const ractive = new Ractive({
      el: fixture,
      template: '
before
 <Test bool="{{bool}}"/>
after
',
      components: { Test }
    });

    t.htmlEqual(fixture.innerHTML, '
before
 FALSE
after
');

    ractive.set('bool', true);
    t.htmlEqual(fixture.innerHTML, '
before
 TRUE
after
');

    ractive.set('bool', false);
    t.htmlEqual(fixture.innerHTML, '
before
 FALSE
after
');
  });

  test('Top-level list sections in components do not cause elements to be out of order (#412 regression)', t => {
    const Widget = Ractive.extend({
      template: '{{#numbers:o}}
{{.}}
{{/numbers}}'
    });

    new Ractive({
      el: fixture,
      template: '
Names
<Widget numbers="{{first}}"/><Widget numbers="{{second}}"/>',
      components: { Widget },
      data: {
        first: { one: 'one', two: 'two' },
        second: { three: 'three', four: 'four' }
      }
    });

    t.htmlEqual(fixture.innerHTML, '
Names

one

two

three

four
');
  });

  test('An unless section in a component should still work with an ambiguous condition should still update (#2165)', t => {
    const cmp = Ractive.extend({
      template: '{{#unless nope}}{{foo}}{{/unless}}'
    });
    const r = new Ractive({
      el: fixture,
      template: '<cmp foo="{{bar}}" />',
      data: { bar: 'yep' },
      components: { cmp }
    });

    t.htmlEqual(fixture.innerHTML, 'yep');
    r.set('bar', 'still');
    t.htmlEqual(fixture.innerHTML, 'still');
  });

  test('components should be able to resolve @index refs from their context', t => {
    const cmp = Ractive.extend({
      template: '{{@index}}',
      isolated: false
    });
    new Ractive({
      el: fixture,
      template: '{{#each list}}<cmp />{{/each}}',
      data: {
        list: [0, 0, 0]
      },
      components: { cmp }
    });

    t.htmlEqual(fixture.innerHTML, '012');
  });

  test('resetting a nested component should find the correct anchor when rendering (#2695)', t => {
    const cmp1 = Ractive.extend({
      template: '<cmp/>',
      components: {
        cmp() {
          return this.get('cmp') || 'cmp2';
        }
      },
      isolated: false
    });
    const cmp2 = Ractive.extend({ template: 'first', isolated: false });
    const cmp3 = Ractive.extend({ template: 'second', isolated: false });
    const r = new Ractive({
      el: fixture,
      template: 'the <cmp1/> place',
      components: { cmp1, cmp2, cmp3 }
    });

    t.htmlEqual(fixture.innerHTML, 'the first place');
    const cmp = r.findComponent('cmp1');
    cmp.reset({ cmp: 'cmp3' });
    t.htmlEqual(fixture.innerHTML, 'the second place');
  });

  test(`decorators, transitions, and binding flags are ignored on components`, t => {
    const cmp = Ractive.extend({
      template: 'yep'
    });
    new Ractive({
      target: fixture,
      template: '<cmp as-foo bar-in lazy />',
      components: { cmp },
      decorators: {
        foo() {
          t.ok(false, 'no decorators');
        }
      },
      transitions: {
        bar(trans) {
          t.ok(false, 'no transitions');
          trans.complete();
        }
      }
    });

    t.htmlEqual(fixture.innerHTML, 'yep');
  });

  test(`components that happen to grow a target are still managed by the component`, t => {
    fixture.innerHTML = '

';

    const cmp = Ractive.extend({
      template: 'cmp',
      on: {
        config() {
          this.target = fixture;
          this.append = true;
        }
      }
    });

    const r = new Ractive({
      target: '#move-test-target',
      template: 'root{{#unless hide}}<cmp/>{{/unless}}',
      components: { cmp }
    });

    t.htmlEqual(fixture.innerHTML, '
root
cmp');

    r.toggle('hide');

    t.htmlEqual(fixture.innerHTML, '
root
');

    r.toggle('hide');

    t.htmlEqual(fixture.innerHTML, '
root
cmp');
  });
}
<|endoftext|>


<commit_before>import React from 'react'
import { navigate, logout, showModal, resetTooltips } from '../actions'
import { makeUrl } from '../util/navigation'
import { calliOSBridge } from '../client/util'
import Icon from './Icon'
import SearchInput from './SearchInput'
import { ThreadsDropdown } from '../containers/ThreadsDropdown'
import { NotificationsDropdown } from '../containers/Notifications'
import A from './A'
import Dropdown from './Dropdown'
import { NonLinkAvatar } from './Avatar'
import { isAdmin, hasFeature } from '../models/currentUser'
import { DIRECT_MESSAGES } from '../config/featureFlags'
import cx from 'classnames'

const { bool, func, object } = React.PropTypes

class SearchMenuItem extends React.Component {
  static contextTypes = {dispatch: func}

  constructor (props) {
    super(props)
    this.state = {}
  }

  render () {
    const { dispatch } = this.context
    const { expanded } = this.state
    const search = text => {
      dispatch(navigate(makeUrl('/search', {q: text})))
      close()
      this.refs.input.clear()
    }
    const open = () => {
      this.setState({expanded: true})
      setTimeout(() => this.refs.input.focus())
    }
    const close = event => {
      this.setState({expanded: false})
      if (event) event.stopPropagation()
    }

    return

          <Icon name='Loupe'/>
          {expanded && <SearchInput onChange={search} ref='input'/>}
          {expanded && <Icon onClick={close} name='Fail'/>}

  }
}

const UserMenu = ({ slug, newMessageCount, newNotificationCount }, { isMobile, dispatch, currentUser }) => {
  const { settings: { last_viewed_messages_at }, id } = currentUser
  const doLogout = () => {
    calliOSBridge({type: 'logout'})
    dispatch(navigate('/login'))
    dispatch(logout())
    return false
  }

  return

    <SearchMenuItem/>

    {hasFeature(currentUser, DIRECT_MESSAGES) &&

        <ThreadsDropdown lastViewed={last_viewed_messages_at} newCount={newMessageCount}/>

}

      <NotificationsDropdown newCount={newNotificationCount}/>

      <Dropdown className='user-menu' alignRight triangle={isMobile}
        rivalrous='nav' backdrop={isMobile} toggleChildren={

            <NonLinkAvatar person={currentUser}/>
            {newNotificationCount > 0 &&
}

        }>

          <A to={`/u/${currentUser.id}`}>
            <Icon name='User'/> My profile

           dispatch(showModal('notifications'))}>
            <Icon name='Bell'/> Notifications
            {newNotificationCount > 0 && {newNotificationCount}}

        {hasFeature(currentUser, DIRECT_MESSAGES) &&

             dispatch(showModal('threads'))}>
              <Icon name='Message-Smile'/> Messages
              {newMessageCount > 0 && {newMessageCount}}

}

            <Icon name='Settings'/> Settings

           dispatch(resetTooltips(id))}>
            <Icon name='ProjectorScreen'/> Start tour

        {isAdmin(currentUser) &&

            <Icon name='Keypad'/> Admin

}

            <Icon name='Fail'/> Log out

      </Dropdown>

}
UserMenu.contextTypes = {isMobile: bool, dispatch: func, currentUser: object}

export default UserMenu
<commit_msg>show user menu dot badge when there are new messages<commit_after>import React from 'react'
import { navigate, logout, showModal, resetTooltips } from '../actions'
import { makeUrl } from '../util/navigation'
import { calliOSBridge } from '../client/util'
import Icon from './Icon'
import SearchInput from './SearchInput'
import { ThreadsDropdown } from '../containers/ThreadsDropdown'
import { NotificationsDropdown } from '../containers/Notifications'
import A from './A'
import Dropdown from './Dropdown'
import { NonLinkAvatar } from './Avatar'
import { isAdmin, hasFeature } from '../models/currentUser'
import { DIRECT_MESSAGES } from '../config/featureFlags'
import cx from 'classnames'

const { bool, func, object } = React.PropTypes

class SearchMenuItem extends React.Component {
  static contextTypes = {dispatch: func}

  constructor (props) {
    super(props)
    this.state = {}
  }

  render () {
    const { dispatch } = this.context
    const { expanded } = this.state
    const search = text => {
      dispatch(navigate(makeUrl('/search', {q: text})))
      close()
      this.refs.input.clear()
    }
    const open = () => {
      this.setState({expanded: true})
      setTimeout(() => this.refs.input.focus())
    }
    const close = event => {
      this.setState({expanded: false})
      if (event) event.stopPropagation()
    }

    return

          <Icon name='Loupe'/>
          {expanded && <SearchInput onChange={search} ref='input'/>}
          {expanded && <Icon onClick={close} name='Fail'/>}

  }
}

const UserMenu = ({ slug, newMessageCount, newNotificationCount }, { isMobile, dispatch, currentUser }) => {
  const { settings: { last_viewed_messages_at }, id } = currentUser
  const doLogout = () => {
    calliOSBridge({type: 'logout'})
    dispatch(navigate('/login'))
    dispatch(logout())
    return false
  }

  const showDotBadge = newNotificationCount > 0 || newMessageCount > 0

  return

    <SearchMenuItem/>

    {hasFeature(currentUser, DIRECT_MESSAGES) &&

        <ThreadsDropdown lastViewed={last_viewed_messages_at} newCount={newMessageCount}/>

}

      <NotificationsDropdown newCount={newNotificationCount}/>

      <Dropdown className='user-menu' alignRight triangle={isMobile}
        rivalrous='nav' backdrop={isMobile} toggleChildren={

            <NonLinkAvatar person={currentUser}/>
            {showDotBadge &&
}

        }>

          <A to={`/u/${currentUser.id}`}>
            <Icon name='User'/> My profile

           dispatch(showModal('notifications'))}>
            <Icon name='Bell'/> Notifications
            {newNotificationCount > 0 && {newNotificationCount}}

        {hasFeature(currentUser, DIRECT_MESSAGES) &&

             dispatch(showModal('threads'))}>
              <Icon name='Message-Smile'/> Messages
              {newMessageCount > 0 && {newMessageCount}}

}

            <Icon name='Settings'/> Settings

           dispatch(resetTooltips(id))}>
            <Icon name='ProjectorScreen'/> Start tour

        {isAdmin(currentUser) &&

            <Icon name='Keypad'/> Admin

}

            <Icon name='Fail'/> Log out

      </Dropdown>

}
UserMenu.contextTypes = {isMobile: bool, dispatch: func, currentUser: object}

export default UserMenu
<|endoftext|>


<commit_before>"use strict";

// Gets the persistent filesystem for this app and wraps it in the fs interface.
module.exports = function (callback) {
  var requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem;
  requestFileSystem(window.PERSISTENT, null, function (fs) {
    callback(null, wrapFileSystem(fs));
  }, function (fileError) {
    callback(new Error("Problem getting fs: " + formatError(fileError)));
  });
};

// These functions make using async function in the repl easier.  If no
// callback is specified, they log the result to the console.
function log(err) {
  if (err) throw err;
  console.log.apply(console, Array.prototype.slice.call(arguments, 1));
}
function formatError(fileError) {
  switch (fileError.code) {
    case window.FileError.QUOTA_EXCEEDED_ERR: return 'QUOTA_EXCEEDED_ERR';
    case window.FileError.NOT_FOUND_ERR: return 'NOT_FOUND_ERR';
    case window.FileError.SECURITY_ERR: return 'SECURITY_ERR';
    case window.FileError.INVALID_MODIFICATION_ERR: return 'INVALID_MODIFICATION_ERR';
    case window.FileError.INVALID_STATE_ERR: return 'INVALID_STATE_ERR';
    default: return 'Unknown Error';
  }
}
function check(path, callback) {
  return function (err) {
    if (err && err instanceof window.FileError) {
      err.path = path;
      err.message = formatError(err) + " at '" + path + "'";
      console.error(err.message);
      callback(err);
    }
    else {
      callback.apply(this, arguments);
    }
  };
}
function rr2(fn) {
  return function (arg1, arg2, callback) {
    return fn(arg1, arg2, check(arg1, callback || log));
  };
}
function rr1(fn) {
  return function (arg, callback) {
    return fn(arg, check(arg, callback || log));
  };
}

function wrapFileSystem(fileSystem) {
  var cwd = fileSystem.root;
  var fs = {
    readfile: rr1(readfile),
    writefile: rr2(writefile),
    rmfile: rr1(rmfile),
    readdir: rr1(readdir),
    mkdir: rr1(mkdir),
    rmdir: rr1(rmdir),
    copy: rr2(copy),
    move: rr2(move),
    chdir: rr1(chdir),
    cwd: function () { return cwd.fullPath; }
  };

  function readfile(path, callback) {
    cwd.getFile(path, {}, function (fileEntry) {
      fileEntry.file(function (file) {
        var reader = new FileReader();
        reader.onloadend = function () {
          callback(null, this.result, fileEntry);
        };
        reader.readAsText(file);
      }, callback);
    }, callback);
  }

  function writefile(path, contents, callback) {
    cwd.getFile(path, {create: true}, function (fileEntry) {
      fileEntry.createWriter(function (fileWriter) {
        fileWriter.onwriteend = function () {
          callback(null, fileEntry);
        };
        fileWriter.onerror = callback;
        fileWriter.write(new Blob([contents], {type: 'text/plain'}));
      }, callback);
    }, callback);
  }
  function rmfile(path, callback) {
    cwd.getFile(path, {}, function (fileEntry) {
      fileEntry.remove(function () {
        callback();
      }, callback);
    }, callback);
  }
  function readdir(path, callback) {
    cwd.getDirectory(path, {}, function (dirEntry) {
      var dirReader = dirEntry.createReader();
      var entries = [];
      readEntries();
      function readEntries() {
        dirReader.readEntries(function (results) {
          if (!results.length) {
            callback(null, entries);
          }
          else {
            entries = entries.concat(Array.prototype.slice.call(results).map(function (entry) {
              return entry.name + (entry.isDirectory ? "/" : "");
            }));
            readEntries();
          }
        }, callback);
      }
    }, callback);
  }
  function mkdir(path, callback) {
    cwd.getDirectory(path, {create: true}, function () {
      callback();
    }, callback);
  }
  function rmdir(path, callback) {
    cwd.getDirectory(path, {}, function (dirEntry) {
      dirEntry.removeRecursively(function () {
        callback();
      }, callback);
    }, callback);
  }
  function copy(src, dest, callback) {
    // TODO: make sure works for cases where dest includes and excludes file name.
    cwd.getFile(src, {}, function(fileEntry) {
      cwd.getDirectory(dest, {}, function(dirEntry) {
        fileEntry.copyTo(dirEntry, function () {
          callback();
        }, callback);
      }, callback);
    }, callback);
  }
  function move(src, dest, callback) {
    // TODO: handle more cases like file renames and moving/renaming directories
    cwd.getFile(src, {}, function(fileEntry) {
      cwd.getDirectory(dest, {}, function(dirEntry) {
        fileEntry.moveTo(dirEntry, function () {
          callback();
        }, callback);
      }, callback);
    }, callback);
  }
  function chdir(path, callback) {
    cwd.getDirectory(path, {}, function (dirEntry) {
      cwd = dirEntry;
      if (fs.onchdir) {
        fs.onchdir(cwd.fullPath);
      }
      callback();
    }, callback);
  }

  return fs;
}

<commit_msg>Expose some useful stuff from the html5 fs implementation<commit_after>"use strict";

// Gets the persistent filesystem for this app and wraps it in the fs interface.
module.exports = getfs;
getfs.wrapFileSystem = wrapFileSystem;
getfs.formatError = formatError;
function getfs(callback) {
  var requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem;
  requestFileSystem(window.PERSISTENT, null, function (fs) {
    callback(null, wrapFileSystem(fs));
  }, function (fileError) {
    callback(new Error("Problem getting fs: " + formatError(fileError)));
  });
}

// These functions make using async function in the repl easier.  If no
// callback is specified, they log the result to the console.
function log(err) {
  if (err) throw err;
  console.log.apply(console, Array.prototype.slice.call(arguments, 1));
}
function formatError(fileError) {
  switch (fileError.code) {
    case window.FileError.QUOTA_EXCEEDED_ERR: return 'QUOTA_EXCEEDED_ERR';
    case window.FileError.NOT_FOUND_ERR: return 'NOT_FOUND_ERR';
    case window.FileError.SECURITY_ERR: return 'SECURITY_ERR';
    case window.FileError.INVALID_MODIFICATION_ERR: return 'INVALID_MODIFICATION_ERR';
    case window.FileError.INVALID_STATE_ERR: return 'INVALID_STATE_ERR';
    default: return 'Unknown Error';
  }
}
function check(path, callback) {
  return function (err) {
    if (err && err instanceof window.FileError) {
      err.path = path;
      err.message = formatError(err) + " at '" + path + "'";
      console.error(err.message);
      callback(err);
    }
    else {
      callback.apply(this, arguments);
    }
  };
}
function rr2(fn) {
  return function (arg1, arg2, callback) {
    return fn(arg1, arg2, check(arg1, callback || log));
  };
}
function rr1(fn) {
  return function (arg, callback) {
    return fn(arg, check(arg, callback || log));
  };
}

function wrapFileSystem(fileSystem) {
  var cwd = fileSystem.root;
  var fs = {
    readfile: rr1(readfile),
    writefile: rr2(writefile),
    rmfile: rr1(rmfile),
    readdir: rr1(readdir),
    mkdir: rr1(mkdir),
    rmdir: rr1(rmdir),
    copy: rr2(copy),
    move: rr2(move),
    chdir: rr1(chdir),
    cwd: function () { return cwd.fullPath; }
  };

  function readfile(path, callback) {
    cwd.getFile(path, {}, function (fileEntry) {
      fileEntry.file(function (file) {
        var reader = new FileReader();
        reader.onloadend = function () {
          callback(null, this.result, fileEntry);
        };
        reader.readAsText(file);
      }, callback);
    }, callback);
  }

  function writefile(path, contents, callback) {
    cwd.getFile(path, {create: true}, function (fileEntry) {
      fileEntry.createWriter(function (fileWriter) {
        fileWriter.onwriteend = function () {
          callback(null, fileEntry);
        };
        fileWriter.onerror = callback;
        fileWriter.write(new Blob([contents], {type: 'text/plain'}));
      }, callback);
    }, callback);
  }
  function rmfile(path, callback) {
    cwd.getFile(path, {}, function (fileEntry) {
      fileEntry.remove(function () {
        callback();
      }, callback);
    }, callback);
  }
  function readdir(path, callback) {
    cwd.getDirectory(path, {}, function (dirEntry) {
      var dirReader = dirEntry.createReader();
      var entries = [];
      readEntries();
      function readEntries() {
        dirReader.readEntries(function (results) {
          if (!results.length) {
            callback(null, entries);
          }
          else {
            entries = entries.concat(Array.prototype.slice.call(results).map(function (entry) {
              return entry.name + (entry.isDirectory ? "/" : "");
            }));
            readEntries();
          }
        }, callback);
      }
    }, callback);
  }
  function mkdir(path, callback) {
    cwd.getDirectory(path, {create: true}, function () {
      callback();
    }, callback);
  }
  function rmdir(path, callback) {
    cwd.getDirectory(path, {}, function (dirEntry) {
      dirEntry.removeRecursively(function () {
        callback();
      }, callback);
    }, callback);
  }
  function copy(src, dest, callback) {
    // TODO: make sure works for cases where dest includes and excludes file name.
    cwd.getFile(src, {}, function(fileEntry) {
      cwd.getDirectory(dest, {}, function(dirEntry) {
        fileEntry.copyTo(dirEntry, function () {
          callback();
        }, callback);
      }, callback);
    }, callback);
  }
  function move(src, dest, callback) {
    // TODO: handle more cases like file renames and moving/renaming directories
    cwd.getFile(src, {}, function(fileEntry) {
      cwd.getDirectory(dest, {}, function(dirEntry) {
        fileEntry.moveTo(dirEntry, function () {
          callback();
        }, callback);
      }, callback);
    }, callback);
  }
  function chdir(path, callback) {
    cwd.getDirectory(path, {}, function (dirEntry) {
      cwd = dirEntry;
      if (fs.onchdir) {
        fs.onchdir(cwd.fullPath);
      }
      callback();
    }, callback);
  }

  return fs;
}

<|endoftext|>


<commit_before>
import Entity from '../../../client/src/entity';

const expect = require('chai').expect;
const RG = require('../../../client/src/battles.js');

const {NO_SERIALISATION} = RG.Component;

describe('Component.Base', () => {

    it('has exactly one related entity', () => {
        const entity = new Entity();
        const entity2 = new Entity();
        const comp = new RG.Component.Base('Base');

        expect(comp.getType()).to.equal('Base');

        comp.setEntity(entity);
        expect(comp.getEntity().getID()).to.equal(entity.getID());

        // Try to override the entity
        RG.suppressErrorMessages = true;
        comp.setEntity(entity2);
        expect(comp.getEntity().getID()).to.equal(entity.getID());
        RG.suppressErrorMessages = false;

        comp.setEntity(null);
        expect(comp.getEntity()).to.be.null;
    });

    it('can be copied, cloned, compared', () => {
        const comp = new RG.Component.Base('Base');
        const compClone = comp.clone();
        expect(comp.equals(compClone)).to.be.true;
        expect(compClone.equals(comp)).to.be.true;

        const compCopy = new RG.Component.Base('XXX');
        compCopy.copy(comp);
        expect(comp.equals(compCopy)).to.be.true;
        expect(comp.toString()).to.match(/Base/);
    });

    it('has onAdd/Remove callback mechanism', () => {
        const comp = new RG.Component.Base('Base');
        const entity = new Entity();

        let calledAdd = false;
        const callbackAdd = () => {calledAdd = true;};

        let calledRemove = false;
        const callbackRemove = () => {calledRemove = true;};

        let calledIllegal = false;
        const callbackIllegal = () => {calledIllegal = true;};

        comp.addCallback('onAdd', callbackAdd);
        comp.addCallback('onRemove', callbackRemove);
        RG.suppressErrorMessages = true;
        comp.addCallback('onIllegal', callbackIllegal);
        RG.suppressErrorMessages = false;

        expect(calledIllegal).to.be.false;

        expect(calledAdd).to.be.false;
        entity.add('Base', comp);
        expect(calledAdd).to.be.true;

        expect(calledRemove).to.be.false;
        entity.remove('Base');
        expect(calledRemove).to.be.true;
        expect(calledIllegal).to.be.false;
    });
});

describe('RG.TagComponent', () => {

    it('is used to create comp declarations with no data fields', () => {
        const conf = {_privateField: true, falseField: false};
        const Undead = RG.TagComponent('Undead', conf);
        const undeadComp = new Undead();
        expect(undeadComp.getType()).to.equal('Undead');
        expect(undeadComp._privateField).to.exist;
        expect(undeadComp.falseField).to.be.false;

        expect(undeadComp.toJSON).to.be.a('function');
        expect(undeadComp.getType).to.be.a('function');
        expect(undeadComp.getID).to.be.a('function');
        expect(undeadComp.getEntity).to.be.a('function');
    });

    it('can be used for non-serialisable components as well', () => {
        const FastFlight = RG.TagComponent('FastFlight',
            {toJSON: NO_SERIALISATION});
        const flight = new FastFlight();
        expect(flight.toJSON).to.equal(NO_SERIALISATION);
    });

});

describe('RG.DataComponent', () => {

    it('is used to create new data component declarations', () => {
        const Immunity = RG.DataComponent('Immunity',
            {ratio: 0, dmgType: ''});

        const immunComp = new Immunity();
        expect(immunComp.getType()).to.equal('Immunity');
        expect(immunComp.getRatio()).to.equal(0);

        immunComp.setDmgType('Fire');
        expect(immunComp.getDmgType()).to.equal('Fire');
    });

    it('cannot overwrite Component.Base methods', () => {
        let Immunity = null;
        const createFunc = () => {
            Immunity = RG.DataComponent('Immunity', ['type']);
        };
        expect(createFunc).to.throw(Error);
    });

});

describe('Component.Poison', () => {
    it('can be copied or cloned', () => {
        const p1 = new RG.Component.Poison();
        p1.setProb(0.01);
        p1.setDamageDie('1d6 + 4');
        p1.setDurationDie('3d5 + 15');
        const p2 = p1.clone();

        expect(p2.getProb()).to.equal(0.01);

        const d1Str = p1.getDamageDie().toString();
        const d2Str = p2.getDamageDie().toString();
        expect(d1Str).to.equal(d2Str);
    });
});

describe('Component.Combat', () => {
    it('contains damage dies for damage dealing', () => {
        const player = RG.FACT.createPlayer('Player', {});
        const combatComp = new RG.Component.Combat();
        player.add('Combat', combatComp);
        expect(player.get('Combat').rollDamage() >= 1).to.equal(true);
        expect(player.get('Combat').rollDamage() <= 4).to.equal(true);
    });
});
<commit_msg>Added test for component action.<commit_after>
import Entity from '../../../client/src/entity';

const expect = require('chai').expect;
const RG = require('../../../client/src/battles.js');
RG.Component = require('../../../client/src/component');

const {NO_SERIALISATION} = RG.Component;

describe('Component.Base', () => {

    it('has exactly one related entity', () => {
        const entity = new Entity();
        const entity2 = new Entity();
        const comp = new RG.Component.Base('Base');

        expect(comp.getType()).to.equal('Base');

        comp.setEntity(entity);
        expect(comp.getEntity().getID()).to.equal(entity.getID());

        // Try to override the entity
        RG.suppressErrorMessages = true;
        comp.setEntity(entity2);
        expect(comp.getEntity().getID()).to.equal(entity.getID());
        RG.suppressErrorMessages = false;

        comp.setEntity(null);
        expect(comp.getEntity()).to.be.null;
    });

    it('can be copied, cloned, compared', () => {
        const comp = new RG.Component.Base('Base');
        const compClone = comp.clone();
        expect(comp.equals(compClone)).to.be.true;
        expect(compClone.equals(comp)).to.be.true;

        const compCopy = new RG.Component.Base('XXX');
        compCopy.copy(comp);
        expect(comp.equals(compCopy)).to.be.true;
        expect(comp.toString()).to.match(/Base/);
    });

    it('has onAdd/Remove callback mechanism', () => {
        const comp = new RG.Component.Base('Base');
        const entity = new Entity();

        let calledAdd = false;
        const callbackAdd = () => {calledAdd = true;};

        let calledRemove = false;
        const callbackRemove = () => {calledRemove = true;};

        let calledIllegal = false;
        const callbackIllegal = () => {calledIllegal = true;};

        comp.addCallback('onAdd', callbackAdd);
        comp.addCallback('onRemove', callbackRemove);
        RG.suppressErrorMessages = true;
        comp.addCallback('onIllegal', callbackIllegal);
        RG.suppressErrorMessages = false;

        expect(calledIllegal).to.be.false;

        expect(calledAdd).to.be.false;
        entity.add('Base', comp);
        expect(calledAdd).to.be.true;

        expect(calledRemove).to.be.false;
        entity.remove('Base');
        expect(calledRemove).to.be.true;
        expect(calledIllegal).to.be.false;
    });
});

describe('RG.TagComponent', () => {

    it('is used to create comp declarations with no data fields', () => {
        const conf = {_privateField: true, falseField: false};
        const Undead = RG.TagComponent('Undead', conf);
        const undeadComp = new Undead();
        expect(undeadComp.getType()).to.equal('Undead');
        expect(undeadComp._privateField).to.exist;
        expect(undeadComp.falseField).to.be.false;

        expect(undeadComp.toJSON).to.be.a('function');
        expect(undeadComp.getType).to.be.a('function');
        expect(undeadComp.getID).to.be.a('function');
        expect(undeadComp.getEntity).to.be.a('function');
    });

    it('can be used for non-serialisable components as well', () => {
        const FastFlight = RG.TagComponent('FastFlight',
            {toJSON: NO_SERIALISATION});
        const flight = new FastFlight();
        expect(flight.toJSON).to.equal(NO_SERIALISATION);
    });

});

describe('RG.DataComponent', () => {

    it('is used to create new data component declarations', () => {
        const Immunity = RG.DataComponent('Immunity',
            {ratio: 0, dmgType: ''});

        const immunComp = new Immunity();
        expect(immunComp.getType()).to.equal('Immunity');
        expect(immunComp.getRatio()).to.equal(0);

        immunComp.setDmgType('Fire');
        expect(immunComp.getDmgType()).to.equal('Fire');
    });

    it('cannot overwrite Component.Base methods', () => {
        let Immunity = null;
        const createFunc = () => {
            Immunity = RG.DataComponent('Immunity', ['type']);
        };
        expect(createFunc).to.throw(Error);
    });

});

describe('Component.Action', () => {

    it('active status and energy', () => {
        const action = new RG.Component.Action();
        expect(action.getEnergy()).to.equal(0);
        expect(action.getActive()).to.equal(false);

    });

});

describe('Component.Poison', () => {
    it('can be copied or cloned', () => {
        const p1 = new RG.Component.Poison();
        p1.setProb(0.01);
        p1.setDamageDie('1d6 + 4');
        p1.setDurationDie('3d5 + 15');
        const p2 = p1.clone();

        expect(p2.getProb()).to.equal(0.01);

        const d1Str = p1.getDamageDie().toString();
        const d2Str = p2.getDamageDie().toString();
        expect(d1Str).to.equal(d2Str);
    });
});

describe('Component.Combat', () => {
    it('contains damage dies for damage dealing', () => {
        const player = RG.FACT.createPlayer('Player', {});
        const combatComp = new RG.Component.Combat();
        player.add('Combat', combatComp);
        expect(player.get('Combat').rollDamage() >= 1).to.equal(true);
        expect(player.get('Combat').rollDamage() <= 4).to.equal(true);
    });
});
<|endoftext|>