<commit_before>/**
 * @fileOverview Search Folder Manager for Thunderbird
 * @author       ClearCode Inc.
 * @version      3
 *
 * @description
 *   Usage:
 *     Components.utills.import('resource://.../SerchFolderManager.jsm');
 *     new SerchFolderManager([
 *       { label:         'Important',
 *         subjectPrefix: '[Important]' },
 *       { label:         'FYI',
 *         conditions:    'OR (subject,begins with,FYI) OR (subject,begins with,[FYI])' },
 *       { label:         'DenyHosts Reports',
 *         subject:       'DenyHosts Report from www.example.com' },
 *       ...
 *     ]);
 *
 * @license
 *   The MIT License, Copyright (c) 2014 ClearCode Inc.
 * @url https://github.com/clear-code/js-codemodule-search-folder-manager
 */

var EXPORTED_SYMBOLS = ['SearchFolderManager'];

var Cc = Components.classes;
var Ci = Components.interfaces;
var Cu = Components.utils;
var Cr = Components.results;

Cu.import('resource://gre/modules/XPCOMUtils.jsm');

XPCOMUtils.defineLazyServiceGetter(this,
                                   'AccountManager',
                                   '@mozilla.org/messenger/account-manager;1',
                                   'nsIMsgAccountManager');
XPCOMUtils.defineLazyServiceGetter(this,
                                   'MailSession',
                                   '@mozilla.org/messenger/services/session;1',
                                   'nsIMsgMailSession');

XPCOMUtils.defineLazyModuleGetter(this,
                                  'Services',
                                  'resource://gre/modules/Services.jsm');
XPCOMUtils.defineLazyModuleGetter(this,
                                  'MailServices',
                                  'resource:///modules/mailServices.js');
XPCOMUtils.defineLazyModuleGetter(this,
                                  'VirtualFolderHelper',
                                  'resource:///modules/virtualFolderWrapper.js');

/**
 * Initializes a search folder manager for given folder
 * definitions (search conditions).
 *
 * @param Array of Definition
 *        An array of search folder definitions (search conditions).
 *
 * Definition:
 *   @param label         String: the name of the folder
 *   @param conditions    String: the search conditions (optional)
 *   @param subject       String: the subject to be matched (optional)
 *   @param subjectPrefix String: the prefix of the subject to be matched (optional)
 *   @param searchTargets String: "all" or comma-separated URIs of mail folders (optional, default=all)
 */
function SearchFolderManager(aDefinitions) {
  this.definitions = aDefinitions;

  Services.obs.addObserver(this, 'mail-tabs-session-restored', false);
}
SearchFolderManager.prototype = {
  get allAccounts() {
    return this.toArray(AccountManager.accounts, Ci.nsIMsgAccount);
  },
  get allAccountKeys() {
    return this.allAccounts.map(function(aAccount) {
      return this.getAccountKey(aAccount);
    }, this).filter(function(aKey) {
      return aKey != '';
    });
  },
  toArray: function (aEnumerator, aInterface) {
    aInterface = aInterface || Ci.nsISupports;
    var array = [];
    if (aEnumerator instanceof Ci.nsISupportsArray) {
      let count = aEnumerator.Count();
      for (let i = 0; i < count; i++) {
        array.push(aEnumerator.QueryElementAt(i, aInterface));
      }
    } else if (aEnumerator instanceof Ci.nsIArray) {
      let count = aEnumerator.length;
      for (let i = 0; i < count; i++) {
        array.push(aEnumerator.queryElementAt(i, aInterface));
      }
    } else if (aEnumerator instanceof Ci.nsISimpleEnumerator) {
      while (aEnumerator.hasMoreElements()) {
        array.push(aEnumerator.getNext().QueryInterface(aInterface));
      }
    }
    return array;
  },
  getDescendants: function(aRoot) {
    var folders = [];
    if ('descendants' in aRoot) { // Thunderbird 24
      let descendants = aRoot.descendants;
      for (let i = 0, maxi = descendants.length; i < maxi; i++) {
        let folder = descendants.queryElementAt(i, Ci.nsIMsgFolder);
        folders.push(folder);
      }
    } else { // Thunderbird 17 or olders
      let descendants = Cc['@mozilla.org/supports-array;1']
                          .createInstance(Ci.nsISupportsArray);
      aRoot.ListDescendents(descendants);
      for (let i = 0, maxi = descendants.Count(); i < maxi; i++) {
        let folder = descendants.GetElementAt(i).QueryInterface(Ci.nsIMsgFolder);
        folders.push(folder);
      }
    }
    return folders;
  },

  observe: function(aSubject, aTopic, aData) {
    Services.obs.removeObserver(this, 'mail-tabs-session-restored');
    // This must be done after mail-tabs-session-restored.
    // Otherwise, non-ASCII search conditions are not saved correctly.
    this.buildSearchFolders();

    this.startListenFolderChanges();
  },
  startListenFolderChanges: function() {
    var notifyFlags = Ci.nsIFolderListener.added |
                        Ci.nsIFolderListener.removed;
    MailSession.AddFolderListener(this, notifyFlags);
  },
  // nsIFolderListener
  OnItemAdded: function(aParent, aItem) {
    try {
      aItem = aItem.QueryInterface(Ci.nsIMsgFolder);
      if (aItem.flags & Ci.nsMsgFolderFlags.Virtual)
        return;
    } catch(e) {
      return;
    }
    this.buildSearchFoldersIn(aItem.rootFolder, {
      addedFolder: aItem.URI
    });
  },
  OnItemRemoved: function(aParent, aItem) {
    try {
      aItem = aItem.QueryInterface(Ci.nsIMsgFolder);
      if (aItem.flags & Ci.nsMsgFolderFlags.Virtual)
        return;
    } catch(e) {
      return;
    }
    this.buildSearchFoldersIn(aItem.rootFolder, {
      removedFolder: aItem.URI
    });
  },
  OnItemPropertyChanged: function() {},
  OnItemIntPropertyChanged: function() {},
  OnItemBoolPropertyChanged: function() {},
  OnItemUnicharPropertyChanged: function() {},
  OnItemEvent: function() {},

  buildSearchFolders: function() {
    this.allAccounts.forEach(function(aAccount) {
      if (!aAccount.incomingServer)
        return;
      this.buildSearchFoldersIn(aAccount.incomingServer.rootMsgFolder);
    }, this);
  },
  buildSearchFoldersIn: function(aRoot, aModification) {
    if (!aRoot)
      return;
    this.definitions.forEach(function(aDefinition) {
      try {
        this.buildSearchFolderIn(aDefinition, aRoot, aModification);
      }
      catch(e) {
        Cu.reportError(e);
      }
    }, this);
  },
  buildSearchFolderIn: function(aDefinition, aRoot, aModification) {
    if (!aDefinition.condition &&
        !aDefinition.subjectPrefix &&
        !aDefinition.subject ||
        !aRoot)
      return;

    var searchTargets = aDefinition.searchTargets || 'all';
    var searchFolders;
    if (!aModification) {
      searchFolders = this.getSearchFolders(aRoot, searchTargets.split(/[,\s]+/));
      if (!searchFolders)
        return;
    }

    var name = 'autogen-' + aDefinition.label;

    var isCreation = false;
    var isModified = false;
    var virtualFolder;
    try {
      virtualFolder = aRoot.getChildNamed(name);
    } catch(e) {
      try {
        virtualFolder = aRoot.getChildNamed(aDefinition.label);
        if (!(virtualFolder.flags & Ci.nsMsgFolderFlags.Virtual))
          virtualFolder = null;
      } catch(e) {
        // folder not found!
      }
    }
    if (!virtualFolder) {
      isCreation = true;
      isModified = true;
      virtualFolder = aRoot.addSubfolder(name);
      virtualFolder.setFlag(Ci.nsMsgFolderFlags.Virtual);
    }

    // We always have to set prettyName because it is not saved.
    virtualFolder.prettyName = aDefinition.label;

    var wrapper = VirtualFolderHelper.wrapVirtualFolder(virtualFolder);

    var conditions = this.buildSearchCondition(aDefinition);
    if (wrapper.searchString != conditions) {
      wrapper.searchString = conditions;
      isModified = true;
    }
    var currentSearchFolders = wrapper.searchFolders.map(function(aFolder) {
      return aFolder.URI
    }).join('|');
    if (aModification) {
      searchFolders = currentSearchFolders.split('|');
      if (aModification.addedFolder) {
        let index = searchFolders.indexOf(aModification.addedFolder);
        if (index < 0) {
          searchFolders.push(aModification.addedFolder);
        }
      }
      if (aModification.removedFolder) {
        let index = searchFolders.indexOf(aModification.removedFolder);
        if (index > -1) {
          searchFolders.splice(index, 1);
        }
      }
      searchFolders = searchFolders.join('|');
    }
    if (currentSearchFolders != searchFolders) {
      wrapper.searchFolders = searchFolders;
      isModified = true;
    }
    if (isCreation) {
      wrapper.onlineSearch = false;
    }

    if (!isModified)
      return;

    wrapper.cleanUpMessageDatabase();
    if (isCreation) {
      virtualFolder.msgDatabase.Close(true);
      aRoot.NotifyItemAdded(virtualFolder);
    }
    MailServices.accounts.saveVirtualFolders();
  },
  getSearchFolders: function(aRoot, aKeys) {
    var flags = {};
    Object.keys(Ci.nsMsgFolderFlags).forEach(function(aKey) {
      flags[aKey.toLowerCase()] = Ci.nsMsgFolderFlags[aKey];
    });
    var folders = [];
    aKeys.some(function(aKey) {
      aKey = aKey.toLowerCase();
      if (aKey == 'all') {
        folders = this.getDescendants(aRoot).map(function(aFolder) {
          return aFolder.URI;
        });
        return true;
      }
      if (typeof flags[aKey] == 'number') {
        let folder = aRoot.getFolderWithFlags(flags[aKey]);
        if (folder)
          folders.push(folder.URI);
      }
      return false;
    }, this);
    try {
      if (folders.length > 0)
        folders.unshift(aRoot.URI);
    } catch(e) {
    }
    return folders.join('|');
  },
  buildSearchCondition: function(aDefinition) {
    if (aDefinition.conditions)
      return this.UnicodeToUTF8(aDefinition.conditions);

    if (aDefinition.subjectPrefix)
      return 'AND (subject,begins with,' + this.UnicodeToUTF8(aDefinition.subjectPrefix) + ')';

    return 'AND (subject,is,' + this.UnicodeToUTF8(aDefinition.subject) + ')';
  },
  UnicodeToUTF8: function(aString) {
    return unescape(encodeURIComponent(aString));
  }
};
<commit_msg>Fix missing "s"<commit_after>/**
 * @fileOverview Search Folder Manager for Thunderbird
 * @author       ClearCode Inc.
 * @version      3
 *
 * @description
 *   Usage:
 *     Components.utills.import('resource://.../SerchFolderManager.jsm');
 *     new SerchFolderManager([
 *       { label:         'Important',
 *         subjectPrefix: '[Important]' },
 *       { label:         'FYI',
 *         conditions:    'OR (subject,begins with,FYI) OR (subject,begins with,[FYI])' },
 *       { label:         'DenyHosts Reports',
 *         subject:       'DenyHosts Report from www.example.com' },
 *       ...
 *     ]);
 *
 * @license
 *   The MIT License, Copyright (c) 2014 ClearCode Inc.
 * @url https://github.com/clear-code/js-codemodule-search-folder-manager
 */

var EXPORTED_SYMBOLS = ['SearchFolderManager'];

var Cc = Components.classes;
var Ci = Components.interfaces;
var Cu = Components.utils;
var Cr = Components.results;

Cu.import('resource://gre/modules/XPCOMUtils.jsm');

XPCOMUtils.defineLazyServiceGetter(this,
                                   'AccountManager',
                                   '@mozilla.org/messenger/account-manager;1',
                                   'nsIMsgAccountManager');
XPCOMUtils.defineLazyServiceGetter(this,
                                   'MailSession',
                                   '@mozilla.org/messenger/services/session;1',
                                   'nsIMsgMailSession');

XPCOMUtils.defineLazyModuleGetter(this,
                                  'Services',
                                  'resource://gre/modules/Services.jsm');
XPCOMUtils.defineLazyModuleGetter(this,
                                  'MailServices',
                                  'resource:///modules/mailServices.js');
XPCOMUtils.defineLazyModuleGetter(this,
                                  'VirtualFolderHelper',
                                  'resource:///modules/virtualFolderWrapper.js');

/**
 * Initializes a search folder manager for given folder
 * definitions (search conditions).
 *
 * @param Array of Definition
 *        An array of search folder definitions (search conditions).
 *
 * Definition:
 *   @param label         String: the name of the folder
 *   @param conditions    String: the search conditions (optional)
 *   @param subject       String: the subject to be matched (optional)
 *   @param subjectPrefix String: the prefix of the subject to be matched (optional)
 *   @param searchTargets String: "all" or comma-separated URIs of mail folders (optional, default=all)
 */
function SearchFolderManager(aDefinitions) {
  this.definitions = aDefinitions;

  Services.obs.addObserver(this, 'mail-tabs-session-restored', false);
}
SearchFolderManager.prototype = {
  get allAccounts() {
    return this.toArray(AccountManager.accounts, Ci.nsIMsgAccount);
  },
  get allAccountKeys() {
    return this.allAccounts.map(function(aAccount) {
      return this.getAccountKey(aAccount);
    }, this).filter(function(aKey) {
      return aKey != '';
    });
  },
  toArray: function (aEnumerator, aInterface) {
    aInterface = aInterface || Ci.nsISupports;
    var array = [];
    if (aEnumerator instanceof Ci.nsISupportsArray) {
      let count = aEnumerator.Count();
      for (let i = 0; i < count; i++) {
        array.push(aEnumerator.QueryElementAt(i, aInterface));
      }
    } else if (aEnumerator instanceof Ci.nsIArray) {
      let count = aEnumerator.length;
      for (let i = 0; i < count; i++) {
        array.push(aEnumerator.queryElementAt(i, aInterface));
      }
    } else if (aEnumerator instanceof Ci.nsISimpleEnumerator) {
      while (aEnumerator.hasMoreElements()) {
        array.push(aEnumerator.getNext().QueryInterface(aInterface));
      }
    }
    return array;
  },
  getDescendants: function(aRoot) {
    var folders = [];
    if ('descendants' in aRoot) { // Thunderbird 24
      let descendants = aRoot.descendants;
      for (let i = 0, maxi = descendants.length; i < maxi; i++) {
        let folder = descendants.queryElementAt(i, Ci.nsIMsgFolder);
        folders.push(folder);
      }
    } else { // Thunderbird 17 or olders
      let descendants = Cc['@mozilla.org/supports-array;1']
                          .createInstance(Ci.nsISupportsArray);
      aRoot.ListDescendents(descendants);
      for (let i = 0, maxi = descendants.Count(); i < maxi; i++) {
        let folder = descendants.GetElementAt(i).QueryInterface(Ci.nsIMsgFolder);
        folders.push(folder);
      }
    }
    return folders;
  },

  observe: function(aSubject, aTopic, aData) {
    Services.obs.removeObserver(this, 'mail-tabs-session-restored');
    // This must be done after mail-tabs-session-restored.
    // Otherwise, non-ASCII search conditions are not saved correctly.
    this.buildSearchFolders();

    this.startListenFolderChanges();
  },
  startListenFolderChanges: function() {
    var notifyFlags = Ci.nsIFolderListener.added |
                        Ci.nsIFolderListener.removed;
    MailSession.AddFolderListener(this, notifyFlags);
  },
  // nsIFolderListener
  OnItemAdded: function(aParent, aItem) {
    try {
      aItem = aItem.QueryInterface(Ci.nsIMsgFolder);
      if (aItem.flags & Ci.nsMsgFolderFlags.Virtual)
        return;
    } catch(e) {
      return;
    }
    this.buildSearchFoldersIn(aItem.rootFolder, {
      addedFolder: aItem.URI
    });
  },
  OnItemRemoved: function(aParent, aItem) {
    try {
      aItem = aItem.QueryInterface(Ci.nsIMsgFolder);
      if (aItem.flags & Ci.nsMsgFolderFlags.Virtual)
        return;
    } catch(e) {
      return;
    }
    this.buildSearchFoldersIn(aItem.rootFolder, {
      removedFolder: aItem.URI
    });
  },
  OnItemPropertyChanged: function() {},
  OnItemIntPropertyChanged: function() {},
  OnItemBoolPropertyChanged: function() {},
  OnItemUnicharPropertyChanged: function() {},
  OnItemEvent: function() {},

  buildSearchFolders: function() {
    this.allAccounts.forEach(function(aAccount) {
      if (!aAccount.incomingServer)
        return;
      this.buildSearchFoldersIn(aAccount.incomingServer.rootMsgFolder);
    }, this);
  },
  buildSearchFoldersIn: function(aRoot, aModification) {
    if (!aRoot)
      return;
    this.definitions.forEach(function(aDefinition) {
      try {
        this.buildSearchFolderIn(aDefinition, aRoot, aModification);
      }
      catch(e) {
        Cu.reportError(e);
      }
    }, this);
  },
  buildSearchFolderIn: function(aDefinition, aRoot, aModification) {
    if (!aDefinition.conditions &&
        !aDefinition.subjectPrefix &&
        !aDefinition.subject ||
        !aRoot)
      return;

    var searchTargets = aDefinition.searchTargets || 'all';
    var searchFolders;
    if (!aModification) {
      searchFolders = this.getSearchFolders(aRoot, searchTargets.split(/[,\s]+/));
      if (!searchFolders)
        return;
    }

    var name = 'autogen-' + aDefinition.label;

    var isCreation = false;
    var isModified = false;
    var virtualFolder;
    try {
      virtualFolder = aRoot.getChildNamed(name);
    } catch(e) {
      try {
        virtualFolder = aRoot.getChildNamed(aDefinition.label);
        if (!(virtualFolder.flags & Ci.nsMsgFolderFlags.Virtual))
          virtualFolder = null;
      } catch(e) {
        // folder not found!
      }
    }
    if (!virtualFolder) {
      isCreation = true;
      isModified = true;
      virtualFolder = aRoot.addSubfolder(name);
      virtualFolder.setFlag(Ci.nsMsgFolderFlags.Virtual);
    }

    // We always have to set prettyName because it is not saved.
    virtualFolder.prettyName = aDefinition.label;

    var wrapper = VirtualFolderHelper.wrapVirtualFolder(virtualFolder);

    var conditions = this.buildSearchCondition(aDefinition);
    if (wrapper.searchString != conditions) {
      wrapper.searchString = conditions;
      isModified = true;
    }
    var currentSearchFolders = wrapper.searchFolders.map(function(aFolder) {
      return aFolder.URI
    }).join('|');
    if (aModification) {
      searchFolders = currentSearchFolders.split('|');
      if (aModification.addedFolder) {
        let index = searchFolders.indexOf(aModification.addedFolder);
        if (index < 0) {
          searchFolders.push(aModification.addedFolder);
        }
      }
      if (aModification.removedFolder) {
        let index = searchFolders.indexOf(aModification.removedFolder);
        if (index > -1) {
          searchFolders.splice(index, 1);
        }
      }
      searchFolders = searchFolders.join('|');
    }
    if (currentSearchFolders != searchFolders) {
      wrapper.searchFolders = searchFolders;
      isModified = true;
    }
    if (isCreation) {
      wrapper.onlineSearch = false;
    }

    if (!isModified)
      return;

    wrapper.cleanUpMessageDatabase();
    if (isCreation) {
      virtualFolder.msgDatabase.Close(true);
      aRoot.NotifyItemAdded(virtualFolder);
    }
    MailServices.accounts.saveVirtualFolders();
  },
  getSearchFolders: function(aRoot, aKeys) {
    var flags = {};
    Object.keys(Ci.nsMsgFolderFlags).forEach(function(aKey) {
      flags[aKey.toLowerCase()] = Ci.nsMsgFolderFlags[aKey];
    });
    var folders = [];
    aKeys.some(function(aKey) {
      aKey = aKey.toLowerCase();
      if (aKey == 'all') {
        folders = this.getDescendants(aRoot).map(function(aFolder) {
          return aFolder.URI;
        });
        return true;
      }
      if (typeof flags[aKey] == 'number') {
        let folder = aRoot.getFolderWithFlags(flags[aKey]);
        if (folder)
          folders.push(folder.URI);
      }
      return false;
    }, this);
    try {
      if (folders.length > 0)
        folders.unshift(aRoot.URI);
    } catch(e) {
    }
    return folders.join('|');
  },
  buildSearchCondition: function(aDefinition) {
    if (aDefinition.conditions)
      return this.UnicodeToUTF8(aDefinition.conditions);

    if (aDefinition.subjectPrefix)
      return 'AND (subject,begins with,' + this.UnicodeToUTF8(aDefinition.subjectPrefix) + ')';

    return 'AND (subject,is,' + this.UnicodeToUTF8(aDefinition.subject) + ')';
  },
  UnicodeToUTF8: function(aString) {
    return unescape(encodeURIComponent(aString));
  }
};
<|endoftext|>


<commit_before>var extend = require('extend');
var util = require('util');
var crypto = require('crypto-browserify');
var bignum = require('browserify-bignum');

var private = {}, self = null,
	library = null, modules = null;

private.delegates = {};
private.u_delegates = {};

function Delegates(cb, _library) {
	self = this;
	library = _library;

	cb(null, self);
}

function applyDiff(source, diff) {
	var res = source ? source.slice() : [];

	for (var i = 0; i < diff.length; i++) {
		var math = diff[i][0];
		var val = diff[i].slice(1);

		if (math == "+") {
			res = res || [];

			var index = -1;
			if (res) {
				index = res.indexOf(val);
			}
			if (index != -1) {
				return false;
			}

			res.push(val);
		}
		if (math == "-") {
			var index = -1;
			if (res) {
				index = res.indexOf(val);
			}
			if (index == -1) {
				return false;
			}
			res.splice(index, 1);
			if (!res.length) {
				res = null;
			}
		}
	}
	return res;
}

Delegates.prototype.getDelegates = function (height, cb, scope) {
	var tmpHeight = Object.keys((scope || private).delegates).reverse().find(function (currentHeight) {
		return height > currentHeight;
	});
	cb(null, (scope || private).delegates[tmpHeight]);
}

private.mergeDelegates = function (delegates, list, height, cb, scope) {
	var lastHeight = Math.max.apply(null, Object.keys(delegates));

	if (delegates[height]) {
		return ("Delegate list exists")
	}

	delegates[height] = applyDiff(delegates[lastHeight], list);

	cb(null, delegates[height]);
}

private.undoLast = function (delegates, cb, scope) {
	var lastHeight = Math.max.apply(null, Object.keys(delegates));
	if (lastHeight == 1) {
		return ("Genesis block is readonly")
	}
	delete delegates[lastHeight];
	lastHeight = Math.max.apply(null, Object.keys(delegates));

	cb(null, delegates[lastHeight]);
}

Delegates.prototype.mergeDelegates = function (list, height, cb, scope) {
	var delegates = (scope || private).delegates;
	private.mergeDelegates(delegates, list, height, function (err, list) {
		if (!err) {
			!scope && library.bus.message("delegates", list);
		}
		cb(err);
	}, scope);
}

Delegates.prototype.mergeU_Delegates = function (list, height, cb, scope) {
	var delegates = (scope || private).u_delegates;
	private.mergeDelegates(delegates, list, height, cb, scope);
}

Delegates.prototype.undoLast = function (cb, scope) {
	var delegates = (scope || private).delegates;

	private.undoLast(delegates, function (err, list) {
		if (!err) {
			!scope && library.bus.message("delegates", list);
		}
		cb(err);
	}, scope);
}

Delegates.prototype.undoU_Last = function (cb, scope) {
	var delegates = (scope || private).u_delegates;

	private.undoLast(delegates, cb, scope);
}

Delegates.prototype.onBind = function (_modules) {
	modules = _modules;
}

module.exports = Delegates;<commit_msg>update delegates<commit_after>var extend = require('extend');
var util = require('util');
var crypto = require('crypto-browserify');
var bignum = require('browserify-bignum');

var private = {}, self = null,
	library = null, modules = null;

private.delegates = {};
private.u_delegates = {};

function Delegates(cb, _library) {
	self = this;
	library = _library;

	cb(null, self);
}

function applyDiff(source, diff) {
	var res = source ? source.slice() : [];

	for (var i = 0; i < diff.length; i++) {
		var math = diff[i][0];
		var val = diff[i].slice(1);

		if (math == "+") {
			res = res || [];

			var index = -1;
			if (res) {
				index = res.indexOf(val);
			}
			if (index != -1) {
				return false;
			}

			res.push(val);
		}
		if (math == "-") {
			var index = -1;
			if (res) {
				index = res.indexOf(val);
			}
			if (index == -1) {
				return false;
			}
			res.splice(index, 1);
			if (!res.length) {
				res = null;
			}
		}
	}
	return res;
}

Delegates.prototype.getDelegates = function (height, cb, scope) {
	var tmpHeight = Object.keys((scope || private).delegates).reverse().find(function (currentHeight) {
		return height >= currentHeight;
	});
	cb(null, (scope || private).delegates[tmpHeight]);
}

private.mergeDelegates = function (delegates, list, height, cb, scope) {
	var lastHeight = Math.max.apply(null, Object.keys(delegates));

	if (delegates[height]) {
		return ("Delegate list exists")
	}

	delegates[height] = applyDiff(delegates[lastHeight], list);

	cb(null, delegates[height]);
}

private.undoLast = function (delegates, cb, scope) {
	var lastHeight = Math.max.apply(null, Object.keys(delegates));
	if (lastHeight == 1) {
		return ("Genesis block is readonly")
	}
	delete delegates[lastHeight];
	lastHeight = Math.max.apply(null, Object.keys(delegates));

	cb(null, delegates[lastHeight]);
}

Delegates.prototype.mergeDelegates = function (list, height, cb, scope) {
	var delegates = (scope || private).delegates;
	private.mergeDelegates(delegates, list, height, function (err, list) {
		if (!err) {
			!scope && library.bus.message("delegates", list);
		}
		cb(err);
	}, scope);
}

Delegates.prototype.mergeU_Delegates = function (list, height, cb, scope) {
	var delegates = (scope || private).u_delegates;
	private.mergeDelegates(delegates, list, height, cb, scope);
}

Delegates.prototype.undoLast = function (cb, scope) {
	var delegates = (scope || private).delegates;

	private.undoLast(delegates, function (err, list) {
		if (!err) {
			!scope && library.bus.message("delegates", list);
		}
		cb(err);
	}, scope);
}

Delegates.prototype.undoU_Last = function (cb, scope) {
	var delegates = (scope || private).u_delegates;

	private.undoLast(delegates, cb, scope);
}

Delegates.prototype.onBind = function (_modules) {
	modules = _modules;
}

module.exports = Delegates;<|endoftext|>


<commit_before>function jsonParse (text) {
  try {
    return JSON.parse(text)
  } catch (err) {
    return null
  }
}

function extractJson (line, source) {
  var parsed = {}
  if (/^\[{0,1}\{.*\}]{0,1}$/.test(line)) {
    parsed = jsonParse(line)
    if (!parsed) {
      return null
    }
    return parsed
  }
}

function filterCloudFoundryMessage (data, context) {
  if (data) {
    data._type = context.sourceName.replace('_' + context.index, '')
    data.logSource = ('' + data.logSource).replace('_' + context.index, '')
    if (!data['@timestamp']) {
      data['@timestamp'] = new Date()
    }
    if (data.message) {
      data.json = extractJson(data.message)
    }
  }
  return data
}

function parseCloudFoundryTags (tags) {
  console.log(tags)
  try {
    const parsedTags2dArr = tags
      .split(' ')
      .map(t => t.replace(/"/g, '').split('='))

    const parsedTags = parsedTags2dArr
      .map(a => {
        const key = a.shift()
        const val = a.shift()
        const obj = {}
        obj[key] = val
        return obj
      })
      .reduce((acc, o) => {
        const key = Object.keys(o).pop()
        acc[key] = o[key]

        return acc
      }, {})

    return parsedTags
  } catch (error) {
    console.log(error)
    return tags
  }
}

module.exports = function (context, config, eventEmitter, log, callback) {
  try {
    const filteredLog = filterCloudFoundryMessage(log, context)
    const json = filteredLog.json
    const message = filteredLog.message
    delete filteredLog.json
    delete filteredLog.message
    const tags = parseCloudFoundryTags(filteredLog.tags)
    delete filteredLog.tags

    const parsedLog = {
      message,
      ...json,
      ...filteredLog,
      ...tags
    }

    return callback(null, parsedLog)
  } catch (err) {
    console.log(err)
    return callback(null, log)
  }
}
<commit_msg>cleaning up cf format<commit_after>function jsonParse (text) {
  try {
    return JSON.parse(text)
  } catch (err) {
    return null
  }
}

function extractJson (line, source) {
  var parsed = {}
  if (/^\[{0,1}\{.*\}]{0,1}$/.test(line)) {
    parsed = jsonParse(line)
    if (!parsed) {
      return null
    }
    return parsed
  }
}

function filterCloudFoundryMessage (data, context) {
  if (data) {
    data._type = context.sourceName.replace('_' + context.index, '')
    data.logSource = ('' + data.logSource).replace('_' + context.index, '')
    if (!data['@timestamp']) {
      data['@timestamp'] = new Date()
    }
    if (data.message) {
      data.json = extractJson(data.message)
    }
  }
  return data
}

function parseCloudFoundryTags (tags) {
  console.log(tags)
  try {
    const parsedTags2dArr = tags
      .split(' ')
      .map(t => t.replace(/"/g, '').split('='))

    const parsedTags = parsedTags2dArr
      .map(a => {
        const obj = {}
        obj[a[0]] = a[1]
        return obj
      })
      .reduce((acc, o) => {
        const key = Object.keys(o).pop()
        acc[key] = o[key]

        return acc
      }, {})

    return parsedTags
  } catch (error) {
    console.log(error)
    return tags
  }
}

module.exports = function (context, config, eventEmitter, log, callback) {
  try {
    const filteredLog = filterCloudFoundryMessage(log, context)
    const json = filteredLog.json
    const message = filteredLog.message
    delete filteredLog.json
    delete filteredLog.message
    const tags = parseCloudFoundryTags(filteredLog.tags)
    delete filteredLog.tags

    const parsedLog = {
      message,
      ...json,
      ...filteredLog,
      ...tags
    }

    return callback(null, parsedLog)
  } catch (err) {
    console.log(err)
    return callback(null, log)
  }
}
<|endoftext|>


<commit_before>angular.module('foundation.common.animations', ['ngAnimate']);

angular.module('foundation.common.animations')
  .animation('.ui-animation', ['$state', '$rootScope', function($state, $rootScope) {
    var events = ['webkitAnimationEnd', 'mozAnimationEnd', 'MSAnimationEnd', 'oanimationend', 'animationend',
                  'webkitTransitionEnd', 'otransitionend', 'transitionend'];

    var parentStyle = 'position-absolute';

    return {
      enter: function(element, done) {
        var scope = element.scope();
        if(scope.vars && scope.vars.animationIn) {

          var animationIn = scope.vars.animationIn;
          var animationOut = scope.vars.animationOut || '';
          var initial = 'ng-enter';
          var activate = 'ng-enter-active';

          //reset possible failed animations and bugs
          element.parent().addClass(parentStyle);
          element.removeClass(activate + ' ' + initial + ' ' + animationIn + ' ' + animationOut);
          element[0].style.transitionDuration = 0;

          //start animation
          element.addClass(animationIn);
          element.addClass(initial);

          $rootScope.$digest();

          element[0].style.transitionDuration = '';
          element.addClass(activate);

          element.one(events.join(' '), function() {
            //cleanup
            element.parent().removeClass(parentStyle);
            element.removeClass(activate + ' ' + initial + ' ' + animationIn + ' ' + animationOut);
            done();
          });
        } else {
          done();
        }

        return function(isCancelled) {

        };
      },
      leave: function(element, done) {
        var scope = element.scope();

        if(scope.vars && scope.vars.animationOut) {
          var animationIn = scope.vars.animationIn || '';
          var animationOut = scope.vars.animationOut;
          var initial = 'ng-leave';
          var activate = 'ng-leave-active';

          element.removeClass(activate + ' ' + initial + ' ' + animationIn + ' ' + animationOut);
          element[0].style.transitionDuration = 0;

          //start animation
          element.addClass(animationOut);
          element.addClass(initial);

          $rootScope.$digest();

          element[0].style.transitionDuration = '';
          element.addClass(activate);

          element.one(events.join(' '), function() {
            //cleanup
            element.removeClass(activate + ' ' + initial + ' ' + animationIn + ' ' + animationOut);
            element.parent().removeClass(parentStyle);
            done();
          });

        } else {
          done();
        }

        return function(isCancelled) {

        };
       }
    };
}]);
<commit_msg>Animation hard timeout<commit_after>angular.module('foundation.common.animations', ['ngAnimate']);

angular.module('foundation.common.animations')
  .animation('.ui-animation', ['$state', '$rootScope', function($state, $rootScope) {
    var events = ['webkitAnimationEnd', 'mozAnimationEnd', 'MSAnimationEnd', 'oanimationend', 'animationend',
                  'webkitTransitionEnd', 'otransitionend', 'transitionend'];

    var parentStyle = 'position-absolute';

    return {
      enter: function(element, done) {
        var scope = element.scope();
        if(scope.vars && scope.vars.animationIn) {

          var animationIn  = scope.vars.animationIn;
          var animationOut = scope.vars.animationOut || '';
          var initial  = 'ng-enter';
          var activate = 'ng-enter-active';
          var timedOut = true;

          //reset possible failed animations and bugs
          element.parent().addClass(parentStyle);
          element.removeClass(activate + ' ' + initial + ' ' + animationIn + ' ' + animationOut);
          element[0].style.transitionDuration = 0;

          //start animation
          element.addClass(animationIn);
          element.addClass(initial);

          $rootScope.$digest();

          element[0].style.transitionDuration = '';
          element.addClass(activate);

          var finishAnimation = function() {
            element.parent().removeClass(parentStyle);
            element.removeClass(activate + ' ' + initial + ' ' + animationIn + ' ' + animationOut);
            timedOut = false;
            done();
          }

          element.one(events.join(' '), function() {
            finishAnimation();
          });

          setTimeout(function() {
            if (timedOut) {
              finishAnimation();
            }
          }, 3000);

        } else {
          done();
        }

        return function(isCancelled) {

        };
      },
      leave: function(element, done) {
        var scope = element.scope();

        if(scope.vars && scope.vars.animationOut) {
          var animationIn  = scope.vars.animationIn || '';
          var animationOut = scope.vars.animationOut;
          var initial  = 'ng-leave';
          var activate = 'ng-leave-active';
          var timedOut = true;

          element.removeClass(activate + ' ' + initial + ' ' + animationIn + ' ' + animationOut);
          element[0].style.transitionDuration = 0;

          //start animation
          element.addClass(animationOut);
          element.addClass(initial);

          $rootScope.$digest();

          element[0].style.transitionDuration = '';
          element.addClass(activate);

          var finishAnimation = function() {
            element.parent().removeClass(parentStyle);
            element.removeClass(activate + ' ' + initial + ' ' + animationIn + ' ' + animationOut);
            timedOut = false;
            done();
          }

          element.one(events.join(' '), function() {
            finishAnimation();
          });

          setTimeout(function() {
            if (timedOut) {
              finishAnimation();
            }
          }, 3000);

        } else {
          done();
        }

        return function(isCancelled) {

        };
       }
    };
}]);
<|endoftext|>


<commit_before>angular.module('foundation.common.animations', ['ngAnimate']);

angular.module('foundation.common.animations')
  .animation('.ui-animation', ['$state', function($state) {
    var events = ['webkitAnimationEnd', 'mozAnimationEnd', 'MSAnimationEnd', 'oanimationend', 'animationend',
                  'webkitTransitionEnd', 'otransitionend', 'transitionend'];
    return {
      enter: function(element, done) {
        var scope = element.scope();

        if(scope.vars && scope.vars.animationIn) {
          animationIn = scope.vars.animationIn;
          animationOut = scope.vars.animationOut || '';

          //reset possible failed animations and bugs
          element.removeClass(animationIn + ' ' + animationOut);

          element.addClass(animationOut);
          element.addClass('animated');

          element.one(events.join(' '), function(){
            //cleanup
            element.removeClass(animationIn + ' ' + animationOut);
            done();
          });
        } else {
          done();
        }

        return function(isCancelled) {

        }
      },
      leave: function(element, done) {
        var scope = element.scope();

        if(scope.vars && scope.vars.animationOut) {
          animationIn = scope.vars.animationIn || '';
          animationOut = scope.vars.animationOut;

          //reset possible failed animations and bugs
          element.removeClass(animationIn + ' ' + animationOut);

          element.addClass(animationOut);
          element.addClass('animated');

          element.one(events.join(' '), function(){
            //cleanup
            element.removeClass(animationIn + ' ' + animationOut);
            done();
          });
        } else {
          done();
        }

          return function(isCancelled) {

          }
       }
    }

  }]);
<commit_msg>Fix typo in animations library that prevented in animations from playing<commit_after>angular.module('foundation.common.animations', ['ngAnimate']);

angular.module('foundation.common.animations')
  .animation('.ui-animation', ['$state', function($state) {
    var events = ['webkitAnimationEnd', 'mozAnimationEnd', 'MSAnimationEnd', 'oanimationend', 'animationend',
                  'webkitTransitionEnd', 'otransitionend', 'transitionend'];
    return {
      enter: function(element, done) {
        var scope = element.scope();

        if(scope.vars && scope.vars.animationIn) {
          animationIn = scope.vars.animationIn;
          animationOut = scope.vars.animationOut || '';

          //reset possible failed animations and bugs
          element.removeClass(animationIn + ' ' + animationOut);

          element.addClass(animationIn);
          element.addClass('animated');

          element.one(events.join(' '), function(){
            //cleanup
            element.removeClass(animationIn + ' ' + animationOut);
            done();
          });
        } else {
          done();
        }

        return function(isCancelled) {

        }
      },
      leave: function(element, done) {
        var scope = element.scope();

        if(scope.vars && scope.vars.animationOut) {
          animationIn = scope.vars.animationIn || '';
          animationOut = scope.vars.animationOut;

          //reset possible failed animations and bugs
          element.removeClass(animationIn + ' ' + animationOut);

          element.addClass(animationOut);
          element.addClass('animated');

          element.one(events.join(' '), function(){
            //cleanup
            element.removeClass(animationIn + ' ' + animationOut);
            done();
          });
        } else {
          done();
        }

          return function(isCancelled) {

          }
       }
    }

  }]);
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2014 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

MODEL({
  name: 'ModelFileDAO',
  package: 'foam.core.bootstrap',
  methods: {
    find: function (key, sink) {
      var X = this.X;
      var model = FOAM.lookup(key, X);

      var tag = this.X.document.createElement('script');
      tag.src = FOAM_BOOT_DIR + '../js/' + key.replace(/\./g, '/') + '.js';
      this.X.document.head.appendChild(tag);

      tag.onload = function() {
//        this.removeChild(tag);

        model = FOAM.lookup(key, X);
        if ( ! model ) {
          sink && sink.error && sink.error('Model load failed for: ', key);
          return;
        }
        sink && sink.put && sink.put(model);
      }.bind(this.X.document.head);
    }
  }
});

X.ModelDAO = X.foam.core.bootstrap.ModelFileDAO.create();
<commit_msg>ModelFileDAO: Changed find() to not duplicate script tags. Sinks will accumulate until the first load completes, then further requests go straight to FOAM.lookup<commit_after>/**
 * @license
 * Copyright 2014 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

MODEL({
  name: 'ModelFileDAO',
  package: 'foam.core.bootstrap',
  methods: {
    init: function() {
      this.SUPER();
      if ( ! this.X.modelFileDAO$RegisteredModels ) {
        this.X.set('modelFileDAO$RegisteredModels', {} );
      }
    },

    find: function (key, sink) {
      /* The find function will accumulate duplicate load attempts, chaining
      the given sinks together until the first onload completes. Subsequent
      load attempts will immediately put the item to the sink. */
      var X = this.X;
      var model = FOAM.lookup(key, X);
      if ( model ) {
        sink && sink.put && sink.put(model);
        return;
      }

      // If we've already seen this key, don't load it again
      if ( this.X.modelFileDAO$RegisteredModels[key] ) {
        var oldSink = this.X.modelFileDAO$RegisteredModels[key];
        // decorate the previous sink to chain it with the new one
        X.modelFileDAO$RegisteredModels[key] = {
          put: function(item) {
            oldSink && oldSink.put && oldSink.put(item);
            sink && sink.put && sink.put(item);
          },
          error: function(item) {
            oldSink && oldSink.error && oldSink.error(item);
            sink && sink.error && sink.error(item);
          }
        }
        return;
      }

      // It's a new key, so remember the sink and make a tag
      X.modelFileDAO$RegisteredModels[key] = sink;

      //var model = FOAM.lookup(key, X);
      var sourcePath = FOAM_BOOT_DIR + '../js/' + key.replace(/\./g, '/') + '.js';

      var tag = X.document.createElement('script');
      tag.src = sourcePath;
      X.document.head.appendChild(tag);

      tag.onload = function() {
        var itemSink = X.modelFileDAO$RegisteredModels[key];
        var model = FOAM.lookup(key, X);
        if ( ! model ) {
          console.warn('Model load failed for: ', key);
          itemSink && itemSink.error && itemSink.error('Model load failed for: ', key);
          return;
        }
        itemSink && itemSink.put && itemSink.put(model);
        // reset the sink we remembered, but don't undefine as we want to remember
        // the load is complete
        X.modelFileDAO$RegisteredModels[key] = {
            put: function() { console.warn("Attempted put into already finished onload: ", key); },
            error: function() { console.warn("Attempted error into already finished onload: ", key); }
          };
      }.bind(this.X.document.head);
    }
  }
});

X.ModelDAO = X.foam.core.bootstrap.ModelFileDAO.create();
<|endoftext|>


<commit_before>define(['./module', 'darkwallet'], function (controllers, DarkWallet) {
  'use strict';
  controllers.controller('ReceiveStealthCtrl', ['$scope', 'notify', function($scope, notify) {
  // function to receive stealth information
  $scope.receiveStealth = function() {
      notify.note("stealth", "initializing");
      notify.progress.start();

      var client = DarkWallet.getClient();
      var stealth_fetched = function(error, results) {
          if (error) {
              console.log("error on stealth");
              notify.error("stealth", error);
              //write_to_screen('ERROR: ' + error);
              return;
          }
          console.log("fetching stealth", results);
          var addresses;
          try {
              addresses = $scope.identity.wallet.processStealth(results);
          } catch (e) {
              notify.error("stealth", e.message);
              return;
          }
          if (addresses && addresses.length) {
              var walletService = DarkWallet.service.wallet;
              addresses.forEach(function(walletAddress) {
                  walletService.initAddress(walletAddress);
              })
              notify.success("stealth ok");
          } else {
              notify.success("stealth ok", "payments detected");
          }
      }
      client.fetch_stealth([0,0], stealth_fetched, 0);
  };

}]);
});
<commit_msg>fix stealth detected payments.<commit_after>define(['./module', 'darkwallet'], function (controllers, DarkWallet) {
  'use strict';
  controllers.controller('ReceiveStealthCtrl', ['$scope', 'notify', function($scope, notify) {
  // function to receive stealth information
  $scope.receiveStealth = function() {
      notify.note("stealth", "initializing");
      notify.progress.start();

      var client = DarkWallet.getClient();
      var stealth_fetched = function(error, results) {
          if (error) {
              console.log("error on stealth");
              notify.error("stealth", error);
              //write_to_screen('ERROR: ' + error);
              return;
          }
          console.log("fetching stealth", results);
          var addresses;
          try {
              addresses = $scope.identity.wallet.processStealth(results);
          } catch (e) {
              notify.error("stealth", e.message);
              return;
          }
          if (addresses && addresses.length) {
              var walletService = DarkWallet.service.wallet;
              addresses.forEach(function(walletAddress) {
                  walletService.initAddress(walletAddress);
              })
              notify.success("stealth ok", addresses.length + " payments detected");
          } else {
              notify.success("stealth ok");
          }
      }
      client.fetch_stealth([0,0], stealth_fetched, 0);
  };

}]);
});
<|endoftext|>


<commit_before>import fsp from 'fs-promise';
import path from 'path';
import R from 'ramda';

import XF from 'xod-func-tools';
import XP from 'xod-project';

import { loadLibs } from './loadLibs';
import { readDir, readJSON } from './read';
import { resolvePath, reassignIds, getPatchName } from './utils';

// :: String -> String -> Boolean
const basenameEquals = basename => R.compose(
  R.equals(basename),
  path.basename
);

// :: [String] -> String -> Boolean
const basenameAmong = basenames => R.compose(
  XF.isAmong(basenames),
  path.basename
);

// :: [String] -> String -> Boolean
const extAmong = extensions => R.compose(
  XF.isAmong(extensions),
  path.extname
);

// :: unpackedFile -> true|false
const isProject = R.pipe(
  R.prop('path'),
  path.basename,
  R.equals('project.xod')
);

// :: [Promise a] -> Promise a
const allPromises = promises => Promise.all(promises);

// :: project -> libs[]
const getProjectLibs = R.pipe(
  R.find(isProject),
  R.path(['content', 'libs'])
);

const readProjectMetaFile = projectFile => readJSON(projectFile)
  .then(R.assoc('path', path.dirname(projectFile)))
  .catch(err => ({ error: true, message: err.toString(), path: projectFile }));

// Returns a Promise of all project metas for given workspace path
export const getProjects = R.composeP(
  allPromises,
  R.map(readProjectMetaFile),
  R.filter(basenameEquals('project.xod')),
  readDir
);

// Returns a promise of filename / content pair for a given
// `filename` path relative to `dir`
// :: String -> String -> Promise (Pair String String)
const readImplFile = dir => filename =>
  fsp.readFile(path.resolve(dir, filename)).then(content => [
    filename,
    content,
  ]);

// Returns a map with filenames in keys and contents in values of
// all implementation source files in a directory given as argument
// :: String -> Promise (StrMap String)
const readImplFiles = dir => R.composeP(
  R.fromPairs,
  allPromises,
  R.map(readImplFile(dir)),
  R.filter(extAmong(['.c', '.cpp', '.h', '.inl', '.js'])),
  fsp.readdir
)(dir);

// :: String -> String -> Promise { path :: String, content :: Object, id :: String }
const readXodFile = projectPath => xodfile =>
  readJSON(xodfile)
    .then((data) => {
      const { base, dir } = path.parse(xodfile);

      const projectFolder = path.resolve(projectPath, '..');
      const patchPath = path.relative(projectFolder, xodfile);

      return readImplFiles(dir).then((impls) => {
        const getContent = base === 'project.xod'
          ? R.identity
          : R.compose(
            R.merge(R.__, { impls }),
            R.assoc('path', XP.getLocalPath(getPatchName(xodfile))),
            reassignIds
          );

        return XF.omitNilValues({
          path: `./${patchPath}`,
          content: getContent(data),
        });
      });
    });

export const loadProjectWithoutLibs = projectPath => R.composeP(
  allPromises,
  R.map(readXodFile(projectPath)),
  R.filter(basenameAmong([
    'project.xod',
    'patch.xodp',
  ])),
  readDir
)(projectPath);

export const loadProjectWithLibs = (projectPath, workspace, libDir = workspace) =>
  loadProjectWithoutLibs(resolvePath(path.resolve(workspace, projectPath)))
    .then(project => loadLibs(getProjectLibs(project), resolvePath(libDir))
      .then(libs => ({
        project,
        libs,
      }))
      .catch((err) => {
        throw Object.assign(err, {
          path: resolvePath(libDir),
          libs: getProjectLibs(project),
        });
      })
    );

export default {
  getProjects,
  loadProjectWithLibs,
  loadProjectWithoutLibs,
};
<commit_msg>tweak(xod-fs): tweak xod-project import<commit_after>import fsp from 'fs-promise';
import path from 'path';
import R from 'ramda';

import XF from 'xod-func-tools';
import * as XP from 'xod-project';

import { loadLibs } from './loadLibs';
import { readDir, readJSON } from './read';
import { resolvePath, reassignIds, getPatchName } from './utils';

// :: String -> String -> Boolean
const basenameEquals = basename => R.compose(
  R.equals(basename),
  path.basename
);

// :: [String] -> String -> Boolean
const basenameAmong = basenames => R.compose(
  XF.isAmong(basenames),
  path.basename
);

// :: [String] -> String -> Boolean
const extAmong = extensions => R.compose(
  XF.isAmong(extensions),
  path.extname
);

// :: unpackedFile -> true|false
const isProject = R.pipe(
  R.prop('path'),
  path.basename,
  R.equals('project.xod')
);

// :: [Promise a] -> Promise a
const allPromises = promises => Promise.all(promises);

// :: project -> libs[]
const getProjectLibs = R.pipe(
  R.find(isProject),
  R.path(['content', 'libs'])
);

const readProjectMetaFile = projectFile => readJSON(projectFile)
  .then(R.assoc('path', path.dirname(projectFile)))
  .catch(err => ({ error: true, message: err.toString(), path: projectFile }));

// Returns a Promise of all project metas for given workspace path
export const getProjects = R.composeP(
  allPromises,
  R.map(readProjectMetaFile),
  R.filter(basenameEquals('project.xod')),
  readDir
);

// Returns a promise of filename / content pair for a given
// `filename` path relative to `dir`
// :: String -> String -> Promise (Pair String String)
const readImplFile = dir => filename =>
  fsp.readFile(path.resolve(dir, filename)).then(content => [
    filename,
    content,
  ]);

// Returns a map with filenames in keys and contents in values of
// all implementation source files in a directory given as argument
// :: String -> Promise (StrMap String)
const readImplFiles = dir => R.composeP(
  R.fromPairs,
  allPromises,
  R.map(readImplFile(dir)),
  R.filter(extAmong(['.c', '.cpp', '.h', '.inl', '.js'])),
  fsp.readdir
)(dir);

// :: String -> String -> Promise { path :: String, content :: Object, id :: String }
const readXodFile = projectPath => xodfile =>
  readJSON(xodfile)
    .then((data) => {
      const { base, dir } = path.parse(xodfile);

      const projectFolder = path.resolve(projectPath, '..');
      const patchPath = path.relative(projectFolder, xodfile);

      return readImplFiles(dir).then((impls) => {
        const getContent = base === 'project.xod'
          ? R.identity
          : R.compose(
              R.merge(R.__, { impls }),
              R.assoc('path', XP.getLocalPath(getPatchName(xodfile))),
              reassignIds
            );

        return XF.omitNilValues({
          path: `./${patchPath}`,
          content: getContent(data),
        });
      });
    });

export const loadProjectWithoutLibs = projectPath => R.composeP(
  allPromises,
  R.map(readXodFile(projectPath)),
  R.filter(basenameAmong([
    'project.xod',
    'patch.xodp',
  ])),
  readDir
)(projectPath);

export const loadProjectWithLibs = (projectPath, workspace, libDir = workspace) =>
  loadProjectWithoutLibs(resolvePath(path.resolve(workspace, projectPath)))
    .then(project => loadLibs(getProjectLibs(project), resolvePath(libDir))
      .then(libs => ({
        project,
        libs,
      }))
      .catch((err) => {
        throw Object.assign(err, {
          path: resolvePath(libDir),
          libs: getProjectLibs(project),
        });
      })
    );

export default {
  getProjects,
  loadProjectWithLibs,
  loadProjectWithoutLibs,
};
<|endoftext|>


<commit_before>/*
 *   Copyright 2014 OSBI Ltd
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

/**
 * The delete query confirmation dialog
 */
var MoveRepositoryObject = Modal.extend({
    type: "save",
    closeText: "Move",

    events: {
        'click': 'select_root_folder', /* select root folder */
        'click .dialog_footer a' : 'call',
        'click .query': 'select_name',
        'dblclick .query': 'open_query',
        'click li.folder': 'toggle_folder',
        'keyup .search_file' : 'search_file',
        'click .cancel_search' : 'cancel_search',
        'click .export_btn' : 'export_zip',
        'change .file' : 'select_file'
    },

    buttons: [
        { id: "test", text: "Move", method: "open_query" },
        { text: "Cancel", method: "close" }
    ],

    initialize: function(args) {
        // Append events
        var self = this;
        var name = "";
        this.movefolder = args.query;
        this.message =  "

Please select a folder.....

Loading....
" +
            "
" +
            '
Search:  ' +
            ' <input type="text" class="search_file"></input>
';

        if (Settings.ALLOW_IMPORT_EXPORT) {
            this.message += "  Import or Export Files for Folder:  < Select Folder... >" +
                "   <input type='submit' value='Export' class='export_btn' disabled />

" +
                "
<form id='importForm' target='_blank' method='POST' enctype='multipart/form-data'>" +
                "<input type='hidden' name='directory' class='directory'/>" +
                "<input type='file' name='file' class='file'/>" +
                "<input type='submit' value='Import' class='import_btn' disabled />" +
                "</form>";
        }
        _.extend(this.options, {
            title: "Move"
        });

        this.selected_folder = null;

        // Initialize repository
        this.repository = new Repository({}, { dialog: this });

        this.bind( 'open', function( ) {
            var height = ( $( "body" ).height() / 2 ) + ( $( "body" ).height() / 6 );
            if( height > 420 ) {
                height = 420;
            }
            $(this.el).find('.RepositoryObjects').height( height );
            $(this.el).dialog( 'option', 'position', 'center' );
            $(this.el).parents('.ui-dialog').css({ width: "550px" });
            $(this.el).find('.dialog_footer').find('a[href="#open_query"]').hide();

            self.repository.fetch( );
        } );

        // Maintain `this`
        _.bindAll( this, "populate", "toggle_folder", "select_name", "select_file", "select_folder", "open_query");

    },
    populate: function( repository ) {
        var self = this;
        $( this.el ).find( '.RepositoryObjects' ).html(
            _.template( $( '#template-repository-objects' ).html( ) )( {
                repoObjects: repository
            } )
        );

        self.queries = {};
        function getQueries( entries ) {
            _.forEach( entries, function( entry ) {
                if(entry.type === 'FOLDER') {
                    self.queries[ entry.path ] = entry;
                //}
                //if( entry.type === 'FOLDER' ) {
                    getQueries( entry.repoObjects );
                }
            } );
        }
        getQueries( repository );
    },

    select_root_folder: function( event ) {
        var isNameInputField = $( event.target ).attr( 'name' ) === 'name';
        if( !isNameInputField ) {
            this.unselect_current_selected_folder( );
        }
    },
    toggle_folder: function( event ) {
        var $target = $( event.currentTarget );
        this.unselect_current_selected_folder( );
        $target.children('.folder_row').addClass( 'selected' );
        var $queries = $target.children( '.folder_content' );
        var isClosed = $target.children( '.folder_row' ).find('.sprite').hasClass( 'collapsed' );
        if( isClosed ) {
            $target.children( '.folder_row' ).find('.sprite').removeClass( 'collapsed' );
            $queries.removeClass( 'hide' );
        } else {
            $target.children( '.folder_row' ).find('.sprite').addClass( 'collapsed' );
            $queries.addClass( 'hide' );
        }

        this.select_folder();
        this.select_name(event);
        return false;
    },

    select_name: function( event ) {
        var $currentTarget = $( event.currentTarget );
        this.unselect_current_selected_folder( );
        $currentTarget.parent( ).parent( ).has( '.folder' ).children('.folder_row').addClass( 'selected' );
        var name = $currentTarget.find( 'a' ).attr('href');
        name = name.replace('#','');
        $(this.el).find('.query_name').html( name );
        $(this.el).find('.dialog_footer').find('a[href="#open_query"]').show();
        this.select_folder();
        return false;
    },

    unselect_current_selected_folder: function( ) {
        $( this.el ).find( '.selected' ).removeClass( 'selected' );
    },
    select_folder: function() {
        var foldersSelected = $( this.el ).find( '.selected' );
        var file = foldersSelected.length > 0 ? foldersSelected.children('a').attr('href').replace('#','') : null;
        if (typeof file != "undefined" && file !== null && file !== "") {
            var form = $('#importForm');
            form.find('.directory').val(file);
            var url = Settings.REST_URL + (new RepositoryZipExport()).url() + "upload";
            form.attr('action', url);
            $(this.el).find('.zip_folder').text(file);
            this.selected_folder = file;
            $(this.el).find('.export_btn, .import_btn').removeAttr('disabled');
            this.select_file();
        } else {
            $(this.el).find('.import_btn, .export_btn').attr('disabled', 'true');
        }

    },

    select_file: function() {
        var form = $('#importForm');
        var filename = form.find('.file').val();
        if (typeof filename != "undefined" && filename !== "" && filename !== null && this.selected_folder !== null) {
            $(this.el).find('.import_btn').removeAttr('disabled');
        } else {
            $(this.el).find('.import_btn').attr('disabled', 'true');
        }
    },

    open_query: function(event) {
        // Save the name for future reference
        var $currentTarget = $( event.currentTarget );
        var file = $(this.el).find('.query_name').html();
        if ($currentTarget.hasClass('query')) {
            file = $currentTarget.find( 'a' ).attr('href').replace('#','');
        }

        /*var selected_query = new SavedQuery({ file: file });
        this.close();
        Saiku.ui.block("Opening query...");
        var item = this.queries[file];
        var params = _.extend({
            file: file,
            formatter: Settings.CELLSET_FORMATTER
        }, Settings.PARAMS);

        var query = new Query(params,{ name: file  });
        var tab = Saiku.tabs.add(new Workspace({ query: query, item: item }));
        */

        var form_data = new FormData();
        form_data.append('source', this.movefolder.get("file"));
        form_data.append('target', file);

        var picture_entity = new MoveObject;
        picture_entity.save(null, {
            data: form_data,
            contentType: false,
            processData: false,
        });

        event.preventDefault();
        return false;
    },

});

var MoveObject = Backbone.Model.extend({
    url: function(){
    return "api/repository/resource/move";
}
});<commit_msg>SKU-1116 #comment fix move function<commit_after>/*
 *   Copyright 2014 OSBI Ltd
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

/**
 * The delete query confirmation dialog
 */
var MoveRepositoryObject = Modal.extend({
    type: "save",
    closeText: "Move",

    events: {
        'click': 'select_root_folder', /* select root folder */
        'click .dialog_footer a' : 'call',
        'click .query': 'select_name',
        'dblclick .query': 'open_query',
        'click li.folder': 'toggle_folder',
        'keyup .search_file' : 'search_file',
        'click .cancel_search' : 'cancel_search',
        'click .export_btn' : 'export_zip',
        'change .file' : 'select_file'
    },

    buttons: [
        { id: "test", text: "Move", method: "open_query" },
        { text: "Cancel", method: "close" }
    ],

    initialize: function(args) {
        // Append events
        var self = this;
        var name = "";
        this.movefolder = args.query;
        this.success = args.success;

        this.message =  "

Please select a folder.....

Loading....
" +
            "
" +
            '
Search:  ' +
            ' <input type="text" class="search_file"></input>
';

        _.extend(this.options, {
            title: "Move"
        });

        this.selected_folder = null;

        // Initialize repository
        this.repository = new Repository({}, { dialog: this });

        this.bind( 'open', function( ) {
            var height = ( $( "body" ).height() / 2 ) + ( $( "body" ).height() / 6 );
            if( height > 420 ) {
                height = 420;
            }
            $(this.el).find('.RepositoryObjects').height( height );
            $(this.el).dialog( 'option', 'position', 'center' );
            $(this.el).parents('.ui-dialog').css({ width: "550px" });
            $(this.el).find('.dialog_footer').find('a[href="#open_query"]').hide();

            self.repository.fetch( );
        } );

        // Maintain `this`
        _.bindAll( this, "populate", "toggle_folder", "select_name", "select_file", "select_folder", "open_query");

    },
    populate: function( repository ) {
        var self = this;
        $( this.el ).find( '.RepositoryObjects' ).html(
            _.template( $( '#template-repository-objects' ).html( ) )( {
                repoObjects: repository
            } )
        );

        self.queries = {};
        function getQueries( entries ) {
            _.forEach( entries, function( entry ) {
                if(entry.type === 'FOLDER') {
                    self.queries[ entry.path ] = entry;
                //}
                //if( entry.type === 'FOLDER' ) {
                    getQueries( entry.repoObjects );
                }
            } );
        }
        getQueries( repository );
    },

    select_root_folder: function( event ) {
        var isNameInputField = $( event.target ).attr( 'name' ) === 'name';
        if( !isNameInputField ) {
            this.unselect_current_selected_folder( );
        }
    },
    toggle_folder: function( event ) {
        var $target = $( event.currentTarget );
        this.unselect_current_selected_folder( );
        $target.children('.folder_row').addClass( 'selected' );
        var $queries = $target.children( '.folder_content' );
        var isClosed = $target.children( '.folder_row' ).find('.sprite').hasClass( 'collapsed' );
        if( isClosed ) {
            $target.children( '.folder_row' ).find('.sprite').removeClass( 'collapsed' );
            $queries.removeClass( 'hide' );
        } else {
            $target.children( '.folder_row' ).find('.sprite').addClass( 'collapsed' );
            $queries.addClass( 'hide' );
        }

        this.select_folder();
        this.select_name(event);
        return false;
    },

    select_name: function( event ) {
        var $currentTarget = $( event.currentTarget );
        this.unselect_current_selected_folder( );
        $currentTarget.parent( ).parent( ).has( '.folder' ).children('.folder_row').addClass( 'selected' );
        var name = $currentTarget.find( 'a' ).attr('href');
        name = name.replace('#','');
        $(this.el).find('.query_name').html( name );
        $(this.el).find('.dialog_footer').find('a[href="#open_query"]').show();
        this.select_folder();
        return false;
    },

    unselect_current_selected_folder: function( ) {
        $( this.el ).find( '.selected' ).removeClass( 'selected' );
    },
    select_folder: function() {
        var foldersSelected = $( this.el ).find( '.selected' );
        var file = foldersSelected.length > 0 ? foldersSelected.children('a').attr('href').replace('#','') : null;
        if (typeof file != "undefined" && file !== null && file !== "") {
            var form = $('#importForm');
            form.find('.directory').val(file);
            var url = Settings.REST_URL + (new RepositoryZipExport()).url() + "upload";
            form.attr('action', url);
            $(this.el).find('.zip_folder').text(file);
            this.selected_folder = file;
            $(this.el).find('.export_btn, .import_btn').removeAttr('disabled');
            this.select_file();
        } else {
            $(this.el).find('.import_btn, .export_btn').attr('disabled', 'true');
        }

    },

    select_file: function() {
        var form = $('#importForm');
        var filename = form.find('.file').val();
        if (typeof filename != "undefined" && filename !== "" && filename !== null && this.selected_folder !== null) {
            $(this.el).find('.import_btn').removeAttr('disabled');
        } else {
            $(this.el).find('.import_btn').attr('disabled', 'true');
        }
    },

    open_query: function(event) {
        // Save the name for future reference
        var $currentTarget = $( event.currentTarget );
        var file = $(this.el).find('.query_name').html();
        if ($currentTarget.hasClass('query')) {
            file = $currentTarget.find( 'a' ).attr('href').replace('#','');
        }

        var that= this;
        var picture_entity = new MoveObject;
        picture_entity.save({source: this.movefolder.get("file"), target: file}, {success: function(){
            that.close();
            that.success();
        }});

        event.preventDefault();
        return false;
    }

});

var MoveObject = Backbone.Model.extend({
    url: function(){
    return "api/repository/resource/move";
}
});<|endoftext|>


<commit_before>const {Sequelize, db} = require('../connection');

const User = db.define('users', {
  username: {
  },
  password: {
  },
  salt: {
  }
});

User.sync();

module.exports = User;<commit_msg>Flesh out user schema<commit_after>const crypto = require('crypto');
const {Sequelize, db} = require('../connection');

const User = db.define('users', {
  username: {
    type: Sequelize.STRING(40),
    allowNull: false,
    unique: true
  },
  password: {
    type: Sequelize.STRING(64),
    allowNull: false
  },
  salt: {
    type: Sequelize.STRING(64),
    allowNull: false
  }
});

User.sync();

module.exports = User;<|endoftext|>


<commit_before>var DS = require('./datasource.js');
var makeDataFrame = require('./dataframe.js').makeDataFrame;

module.exports.dataSource = DS.makeDataSource(
  'police-uk', // name

  'Some crime stats', // description

  function hasNewsSince(date) {
    // TODO use http://data.police.uk/api/crime-last-updated and compare date to
    // the returned timestamp
    var threshold = new Date('2014-05-01');
    return date > threshold;
  },

  function getData(from, to, lat, lng) {
    // let currDate be the 1st of current month if day <= 15, 1st of next month
    // otherwise
    var currDate = from;
    if (currDate.getDay() <= 15) {
      currDate.setDate(1);
    }
    else {
      currDate.setDate(32) // will go to 1st of next month
    }

    var crimeArray = new Array();
    var baseQuery =
        "http://data.police.uk/api/crimes-street/all-crime?lat=" + lat + "&lng=" + lng;

    while (currDate <= to) {
      var currMonth = currDate.getMonth() + 1; // months start at 0 ¬_¬
      currMonth = currMonth > 9 ? String(currMonth) : '0' + String(currMonth);
      var timeQuery = "&date=" + currDate.getFullYear() + "-" + currMonth;

      var data = crimeQuery(baseQuery + timeQuery);
      crimeArray.push(makeDataFrame(currDate, data));

      currDate.setMonth(currDate.getMonth() + 1); // go to the next dataframe
    }

    return crimeArray;
  }
);

function crimeQuery(path_json, crimeArrayIndex) {
  var monthCrimeStat = {};

  $.ajax({
    url: path_json,
    async: false,
    success: process(result)
  });

  function process(data) {
    for(var i = 0; i < data.length; i++) {
      if (!(data[i].category in crime)) {
        monthCrimeStat[data[i].category] = 0; // 1st crime seen for this category
      }
      monthCrimeStat[data[i].category] += 1;
    }
  };

  return monthCrimeStat;
}

<commit_msg>datasource-police-uk: add some serious description<commit_after>var DS = require('./datasource.js');
var makeDataFrame = require('./dataframe.js').makeDataFrame;

module.exports.dataSource = DS.makeDataSource(
  'police-uk', // name

  "#Police-uk\n" +
  "This sources return the monthly crime statistiques for a given localisation.\n" +

  "##getData parameters\n" +
  "In addition to the mandatory (from, to) time parameters, getData takes two\n" +
  "parameters for the localisation of the crime statistic: latitude and\n" +
  "longitude GPS coordinates.\n" +

  "##returned data format\n" +
  "The source returns a dataframe per month. The date attribute of the frame is\n" +
  "set to the first day of the month, and the data attribute carries an object.\n" +
  "This object has a field of each crime type (eg. `antisocial-behaviour` or \n" +
  "`bike-theft`), and the field's value is the monthly arrest count for this crime.",

  function hasNewsSince(date) {
    // TODO use http://data.police.uk/api/crime-last-updated and compare date to
    // the returned timestamp
    var threshold = new Date('2014-05-01');
    return date > threshold;
  },

  function getData(from, to, lat, lng) {
    // let currDate be the 1st of current month if day <= 15, 1st of next month
    // otherwise
    var currDate = from;
    if (currDate.getDay() <= 15) {
      currDate.setDate(1);
    }
    else {
      currDate.setDate(32) // will go to 1st of next month
    }

    var crimeArray = new Array();
    var baseQuery =
        "http://data.police.uk/api/crimes-street/all-crime?lat=" + lat + "&lng=" + lng;

    while (currDate <= to) {
      var currMonth = currDate.getMonth() + 1; // months start at 0 ¬_¬
      currMonth = currMonth > 9 ? String(currMonth) : '0' + String(currMonth);
      var timeQuery = "&date=" + currDate.getFullYear() + "-" + currMonth;

      var data = crimeQuery(baseQuery + timeQuery);
      crimeArray.push(makeDataFrame(currDate, data));

      currDate.setMonth(currDate.getMonth() + 1); // go to the next dataframe
    }

    return crimeArray;
  }
);

function crimeQuery(path_json, crimeArrayIndex) {
  var monthCrimeStat = {};

  $.ajax({
    url: path_json,
    async: false,
    success: process(result)
  });

  function process(data) {
    for(var i = 0; i < data.length; i++) {
      if (!(data[i].category in crime)) {
        monthCrimeStat[data[i].category] = 0; // 1st crime seen for this category
      }
      monthCrimeStat[data[i].category] += 1;
    }
  };

  return monthCrimeStat;
}

<|endoftext|>


<commit_before>/*
 * grunt-init-gebo-react-hai
 * https://github.com/RaphaelDeLaGhetto/grunt-init-gebo-react-hai
 * Daniel Bidulock
 *
 * Adapted from:
 *
 * grunt-init-commonjs
 * https://gruntjs.com/
 *
 * Copyright (c) 2013 "Cowboy" Ben Alman, contributors
 * Licensed under the MIT license.
 */

'use strict';

// Basic template description.
exports.description = 'Create a React gebo human-agent interface'

// Template-specific notes to be displayed before question prompts.
exports.notes = '';

// Template-specific notes to be displayed after question prompts.
exports.after = 'You should now install project dependencies with _npm ' +
  'install_. After that, you may execute project tasks with _grunt_. For ' +
  'more information about installing and configuring Grunt, please see ' +
  'the Getting Started guide:' +
  '\n\n' +
  'http://gruntjs.com/getting-started';

// Any existing file or directory matching this wildcard will cause a warning.
exports.warnOn = '*';

// The actual init template.
exports.template = function(grunt, init, done) {

  init.process({}, [
    // Prompt for these values.
    init.prompt('name'),
    init.prompt('description'),
    init.prompt('version', '0.0.0'),
    init.prompt('repository'),
    init.prompt('homepage'),
    init.prompt('bugs'),
    init.prompt('licenses'),
    init.prompt('author_name'),
    init.prompt('author_email'),
    init.prompt('author_url'),
    init.prompt('main', 'index.html')
  ], function(err, props) {
    props.scripts: {
        "test": "jest"
    };
    props.keywords = [];
    props.dependencies = {};
    props.devDependencies = {
        "grunt": "~0.4.x",
        "grunt-contrib-clean": "~0.6.x",
        "grunt-contrib-concat": "~0.5.x",
        "grunt-contrib-connect": "~0.8.x",
        "grunt-contrib-copy": "~0.6.x",
        "grunt-contrib-livereload": "~0.1.x",
        "grunt-contrib-uglify": "~0.6.x",
        "grunt-contrib-watch": "^0.6.x",
        "grunt-open": "^0.2.x",
        "grunt-react": "^0.9.x",
        "grunt-regarde": "~0.1.x",
        "grunt-usemin": "^2.4.x",
        "jest-cli": "^0.1.x",
        "jquery": "^2.1.x",
        "matchdep": "~0.3.x",
        "react": "^0.11.x",
        "react-tools": "^0.11.x"
    };
    props.jest = {
        "scriptPreprocessor": "<rootDir>/__tests__/preprocessor.js",
        "unmockedModulePathPatterns": [
          "<rootDir>/node_modules/react"
        ]
    };

    // Files to copy (and process).
    var files = init.filesToCopy(props);

    // Add properly-named license files.
    init.addLicenseFiles(files, props.licenses);

    // Actually copy (and process) files.
    init.copyAndProcess(files, props, { noProcess: 'assets/images/**' });

    // Generate package.json file.
    init.writePackageJSON('package.json', props);

    // All done!
    done();
  });
};
<commit_msg>Correct syntax<commit_after>/*
 * grunt-init-gebo-react-hai
 * https://github.com/RaphaelDeLaGhetto/grunt-init-gebo-react-hai
 * Daniel Bidulock
 *
 * Adapted from:
 *
 * grunt-init-commonjs
 * https://gruntjs.com/
 *
 * Copyright (c) 2013 "Cowboy" Ben Alman, contributors
 * Licensed under the MIT license.
 */

'use strict';

// Basic template description.
exports.description = 'Create a React gebo human-agent interface'

// Template-specific notes to be displayed before question prompts.
exports.notes = '';

// Template-specific notes to be displayed after question prompts.
exports.after = 'You should now install project dependencies with _npm ' +
  'install_. After that, you may execute project tasks with _grunt_. For ' +
  'more information about installing and configuring Grunt, please see ' +
  'the Getting Started guide:' +
  '\n\n' +
  'http://gruntjs.com/getting-started';

// Any existing file or directory matching this wildcard will cause a warning.
exports.warnOn = '*';

// The actual init template.
exports.template = function(grunt, init, done) {

  init.process({}, [
    // Prompt for these values.
    init.prompt('name'),
    init.prompt('description'),
    init.prompt('version', '0.0.0'),
    init.prompt('repository'),
    init.prompt('homepage'),
    init.prompt('bugs'),
    init.prompt('licenses'),
    init.prompt('author_name'),
    init.prompt('author_email'),
    init.prompt('author_url'),
    init.prompt('main', 'index.html')
  ], function(err, props) {
    props.scripts = {
        "test": "jest"
    };
    props.keywords = [];
    props.dependencies = {};
    props.devDependencies = {
        "grunt": "~0.4.x",
        "grunt-contrib-clean": "~0.6.x",
        "grunt-contrib-concat": "~0.5.x",
        "grunt-contrib-connect": "~0.8.x",
        "grunt-contrib-copy": "~0.6.x",
        "grunt-contrib-livereload": "~0.1.x",
        "grunt-contrib-uglify": "~0.6.x",
        "grunt-contrib-watch": "^0.6.x",
        "grunt-open": "^0.2.x",
        "grunt-react": "^0.9.x",
        "grunt-regarde": "~0.1.x",
        "grunt-usemin": "^2.4.x",
        "jest-cli": "^0.1.x",
        "jquery": "^2.1.x",
        "matchdep": "~0.3.x",
        "react": "^0.11.x",
        "react-tools": "^0.11.x"
    };
    props.jest = {
        "scriptPreprocessor": "<rootDir>/__tests__/preprocessor.js",
        "unmockedModulePathPatterns": [
          "<rootDir>/node_modules/react"
        ]
    };

    // Files to copy (and process).
    var files = init.filesToCopy(props);

    // Add properly-named license files.
    init.addLicenseFiles(files, props.licenses);

    // Actually copy (and process) files.
    init.copyAndProcess(files, props, { noProcess: 'assets/images/**' });

    // Generate package.json file.
    init.writePackageJSON('package.json', props);

    // All done!
    done();
  });
};
<|endoftext|>


<commit_before>import React from 'react';
import autoBind from 'react-autobind';
import Checkbox from 'js/components/common/checkbox';
import TextBox from 'js/components/common/textBox';
import ToggleSwitch from 'js/components/common/toggleSwitch';
import Select from 'react-select';
import {assign} from 'utils';
import {
  META_QUESTION_TYPES,
  SURVEY_DETAIL_ATTRIBUTES,
  FUNCTION_TYPE,
} from 'js/constants';
import bem from 'js/bem';
import envStore from 'js/envStore';

const AUDIT_SUPPORT_URL = 'audit_logging.html';
const RECORDING_SUPPORT_URL = 'recording-interviews.html';

const AUDIO_QUALITY_OPTIONS = [
  {value: 'quality=low', label: 'Low'},
  {value: 'quality=normal', label: 'Normal'},
  {value: 'quality=voice-only', label: 'Voice only'},
];
const ODK_DEFAULT_AUDIO_QUALITY = AUDIO_QUALITY_OPTIONS[2];

/**
 * @prop {object} survey
 * @prop {boolean} isDisabled whether everything is disabled
 * @prop {function} onChange
 */
export default class MetadataEditor extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      metaProperties: [],
    };
    autoBind(this);
  }

  componentDidMount() {
    this.rebuildState();
  }

  rebuildState() {
    const newState = {metaProperties: []};
    Object.keys(META_QUESTION_TYPES).forEach((metaType) => {
      const detail = this.getSurveyDetail(metaType);
      if (detail) {
        newState.metaProperties.push(assign({}, detail.attributes));
      }
    });
    this.setState(newState);
  }

  getMetaProperty(metaType) {
    return this.state.metaProperties.find(
      (metaProp) => metaProp.name === metaType
    );
  }

  getSurveyDetail(sdId) {
    return this.props.survey.surveyDetails.filter(
      (sd) => sd.attributes.name === sdId
    )[0];
  }

  onCheckboxChange(name, isChecked) {
    this.getSurveyDetail(name).set(
      SURVEY_DETAIL_ATTRIBUTES.value.id,
      isChecked
    );
    // Append parameters column with ODK_DEFAULT_AUDIO_QUALITY by default for
    // background-audio type
    if (isChecked && name === META_QUESTION_TYPES['background-audio']) {
      this.getSurveyDetail(name).set(
        SURVEY_DETAIL_ATTRIBUTES.parameters.id,
        ODK_DEFAULT_AUDIO_QUALITY.value
      );
    }

    this.rebuildState();
    if (typeof this.props.onChange === FUNCTION_TYPE.function.id) {
      this.props.onChange();
    }
  }

  onAuditParametersChange(newVal) {
    this.getSurveyDetail(META_QUESTION_TYPES.audit).set(
      SURVEY_DETAIL_ATTRIBUTES.parameters.id,
      newVal
    );
    this.rebuildState();
    if (typeof this.props.onChange === FUNCTION_TYPE.function.id) {
      this.props.onChange();
    }
  }

  isAuditEnabled() {
    const metaProp = this.getMetaProperty(META_QUESTION_TYPES.audit);
    return metaProp.value === true;
  }

  onBackgroundAudioParametersChange(newVal) {
    this.getSurveyDetail(META_QUESTION_TYPES['background-audio']).set(
      SURVEY_DETAIL_ATTRIBUTES.parameters.id,
      newVal.value
    );
    this.rebuildState();
    if (typeof this.props.onChange === FUNCTION_TYPE.function.id) {
      this.props.onChange();
    }
  }

  isBackgroundAudioEnabled() {
    const metaProp = this.getMetaProperty(
      META_QUESTION_TYPES['background-audio']
    );
    return metaProp.value === true;
  }

  getBackgroundAudioParameters() {
    const metaProp = this.getMetaProperty(
      META_QUESTION_TYPES['background-audio']
    );
    let foundParams = ODK_DEFAULT_AUDIO_QUALITY;
    if (metaProp.parameters) {
      foundParams = AUDIO_QUALITY_OPTIONS.find(
        (option) => option.value === metaProp.parameters
      );
    }
    return foundParams;
  }

  getAuditParameters() {
    const metaProp = this.getMetaProperty(META_QUESTION_TYPES.audit);
    return metaProp.parameters;
  }

  renderAuditInputLabel() {
    return (
      <React.Fragment>
        {t('Audit settings')}

        {envStore.isReady &&
          envStore.data.support_url && (
            <bem.TextBox__labelLink
              href={
                envStore.data.support_url + AUDIT_SUPPORT_URL
              }
              target='_blank'
            >

            </bem.TextBox__labelLink>
          )}
      </React.Fragment>
    );
  }

  renderBackgroundAudioLabel() {
    return (
      <React.Fragment>
        {t('Background audio')}

        {envStore.isReady &&
          envStore.data.support_url && (
            <bem.TextBox__labelLink
              // TODO update support article to include background-audio
              href={
                envStore.data.support_url +
                RECORDING_SUPPORT_URL
              }
              target='_blank'
            >

            </bem.TextBox__labelLink>
          )}
      </React.Fragment>
    );
  }

  render() {
    if (this.state.metaProperties.length === 0) {
      return null;
    }

    const leftColumn = [
      META_QUESTION_TYPES.start,
      META_QUESTION_TYPES.end,
      META_QUESTION_TYPES.today,
      META_QUESTION_TYPES.deviceid,
      META_QUESTION_TYPES.audit,
    ];
    const rightColumn = [
      META_QUESTION_TYPES.username,
      META_QUESTION_TYPES.phonenumber,
    ];

    let backgroundAudioProp = this.getMetaProperty(
      META_QUESTION_TYPES['background-audio']
    );

    return (
      <bem.FormBuilderMeta>
        <bem.FormBuilderMeta__columns>
          <bem.FormBuilderMeta__column>
            {leftColumn.map((metaType) => {
              const metaProp = this.getMetaProperty(metaType);
              return (
                <Checkbox
                  key={`meta-${metaProp.name}`}
                  label={metaProp.label}
                  checked={metaProp.value}
                  disabled={this.props.isDisabled}
                  onChange={this.onCheckboxChange.bind(this, metaProp.name)}
                />
              );
            })}
          </bem.FormBuilderMeta__column>

          <bem.FormBuilderMeta__column>
            {rightColumn.map((metaType) => {
              const metaProp = this.getMetaProperty(metaType);
              return (
                <Checkbox
                  key={`meta-${metaProp.name}`}
                  label={metaProp.label}
                  checked={metaProp.value}
                  disabled={this.props.isDisabled}
                  onChange={this.onCheckboxChange.bind(this, metaProp.name)}
                />
              );
            })}
          </bem.FormBuilderMeta__column>
        </bem.FormBuilderMeta__columns>

        {this.isAuditEnabled() && (
          <bem.FormBuilderMeta__row>
            <TextBox
              customModifiers='on-white'
              label={this.renderAuditInputLabel()}
              value={this.getAuditParameters()}
              disabled={this.props.isDisabled}
              onChange={this.onAuditParametersChange}
              placeholder={t('Enter audit settings here')}
            />
          </bem.FormBuilderMeta__row>
        )}

        <bem.FormBuilderMeta__row m='background-audio'>
          <bem.FormBuilderAside__header>
            {this.renderBackgroundAudioLabel()}
          </bem.FormBuilderAside__header>

          <bem.FormModal__item>

              {t('This functionality is available in ')}

                {t('Collect version 1.30 and above')}

            <ToggleSwitch
              checked={backgroundAudioProp.value}
              onChange={this.onCheckboxChange.bind(
                this,
                backgroundAudioProp.name
              )}
              label={
                backgroundAudioProp.value
                  ? t('This survey will be recorded')
                  : t('Enable audio recording in the background')
              }
            />
          </bem.FormModal__item>
         </bem.FormBuilderMeta__row>

        {this.isBackgroundAudioEnabled() && (
          <bem.FormBuilderMeta__row>
            <bem.FormModal__item>
              {t('Audio quality')}

              <Select
                className='kobo-select'
                classNamePrefix='kobo-select'
                value={this.getBackgroundAudioParameters()}
                defaultValue={ODK_DEFAULT_AUDIO_QUALITY}
                options={AUDIO_QUALITY_OPTIONS}
                onChange={this.onBackgroundAudioParametersChange}
              />
            </bem.FormModal__item>
          </bem.FormBuilderMeta__row>
        )}
      </bem.FormBuilderMeta>
    );
  }
}
<commit_msg>Move device id checkbox to another column (cleaner UI)<commit_after>import React from 'react';
import autoBind from 'react-autobind';
import Checkbox from 'js/components/common/checkbox';
import TextBox from 'js/components/common/textBox';
import ToggleSwitch from 'js/components/common/toggleSwitch';
import Select from 'react-select';
import {assign} from 'utils';
import {
  META_QUESTION_TYPES,
  SURVEY_DETAIL_ATTRIBUTES,
  FUNCTION_TYPE,
} from 'js/constants';
import bem from 'js/bem';
import envStore from 'js/envStore';

const AUDIT_SUPPORT_URL = 'audit_logging.html';
const RECORDING_SUPPORT_URL = 'recording-interviews.html';

const AUDIO_QUALITY_OPTIONS = [
  {value: 'quality=low', label: 'Low'},
  {value: 'quality=normal', label: 'Normal'},
  {value: 'quality=voice-only', label: 'Voice only'},
];
const ODK_DEFAULT_AUDIO_QUALITY = AUDIO_QUALITY_OPTIONS[2];

/**
 * @prop {object} survey
 * @prop {boolean} isDisabled whether everything is disabled
 * @prop {function} onChange
 */
export default class MetadataEditor extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      metaProperties: [],
    };
    autoBind(this);
  }

  componentDidMount() {
    this.rebuildState();
  }

  rebuildState() {
    const newState = {metaProperties: []};
    Object.keys(META_QUESTION_TYPES).forEach((metaType) => {
      const detail = this.getSurveyDetail(metaType);
      if (detail) {
        newState.metaProperties.push(assign({}, detail.attributes));
      }
    });
    this.setState(newState);
  }

  getMetaProperty(metaType) {
    return this.state.metaProperties.find(
      (metaProp) => metaProp.name === metaType
    );
  }

  getSurveyDetail(sdId) {
    return this.props.survey.surveyDetails.filter(
      (sd) => sd.attributes.name === sdId
    )[0];
  }

  onCheckboxChange(name, isChecked) {
    this.getSurveyDetail(name).set(
      SURVEY_DETAIL_ATTRIBUTES.value.id,
      isChecked
    );
    // Append parameters column with ODK_DEFAULT_AUDIO_QUALITY by default for
    // background-audio type
    if (isChecked && name === META_QUESTION_TYPES['background-audio']) {
      this.getSurveyDetail(name).set(
        SURVEY_DETAIL_ATTRIBUTES.parameters.id,
        ODK_DEFAULT_AUDIO_QUALITY.value
      );
    }

    this.rebuildState();
    if (typeof this.props.onChange === FUNCTION_TYPE.function.id) {
      this.props.onChange();
    }
  }

  onAuditParametersChange(newVal) {
    this.getSurveyDetail(META_QUESTION_TYPES.audit).set(
      SURVEY_DETAIL_ATTRIBUTES.parameters.id,
      newVal
    );
    this.rebuildState();
    if (typeof this.props.onChange === FUNCTION_TYPE.function.id) {
      this.props.onChange();
    }
  }

  isAuditEnabled() {
    const metaProp = this.getMetaProperty(META_QUESTION_TYPES.audit);
    return metaProp.value === true;
  }

  onBackgroundAudioParametersChange(newVal) {
    this.getSurveyDetail(META_QUESTION_TYPES['background-audio']).set(
      SURVEY_DETAIL_ATTRIBUTES.parameters.id,
      newVal.value
    );
    this.rebuildState();
    if (typeof this.props.onChange === FUNCTION_TYPE.function.id) {
      this.props.onChange();
    }
  }

  isBackgroundAudioEnabled() {
    const metaProp = this.getMetaProperty(
      META_QUESTION_TYPES['background-audio']
    );
    return metaProp.value === true;
  }

  getBackgroundAudioParameters() {
    const metaProp = this.getMetaProperty(
      META_QUESTION_TYPES['background-audio']
    );
    let foundParams = ODK_DEFAULT_AUDIO_QUALITY;
    if (metaProp.parameters) {
      foundParams = AUDIO_QUALITY_OPTIONS.find(
        (option) => option.value === metaProp.parameters
      );
    }
    return foundParams;
  }

  getAuditParameters() {
    const metaProp = this.getMetaProperty(META_QUESTION_TYPES.audit);
    return metaProp.parameters;
  }

  renderAuditInputLabel() {
    return (
      <React.Fragment>
        {t('Audit settings')}

        {envStore.isReady &&
          envStore.data.support_url && (
            <bem.TextBox__labelLink
              href={
                envStore.data.support_url + AUDIT_SUPPORT_URL
              }
              target='_blank'
            >

            </bem.TextBox__labelLink>
          )}
      </React.Fragment>
    );
  }

  renderBackgroundAudioLabel() {
    return (
      <React.Fragment>
        {t('Background audio')}

        {envStore.isReady &&
          envStore.data.support_url && (
            <bem.TextBox__labelLink
              // TODO update support article to include background-audio
              href={
                envStore.data.support_url +
                RECORDING_SUPPORT_URL
              }
              target='_blank'
            >

            </bem.TextBox__labelLink>
          )}
      </React.Fragment>
    );
  }

  render() {
    if (this.state.metaProperties.length === 0) {
      return null;
    }

    const leftColumn = [
      META_QUESTION_TYPES.start,
      META_QUESTION_TYPES.end,
      META_QUESTION_TYPES.today,
      META_QUESTION_TYPES.audit,
    ];
    const rightColumn = [
      META_QUESTION_TYPES.username,
      META_QUESTION_TYPES.phonenumber,
      META_QUESTION_TYPES.deviceid,
    ];

    let backgroundAudioProp = this.getMetaProperty(
      META_QUESTION_TYPES['background-audio']
    );

    return (
      <bem.FormBuilderMeta>
        <bem.FormBuilderMeta__columns>
          <bem.FormBuilderMeta__column>
            {leftColumn.map((metaType) => {
              const metaProp = this.getMetaProperty(metaType);
              return (
                <Checkbox
                  key={`meta-${metaProp.name}`}
                  label={metaProp.label}
                  checked={metaProp.value}
                  disabled={this.props.isDisabled}
                  onChange={this.onCheckboxChange.bind(this, metaProp.name)}
                />
              );
            })}
          </bem.FormBuilderMeta__column>

          <bem.FormBuilderMeta__column>
            {rightColumn.map((metaType) => {
              const metaProp = this.getMetaProperty(metaType);
              return (
                <Checkbox
                  key={`meta-${metaProp.name}`}
                  label={metaProp.label}
                  checked={metaProp.value}
                  disabled={this.props.isDisabled}
                  onChange={this.onCheckboxChange.bind(this, metaProp.name)}
                />
              );
            })}
          </bem.FormBuilderMeta__column>
        </bem.FormBuilderMeta__columns>

        {this.isAuditEnabled() && (
          <bem.FormBuilderMeta__row>
            <TextBox
              customModifiers='on-white'
              label={this.renderAuditInputLabel()}
              value={this.getAuditParameters()}
              disabled={this.props.isDisabled}
              onChange={this.onAuditParametersChange}
              placeholder={t('Enter audit settings here')}
            />
          </bem.FormBuilderMeta__row>
        )}

        <bem.FormBuilderMeta__row m='background-audio'>
          <bem.FormBuilderAside__header>
            {this.renderBackgroundAudioLabel()}
          </bem.FormBuilderAside__header>

          <bem.FormModal__item>

              {t('This functionality is available in ')}

                {t('Collect version 1.30 and above')}

            <ToggleSwitch
              checked={backgroundAudioProp.value}
              onChange={this.onCheckboxChange.bind(
                this,
                backgroundAudioProp.name
              )}
              label={
                backgroundAudioProp.value
                  ? t('This survey will be recorded')
                  : t('Enable audio recording in the background')
              }
            />
          </bem.FormModal__item>
         </bem.FormBuilderMeta__row>

        {this.isBackgroundAudioEnabled() && (
          <bem.FormBuilderMeta__row>
            <bem.FormModal__item>
              {t('Audio quality')}

              <Select
                className='kobo-select'
                classNamePrefix='kobo-select'
                value={this.getBackgroundAudioParameters()}
                defaultValue={ODK_DEFAULT_AUDIO_QUALITY}
                options={AUDIO_QUALITY_OPTIONS}
                onChange={this.onBackgroundAudioParametersChange}
              />
            </bem.FormModal__item>
          </bem.FormBuilderMeta__row>
        )}
      </bem.FormBuilderMeta>
    );
  }
}
<|endoftext|>