<commit_before>window.texts = window.texts || {};

texts.TextLoader = (function() {

	var data = {},
		baseFolder = 'content/texts/';

	function setBaseFolder(folder) {
		baseFolder = folder;
	}

	function load(textid, sectionid, successCallback, errorCallback) {

		if (typeof textid != 'string') {
			textid = textid.id;
		}

		// store texts during loading?
		if (typeof data[textid] == 'undefined') {
			data[textid] = {};
		}

		if (typeof data[textid][sectionid] != 'undefined') {
			successCallback (data[textid][sectionid]);
		}

		var url = baseFolder + textid + '/' + sectionid + '.json?' + new Date();

		console.log(textid, sectionid, url);

		$.ajax({
			url: url,
			success: function(d) {

				var doc = $(d.text);

				data[textid][sectionid] = doc;

				//console.log(textid, sectionid, d.text);

				successCallback(doc);

			}, error: function(jqXHR, textStatus, errorThrown) {
				if (errorCallback) {
					errorCallback(textid, sectionid);
				}

				//console.log('error', textStatus, errorThrown, jqXHR );
			}
		});
	}

	return {
		setBaseFolder: setBaseFolder,
		load: load
	}

})();

<commit_msg>trying to prevent loading a missing seciotn/chatper<commit_after>window.texts = window.texts || {};

texts.TextLoader = (function() {

	var data = {},
		baseFolder = 'content/texts/';

	function setBaseFolder(folder) {
		baseFolder = folder;
	}

	function load(textInfo, sectionid, successCallback, errorCallback) {

		var textid = '';

		if (typeof textInfo == 'string') {
			textid = textInfo;
		} else {
			textid = textInfo.id;

			if (textInfo.sections.indexOf(sectionid) == -1) {
				//sectionid = textInfo.sections[0];
			}

			console.log('hasit', sectionid, textInfo.sections.indexOf(sectionid), textInfo.sections[0]);
		}

		// store texts during loading?
		if (typeof data[textid] == 'undefined') {
			data[textid] = {};
		}

		if (typeof data[textid][sectionid] != 'undefined') {
			successCallback (data[textid][sectionid]);
		}

		var url = baseFolder + textid + '/' + sectionid + '.json?' + new Date();

		console.log(textid, sectionid, url);

		$.ajax({
			url: url,
			success: function(d) {

				var doc = $(d.text);

				data[textid][sectionid] = doc;

				//console.log(textid, sectionid, d.text);

				successCallback(doc);

			}, error: function(jqXHR, textStatus, errorThrown) {
				if (errorCallback) {
					errorCallback(textid, sectionid);
				}

				//console.log('error', textStatus, errorThrown, jqXHR );
			}
		});
	}

	return {
		setBaseFolder: setBaseFolder,
		load: load
	}

})();

<|endoftext|>


<commit_before>/* eslint-disable */
DOCVERSION = '2'
VERSION = '2.2.0'
Status = {
  grey: 'lair-grey',
  blue: 'lair-blue',
  green: 'lair-green',
  orange: 'lair-orange',
  red: 'lair-red'
}
StatusMap = [Status.grey, Status.blue, Status.green, Status.orange, Status.red]
/* eslint-enable */
<commit_msg>2.3.0<commit_after>/* eslint-disable */
DOCVERSION = '2'
VERSION = '2.3.0'
Status = {
  grey: 'lair-grey',
  blue: 'lair-blue',
  green: 'lair-green',
  orange: 'lair-orange',
  red: 'lair-red'
}
StatusMap = [Status.grey, Status.blue, Status.green, Status.orange, Status.red]
/* eslint-enable */
<|endoftext|>


<commit_before>// FileLoader - A caching file downloader for Titanium

// TODO: Docs

// Constants {{{1
// Load constants allowing them to be overwriten with configuration.
var HTTP_TIMEOUT    = 10000;
var MAX_ASYNC_TASKS = 10;
var CACHE_METADATA_PROPERTY, EXPIRATION_TIME, CACHE_PATH_PREFIX;
(function() {
  var have_alloy = (typeof Alloy !== 'undefined' && Alloy !== null && Alloy.CFG);

  if (have_alloy && Alloy.CFG.cache_metadata_property) {
    CACHE_METADATA_PROPERTY = Alloy.CFG.caching_property_key;
  }
  else if (typeof caching_property_key !== 'undefined' && caching_property_key !== null) {
    CACHE_METADATA_PROPERTY = CACHING_PROPERTY_KEY;
  }
  else {
    CACHE_METADATA_PROPERTY = "file_loader_cache_metadata";
  }

  if (have_alloy && Alloy.CFG.cache_expiration) {
    EXPIRATION_TIME = Alloy.CFG.cache_expiration;
  }
  else if (typeof cache_expiration !== 'undefined' && cache_expiration !== null) {
    EXPIRATION_TIME = cache_expiration;
  }
  else {
    EXPIRATION_TIME = 3600000; // 60 minutes
  }

  if (have_alloy && Alloy.CFG.cache_directory) {
    CACHE_PATH_PREFIX = Alloy.CFG.cache_directory;
  }
  else if (typeof cache_directory !== 'undefined' && cache_directory !== null) {
    CACHE_PATH_PREFIX = cache_directory;
  }
  else {
    CACHE_PATH_PREFIX = "cached_files";
  }
})();

// Metadata {{{1
var metadata = Ti.App.Properties.getObject(CACHE_METADATA_PROPERTY) || {};

function saveMetaData() {
  Ti.App.Properties.setObject(CACHE_METADATA_PROPERTY, metadata);
}

// Cache path {{{1
// Make sure we have the directory to store files.
var cache_path = (function() {
  var os = Ti.Platform.osname;
  var data_dir = (os === "iphone" || os === "ipad") ?
    Ti.Filesystem.applicationSupportDirectory :
    Ti.Filesystem.applicationDataDirectory;
  var cache_dir = Ti.Filesystem.getFile(data_dir, CACHE_PATH_PREFIX);
  if (!cache_dir.exists()) {
    cache_dir.createDirectory();
  }
  return cache_dir.resolve();
})();

// Class: File {{{1

// Constructor {{{2
function File(id) {
  this.id        = id;
  var cache_data = metadata[this.id];
  this.file_path = Ti.Filesystem.getFile(cache_path, this.id);

  if (cache_data) {
    this.is_cached    = this.exists();
    this.last_used_at = cache_data.last_used_at;
    this.md5          = cache_data.md5;
  }
  else {
    this.is_cached    = false;
    this.last_used_at = 0;
    this.md5          = null;
  }
}

// File::updateLastUsedAt {{{2
File.prototype.updateLastUsedAt = function() {
  this.last_used_at = new Date().getTime();
  return this;
};

// File::save {{{2
File.prototype.save = function() {
  metadata[this.id] = {
    last_used_at: this.last_used_at,
    md5:          this.md5
  };
  saveMetaData();
  this.is_cached = true;
  return this;
};

// File::write {{{2
File.prototype.write = function(data) {
  // A Titanium bug cause this to always return false. We need to manually
  // check it exists. And assume it worked.
  // (https://jira.appcelerator.org/browse/TIMOB-1658)
  this.file_path.write(data);
  this.md5 = File.getMD5(data);
  return this.exists();
};

// File::exists {{{2
File.prototype.exists = function() {
  return this.file_path.exists();
};

// File::expired {{{2
File.prototype.expired = function() {
  return ((new Date().getTime() - this.last_used_at) > EXPIRATION_TIME);
};

// File::expunge {{{2
File.prototype.expunge = function() {
  this.file_path.deleteFile();
  delete metadata[this.id];
  saveMetaData();
  this.is_cached = false;
};

// File::getPath {{{2
File.prototype.getPath = function() {
  return this.file_path.resolve();
};

// File.getFile {{{2
File.prototype.getFile = function() {
  return this.file_path;
};

// File.getMD5 {{{2
File.getMD5 = function(data) {
  return Ti.Utils.md5HexDigest(data);
};

// File.idFromUrl {{{2
File.idFromUrl = function(url) {
  // Insanely simple conversion to keep id unique to the url and prevent
  // possible illegal filesystem characters and removes path seperators.
  // MD5 should be fast enough not that this is repeated so much.
  return Ti.Utils.md5HexDigest(url);
};

// File.fromURL {{{2
File.fromURL = function(url) {
  return new File(File.idFromUrl(url));
};

// FileLoader {{{1
var pending_tasks = {dispatch_queue:[]};
var FileLoader = {};

// dispatchNextTask {{{2
function dispatchNextTask() {
  var task;
  if (pending_tasks.dispatch_queue.length < MAX_ASYNC_TASKS) {
    task = pending_tasks.dispatch_queue.shift();
    if (!task) { return; }
    if (task.resolve) { task.resolve(); }
    else { poorMansNextTick(task); }
  }
}

// spawnHTTPClient {{{2
function spawnHTTPClient(url, callbacks) {
  var http = Ti.Network.createHTTPClient({
    onload:       callbacks.resolve,
    onerror:      callbacks.reject,
    ondatastream: callbacks.notify,
    timeout:      HTTP_TIMEOUT
  });
  http.open("GET", url);
  http.send();
}

// Exports {{{1
// #download - Attempt to download and cache url {{{2
FileLoader.download = function(url, callbacks) {
  // Promises are better. Why would you not use them?!
  if (!callbacks) { return FileLoader.downloadP(url); }
  if (!throttle_warn) {
    Ti.API.warn(
      "Using callbacks does NOT throttle requests. " +
      "This can be dangerous as all downloads happen at the same time.\n" +
      "It is preferred to use FileLoader.download(url).then(...) after installing q.js"
    );
    throttle_warn = true;
  }

  var file = File.fromURL(url);
  if (file.is_cached && !file.expired()) {
    file.updateLastUsedAt().save();
    // Poor man's nextTick:
    Ti.API.info("Cached " + file.id + ": " + url);
    setTimeout(function() { attemptCallback(file); }, 0);
    return;
  }

  // I hate this callback crap
  function onload() {
    if (!file.write(this.responseData)) {
      if (typeof callbacks.onerror === 'function') {
        callbacks.onerror({message: "Failed to save data from " + url + " to " + file.getPath()});
      }
      return;
    }
    file.downloaded = true;
    file.updateLastUsedAt().save();
    attemptCallback(file);
  }

  function attemptCallback(value) {
    var cb = callbacks.onload || callbacks;
    if (typeof cb === 'function') {
      cb(value);
    }
  }

  spawnHTTPClient(url, {
    resolve: onload,
    reject:  callbacks.onerror,
    notify:  callbacks.ondatastream
  });

  Ti.API.info("Downloading " + file.id + ": " + url);
};

// #downloadP - Same as #download but returns a promise {{{2
FileLoader.downloadP = function(url) {
  var Q;
  try { Q = require("q"); }
  catch (e) {
    throw new Error("Promises are not supported without the Q library. Install q.js into Resources/ or app/lib/");
  }

  function requestDispatch() {
    var waitForDispatch = Q.defer();
    pending_tasks.dispatch_queue.push(waitForDispatch);
    return waitForDispatch.promise;
  }

  var file = File.fromURL(url);

  if (pending_tasks[file.id]) {
    Ti.API.info("Pending " + file.id + ": " + url);
    return pending_tasks[file.id];
  }

  if (file.is_cached && !file.expired()) {
    file.updateLastUsedAt().save();
    Ti.API.info("Cached " + file.id + ": " + url);
    return Q(file);
  }

  var waitingForDownload = requestDispatch()
    .then(function() {
      var waitForHttp = Q.defer();
      spawnHTTPClient(url, waitForHttp);
      Ti.API.info("Downloading " + file.id + ": " + url);
      return waitForHttp.promise;
    })
    .get("source")
    .get("responseData")
    .then(function(data) {
      if (!file.write(data)) {
        throw new Error("Failed to save data from " + url + " to " + file.getPath());
      }
      file.downloaded = true;
      file.updateLastUsedAt().save();
      return file;
    })
    .fin(function() {
      delete pending_tasks[file.id];
      dispatchNextTask();
    });

  pending_tasks[file.id] = waitingForDownload;
  dispatchNextTask();

  return waitingForDownload;
};

// pruneStaleCache - (alias: gc) Remove stale cache files {{{2
FileLoader.pruneStaleCache = FileLoader.gc = function(force) {
  var id, file;
  for (id in metadata) {
    file = new File(id);
    if (force || file.expired()) {
      file.expunge();
    }
  }
};

// Export File class {{{2
FileLoader.File = File;

// PinkySwear - Minimalistic implementation of the Promises/A+ spec {{{1
// Public Domain. Use, modify and distribute it any way you like. No attribution required.
// NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
// https://github.com/timjansen/PinkySwear.js
var pinkySwear = FileLoader.pinkySwear = (function() {
  /* jshint eqnull:true */
  function isFunction(f,o) { return typeof f == 'function'; }
  function defer(callback) { setTimeout(callback, 0); }

  function pinkySwear() {
    var state;           // undefined/null = pending, true = fulfilled, false = rejected
    var values = [];     // an array of values as arguments for the then() handlers
    var deferred = [];   // functions to call when set() is invoked
    var progress = [];   // functoins to call when notify() is invoked

    var set = function promise(newState, newValues) {
      if (state == null) {
        state = newState;
        values = newValues;
        defer(function() {
          for (var i = 0; i < deferred.length; i++)
            deferred[i]();
        });
      }
    };
    set.then = function(onFulfilled, onRejected, onProgress) {
      var newPromise = pinkySwear();
      var callCallbacks = function() {
        try {
          var f = (state ? onFulfilled : onRejected);
          if (isFunction(f)) {
            var r = f.apply(null, values);
            if (r && isFunction(r.then))
              r.then(
                function(value){newPromise(true,  [value]);},
                function(value){newPromise(false, [value]);}
              );
            else
              newPromise(true, [r]);
          }
          else
            newPromise(state, values);
        }
        catch (e) {
          newPromise(false, [e]);
        }
      };
      if (state != null)
        defer(callCallbacks);
      else
        deferred.push(callCallbacks);
      if (isFunction(onProgress))
        set.progress(function(value) {
          newPromise.notify(onProgress(value));
        });
      return newPromise;
    };

    set.notify = function(value) {
      defer(function() {
        for (var i = 0; i < progress.length; i++)
          progress[i](value);
      });
    };

    set.progress = function(onProgress) { progress.push(onProgress); return set; };

    // always(func) is the same as then(func, func)
    set.always = set.fin = function(func) { return set.then(func, func); };

    // error(func) is the same as then(0, func)
    set.error = set.fail = function(func) { return set.then(0, func); };

    set.get = function(prop) {
      return set.then(function(value) { return value[prop]; });
    };

    set.invoke = function(prop) {
      var args = [].slice.call(arguments, 1) || [];
      return set.then(function(value) { return value[prop].apply(value, args); });
    };

    return set;
  }

  return pinkySwear;
})();
// }}}1

module.exports  = FileLoader;
/* vim:set ts=2 sw=2 et fdm=marker: */
<commit_msg>Fix fin bug<commit_after>// FileLoader - A caching file downloader for Titanium

// TODO: Docs

// Constants {{{1
// Load constants allowing them to be overwriten with configuration.
var HTTP_TIMEOUT    = 10000;
var MAX_ASYNC_TASKS = 10;
var CACHE_METADATA_PROPERTY, EXPIRATION_TIME, CACHE_PATH_PREFIX;
(function() {
  var have_alloy = (typeof Alloy !== 'undefined' && Alloy !== null && Alloy.CFG);

  if (have_alloy && Alloy.CFG.cache_metadata_property) {
    CACHE_METADATA_PROPERTY = Alloy.CFG.caching_property_key;
  }
  else if (typeof caching_property_key !== 'undefined' && caching_property_key !== null) {
    CACHE_METADATA_PROPERTY = CACHING_PROPERTY_KEY;
  }
  else {
    CACHE_METADATA_PROPERTY = "file_loader_cache_metadata";
  }

  if (have_alloy && Alloy.CFG.cache_expiration) {
    EXPIRATION_TIME = Alloy.CFG.cache_expiration;
  }
  else if (typeof cache_expiration !== 'undefined' && cache_expiration !== null) {
    EXPIRATION_TIME = cache_expiration;
  }
  else {
    EXPIRATION_TIME = 3600000; // 60 minutes
  }

  if (have_alloy && Alloy.CFG.cache_directory) {
    CACHE_PATH_PREFIX = Alloy.CFG.cache_directory;
  }
  else if (typeof cache_directory !== 'undefined' && cache_directory !== null) {
    CACHE_PATH_PREFIX = cache_directory;
  }
  else {
    CACHE_PATH_PREFIX = "cached_files";
  }
})();

// Metadata {{{1
var metadata = Ti.App.Properties.getObject(CACHE_METADATA_PROPERTY) || {};

function saveMetaData() {
  Ti.App.Properties.setObject(CACHE_METADATA_PROPERTY, metadata);
}

// Cache path {{{1
// Make sure we have the directory to store files.
var cache_path = (function() {
  var os = Ti.Platform.osname;
  var data_dir = (os === "iphone" || os === "ipad") ?
    Ti.Filesystem.applicationSupportDirectory :
    Ti.Filesystem.applicationDataDirectory;
  var cache_dir = Ti.Filesystem.getFile(data_dir, CACHE_PATH_PREFIX);
  if (!cache_dir.exists()) {
    cache_dir.createDirectory();
  }
  return cache_dir.resolve();
})();

// Class: File {{{1

// Constructor {{{2
function File(id) {
  this.id        = id;
  var cache_data = metadata[this.id];
  this.file_path = Ti.Filesystem.getFile(cache_path, this.id);

  if (cache_data) {
    this.is_cached    = this.exists();
    this.last_used_at = cache_data.last_used_at;
    this.md5          = cache_data.md5;
  }
  else {
    this.is_cached    = false;
    this.last_used_at = 0;
    this.md5          = null;
  }
}

// File::updateLastUsedAt {{{2
File.prototype.updateLastUsedAt = function() {
  this.last_used_at = new Date().getTime();
  return this;
};

// File::save {{{2
File.prototype.save = function() {
  metadata[this.id] = {
    last_used_at: this.last_used_at,
    md5:          this.md5
  };
  saveMetaData();
  this.is_cached = true;
  return this;
};

// File::write {{{2
File.prototype.write = function(data) {
  // A Titanium bug cause this to always return false. We need to manually
  // check it exists. And assume it worked.
  // (https://jira.appcelerator.org/browse/TIMOB-1658)
  this.file_path.write(data);
  this.md5 = File.getMD5(data);
  return this.exists();
};

// File::exists {{{2
File.prototype.exists = function() {
  return this.file_path.exists();
};

// File::expired {{{2
File.prototype.expired = function() {
  return ((new Date().getTime() - this.last_used_at) > EXPIRATION_TIME);
};

// File::expunge {{{2
File.prototype.expunge = function() {
  this.file_path.deleteFile();
  delete metadata[this.id];
  saveMetaData();
  this.is_cached = false;
};

// File::getPath {{{2
File.prototype.getPath = function() {
  return this.file_path.resolve();
};

// File.getFile {{{2
File.prototype.getFile = function() {
  return this.file_path;
};

// File.getMD5 {{{2
File.getMD5 = function(data) {
  return Ti.Utils.md5HexDigest(data);
};

// File.idFromUrl {{{2
File.idFromUrl = function(url) {
  // Insanely simple conversion to keep id unique to the url and prevent
  // possible illegal filesystem characters and removes path seperators.
  // MD5 should be fast enough not that this is repeated so much.
  return Ti.Utils.md5HexDigest(url);
};

// File.fromURL {{{2
File.fromURL = function(url) {
  return new File(File.idFromUrl(url));
};

// FileLoader {{{1
var pending_tasks = {dispatch_queue:[]};
var FileLoader = {};

// dispatchNextTask {{{2
function dispatchNextTask() {
  var task;
  if (pending_tasks.dispatch_queue.length < MAX_ASYNC_TASKS) {
    task = pending_tasks.dispatch_queue.shift();
    if (!task) { return; }
    if (task.resolve) { task.resolve(); }
    else { poorMansNextTick(task); }
  }
}

// spawnHTTPClient {{{2
function spawnHTTPClient(url, callbacks) {
  var http = Ti.Network.createHTTPClient({
    onload:       callbacks.resolve,
    onerror:      callbacks.reject,
    ondatastream: callbacks.notify,
    timeout:      HTTP_TIMEOUT
  });
  http.open("GET", url);
  http.send();
}

// Exports {{{1
// #download - Attempt to download and cache url {{{2
FileLoader.download = function(url, callbacks) {
  // Promises are better. Why would you not use them?!
  if (!callbacks) { return FileLoader.downloadP(url); }
  if (!throttle_warn) {
    Ti.API.warn(
      "Using callbacks does NOT throttle requests. " +
      "This can be dangerous as all downloads happen at the same time.\n" +
      "It is preferred to use FileLoader.download(url).then(...) after installing q.js"
    );
    throttle_warn = true;
  }

  var file = File.fromURL(url);
  if (file.is_cached && !file.expired()) {
    file.updateLastUsedAt().save();
    // Poor man's nextTick:
    Ti.API.info("Cached " + file.id + ": " + url);
    setTimeout(function() { attemptCallback(file); }, 0);
    return;
  }

  // I hate this callback crap
  function onload() {
    if (!file.write(this.responseData)) {
      if (typeof callbacks.onerror === 'function') {
        callbacks.onerror({message: "Failed to save data from " + url + " to " + file.getPath()});
      }
      return;
    }
    file.downloaded = true;
    file.updateLastUsedAt().save();
    attemptCallback(file);
  }

  function attemptCallback(value) {
    var cb = callbacks.onload || callbacks;
    if (typeof cb === 'function') {
      cb(value);
    }
  }

  spawnHTTPClient(url, {
    resolve: onload,
    reject:  callbacks.onerror,
    notify:  callbacks.ondatastream
  });

  Ti.API.info("Downloading " + file.id + ": " + url);
};

// #downloadP - Same as #download but returns a promise {{{2
FileLoader.downloadP = function(url) {
  var Q;
  try { Q = require("q"); }
  catch (e) {
    throw new Error("Promises are not supported without the Q library. Install q.js into Resources/ or app/lib/");
  }

  function requestDispatch() {
    var waitForDispatch = Q.defer();
    pending_tasks.dispatch_queue.push(waitForDispatch);
    return waitForDispatch.promise;
  }

  var file = File.fromURL(url);

  if (pending_tasks[file.id]) {
    Ti.API.info("Pending " + file.id + ": " + url);
    return pending_tasks[file.id];
  }

  if (file.is_cached && !file.expired()) {
    file.updateLastUsedAt().save();
    Ti.API.info("Cached " + file.id + ": " + url);
    return Q(file);
  }

  var waitingForDownload = requestDispatch()
    .then(function() {
      var waitForHttp = Q.defer();
      spawnHTTPClient(url, waitForHttp);
      Ti.API.info("Downloading " + file.id + ": " + url);
      return waitForHttp.promise;
    })
    .get("source")
    .get("responseData")
    .then(function(data) {
      if (!file.write(data)) {
        throw new Error("Failed to save data from " + url + " to " + file.getPath());
      }
      file.downloaded = true;
      file.updateLastUsedAt().save();
      return file;
    })
    .fin(function() {
      delete pending_tasks[file.id];
      dispatchNextTask();
    });

  pending_tasks[file.id] = waitingForDownload;
  dispatchNextTask();

  return waitingForDownload;
};

// pruneStaleCache - (alias: gc) Remove stale cache files {{{2
FileLoader.pruneStaleCache = FileLoader.gc = function(force) {
  var id, file;
  for (id in metadata) {
    file = new File(id);
    if (force || file.expired()) {
      file.expunge();
    }
  }
};

// Export File class {{{2
FileLoader.File = File;

// PinkySwear - Minimalistic implementation of the Promises/A+ spec {{{1
// Public Domain. Use, modify and distribute it any way you like. No attribution required.
// NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
// https://github.com/timjansen/PinkySwear.js
var pinkySwear = FileLoader.pinkySwear = (function() {
  /* jshint eqnull:true */
  function isFunction(f,o) { return typeof f == 'function'; }
  function defer(callback) { setTimeout(callback, 0); }

  function pinkySwear() {
    var state;           // undefined/null = pending, true = fulfilled, false = rejected
    var values = [];     // an array of values as arguments for the then() handlers
    var deferred = [];   // functions to call when set() is invoked
    var progress = [];   // functoins to call when notify() is invoked

    var set = function promise(newState, newValues) {
      if (state == null) {
        state = newState;
        values = newValues;
        defer(function() {
          for (var i = 0; i < deferred.length; i++)
            deferred[i]();
        });
      }
    };
    set.then = function(onFulfilled, onRejected, onProgress) {
      var newPromise = pinkySwear();
      var callCallbacks = function() {
        try {
          var f = (state ? onFulfilled : onRejected);
          if (isFunction(f)) {
            var r = f.apply(null, values);
            if (r && isFunction(r.then))
              r.then(
                function(value){newPromise(true,  [value]);},
                function(value){newPromise(false, [value]);}
              );
            else
              newPromise(true, [r]);
          }
          else
            newPromise(state, values);
        }
        catch (e) {
          newPromise(false, [e]);
        }
      };
      if (state != null)
        defer(callCallbacks);
      else
        deferred.push(callCallbacks);
      if (isFunction(onProgress))
        set.progress(function(value) {
          newPromise.notify(onProgress(value));
        });
      return newPromise;
    };

    set.notify = function(value) {
      defer(function() {
        for (var i = 0; i < progress.length; i++)
          progress[i](value);
      });
    };

    set.progress = function(onProgress) { progress.push(onProgress); return set; };

    // always(func) is the same as then(func, func)
    set.always = function(func) { return set.then(func, func); };

    // fin(func) is like always() but doesn't modify the promise chain
    set.fin = function(func) { set.then(func, func); return set; };

    // error(func) is the same as then(0, func)
    set.error = set.fail = function(func) { return set.then(0, func); };

    set.get = function(prop) {
      return set.then(function(value) { return value[prop]; });
    };

    set.invoke = function(prop) {
      var args = [].slice.call(arguments, 1) || [];
      return set.then(function(value) { return value[prop].apply(value, args); });
    };

    return set;
  }

  return pinkySwear;
})();
// }}}1

module.exports  = FileLoader;
/* vim:set ts=2 sw=2 et fdm=marker: */
<|endoftext|>


<commit_before>function Screensaver(timeoutSeconds, videoSrc, onSleepCallback, onAwakeCallback) {

  // How long is timeout.
  this.timeoutSeconds = timeoutSeconds || 60;

  //Callback functions to reset external
  this.onSleepCallback = onSleepCallback || function() {};

  this.onAwakeCallback = onAwakeCallback || function() {};

  // Start the clock
  this.idleTime = 0;
  this.active = false;

  var _this = this;

  // Increment the idle time counter every minute.
  this.idleInterval = setInterval(function() { _this.timerIncrement(); }, 1000);// 1 second

  // Zero the idle timer on any movement.
  $('body').on('touchstart keypress mousemove mousedown', function() { _this.anyAction(); });

  //Setup video screensaver
  this.createVideo(videoSrc);

  //Default to hidden screensaver
  this.awake();

}

/**
 * Setup fullscreen looping video to be shown during sleep.
 */
Screensaver.prototype.createVideo = function(videoSrc) {

  //Create video tag
  var videoTag = '<video id="screensaver_video" style="position:fixed; top:0px; left:0px; z-index:999;" class="video-js vjs-default-skin vjs-big-play-centered"><source src="' + videoSrc + '" type="video/webm" /></video>';
  var videoOptions = { controls: false, autoplay: false, loop: 'true', preload: 'auto' };

  //Append to html
  $('body').append(videoTag);

  //Initialize player
  // this.videoPlayer = videojs('screensaver_video', videoOptions, function() {
  //   // Player (this) is initialized and ready.
  // });

};

/**
 * Start the screensaver after X seconds of inactivity.
 */
Screensaver.prototype.timerIncrement = function() {

  //Increment counter
  this.idleTime = this.idleTime + 1;

  // If it's been X seconds of inactivity, save the screen
  if (this.idleTime > this.timeoutSeconds && this.active == false) {

    this.sleep();

  }

};

/**
* Zero the idle timer on any movement.
*/
Screensaver.prototype.anyAction = function() {

  this.idleTime = 0;

  if (this.active == true) {

    this.awake();

  }

};

/**
* Display the screensaver
*/
Screensaver.prototype.sleep = function() {

  this.active = true;
  this.onSleepCallback();

  //Show the video
  $('#screensaver_video').fadeIn('slow');
  this.videoPlayer.play();

};

/**
* Remove the screensaver
*/
Screensaver.prototype.awake = function() {

  this.active = false;
  this.onAwakeCallback();

  //Hide the video
  var _this = this;
  $('#screensaver_video').fadeOut('slow', function() {
    $('#screensaver_video').get(0).pause();
  });

};

export default Screensaver;
<commit_msg>Removing olf videojs code<commit_after>function Screensaver(timeoutSeconds, videoSrc, onSleepCallback, onAwakeCallback) {

  // How long is timeout.
  this.timeoutSeconds = timeoutSeconds || 60;

  //Callback functions to reset external
  this.onSleepCallback = onSleepCallback || function() {};

  this.onAwakeCallback = onAwakeCallback || function() {};

  // Start the clock
  this.idleTime = 0;
  this.active = false;

  var _this = this;

  // Increment the idle time counter every minute.
  this.idleInterval = setInterval(function() { _this.timerIncrement(); }, 1000);// 1 second

  // Zero the idle timer on any movement.
  $('body').on('touchstart keypress mousemove mousedown', function() { _this.anyAction(); });

  //Setup video screensaver
  this.createVideo(videoSrc);

  //Default to hidden screensaver
  this.awake();

}

/**
 * Setup fullscreen looping video to be shown during sleep.
 */
Screensaver.prototype.createVideo = function(videoSrc) {

  //Create video tag
  var videoTag = '<video id="screensaver_video" style="position:fixed; top:0px; left:0px; z-index:999;" class="video-js vjs-default-skin vjs-big-play-centered"><source src="' + videoSrc + '" type="video/webm" /></video>';
  var videoOptions = { controls: false, autoplay: false, loop: 'true', preload: 'auto' };

  //Append to html
  $('body').append(videoTag);

};

/**
 * Start the screensaver after X seconds of inactivity.
 */
Screensaver.prototype.timerIncrement = function() {

  //Increment counter
  this.idleTime = this.idleTime + 1;

  // If it's been X seconds of inactivity, save the screen
  if (this.idleTime > this.timeoutSeconds && this.active == false) {

    this.sleep();

  }

};

/**
* Zero the idle timer on any movement.
*/
Screensaver.prototype.anyAction = function() {

  this.idleTime = 0;

  if (this.active == true) {

    this.awake();

  }

};

/**
* Display the screensaver
*/
Screensaver.prototype.sleep = function() {

  this.active = true;
  this.onSleepCallback();

  //Show the video
  $('#screensaver_video').fadeIn('slow');
  this.videoPlayer.play();

};

/**
* Remove the screensaver
*/
Screensaver.prototype.awake = function() {

  this.active = false;
  this.onAwakeCallback();

  //Hide the video
  var _this = this;
  $('#screensaver_video').fadeOut('slow', function() {
    $('#screensaver_video').get(0).pause();
  });

};

export default Screensaver;
<|endoftext|>


<commit_before>import 'bootstrap/less/bootstrap.less';
import React from 'react'
import { render } from 'react-dom'
import { Router, browserHistory} from 'react-router'

const rootRoute = {
    component: 'div',
    childRoutes: [{
        path: '/',
        component: [require('./../components/App')],
        childRoutes: [
            require('./../routes/Calendar'),
            require('./../routes/Course'),
            require('./../routes/Messages'),
            require('./../routes/Grades'),
            require('./../routes/Profile')
        ]
    }]
}
render(
    <Router history={browserHistory} routes={rootRoute} />
 , document.getElementById('index'));
<commit_msg>初始化项目结构<commit_after>import 'bootstrap/less/bootstrap.less';
import React from 'react'
import { render } from 'react-dom'
import { Router, browserHistory} from 'react-router'

const rootRoute = {
    component: 'div',
    childRoutes: [{
        path: '/',
        component: require('./../components/App'),
        childRoutes: [
            require('./../routes/Calendar'),
            require('./../routes/Course'),
            require('./../routes/Messages'),
            require('./../routes/Grades'),
            require('./../routes/Profile')
        ]
    }]
}
render(
    <Router history={browserHistory} routes={rootRoute} />
 , document.getElementById('index'));
<|endoftext|>


<commit_before>'use strict';

angular.module('myApp.mapView', ['ngRoute', 'ngMaterial'])

.config(['$routeProvider', function($routeProvider) {
  $routeProvider.when('/mapView/:latitude/:longitude', {
    templateUrl: 'mapView/mapView.html',
    controller: 'MapViewController'
});
}])

.controller('MapViewController', ['$scope', '$routeParams', function($scope, $routeParams)  {
    var directionsDisplay;
    var directionsService = new google.maps.DirectionsService();
    var map;

    function intializeMap() {
      directionsDisplay = new google.maps.DirectionsRenderer({
        panel: document.getElementById("directions-info")
      });
      var currentLocation = new google.maps.LatLng(
        localStorage["originLatitude"],
        localStorage["originLongitude"]
      );
      var mapOptions = {
        zoom:7,
        center: currentLocation
      }
      map = new google.maps.Map(document.getElementById("map-canvas"), mapOptions);
      directionsDisplay.setMap(map);
    }

    function calcRoute() {
      var start = new google.maps.LatLng( localStorage["originLatitude"] , localStorage["originLongitude"]);
      var end = new google.maps.LatLng( localStorage["destinationLatitude"] , localStorage["destinationLongitude"]);
      var request = {
        origin:start,
        destination:end,
        travelMode: google.maps.TravelMode.DRIVING
      };
      directionsService.route(request, function(result, status) {
        if (status == google.maps.DirectionsStatus.OK) {
          directionsDisplay.setDirections(result);
        }
      });
    }

    $scope.initialize = function(){
        intializeMap();
        calcRoute();
    }
}]);<commit_msg>Setting route to walking<commit_after>'use strict';

angular.module('myApp.mapView', ['ngRoute', 'ngMaterial'])

.config(['$routeProvider', function($routeProvider) {
  $routeProvider.when('/mapView/:latitude/:longitude', {
    templateUrl: 'mapView/mapView.html',
    controller: 'MapViewController'
});
}])

.controller('MapViewController', ['$scope', '$routeParams', function($scope, $routeParams)  {
    var directionsDisplay;
    var directionsService = new google.maps.DirectionsService();
    var map;

    function intializeMap() {
      directionsDisplay = new google.maps.DirectionsRenderer({
        panel: document.getElementById("directions-info")
      });
      var currentLocation = new google.maps.LatLng(
        localStorage["originLatitude"],
        localStorage["originLongitude"]
      );
      var mapOptions = {
        zoom:7,
        center: currentLocation
      }
      map = new google.maps.Map(document.getElementById("map-canvas"), mapOptions);
      directionsDisplay.setMap(map);
    }

    function calcRoute() {
      var start = new google.maps.LatLng( localStorage["originLatitude"] , localStorage["originLongitude"]);
      var end = new google.maps.LatLng( localStorage["destinationLatitude"] , localStorage["destinationLongitude"]);
      var request = {
        origin:start,
        destination:end,
        travelMode: google.maps.TravelMode.WALKING
      };
      directionsService.route(request, function(result, status) {
        if (status == google.maps.DirectionsStatus.OK) {
          directionsDisplay.setDirections(result);
        }
      });
    }

    $scope.initialize = function(){
        intializeMap();
        calcRoute();
    }
}]);<|endoftext|>


<commit_before>export default DS.Model.extend({
    lastModified: DS.attr('date'),
    modifiedBy: DS.attr(),
    modifiedFields: DS.attr(),

    /**
    * Before saving the record, update the modifiedFields attribute to denote what fields were changed when.
    * Also, if the save failed because of a conflict, reload the record and reapply the changed attributes and
    * attempt to save again.
    */
    save: function(retry) {
        var attribute,
            changedAttributes = this.changedAttributes(),
            modifiedDate = new Date(),
            modifiedFields = this.get('modifiedFields'),
            self = this;
        if (this.get('isDirty') && !this.get('isDeleted')) {
            if (Ember.isEmpty(modifiedFields)) {
                modifiedFields = {};
            }
            this.set('lastModified', modifiedDate);
            for (attribute in changedAttributes) {
                modifiedFields[attribute] = modifiedDate;
            }
            this.set('modifiedFields', modifiedFields);
        }

        return new Ember.RSVP.Promise(function(resolve, reject){
            self._super().then(function(results) {
                Ember.run(null, resolve, results);
            }, function(error) {
                if (retry) {
                    //We failed on the second attempt to save the record, so reject the save.
                    Ember.run(null, reject, error);
                } else {
                    if (error.indexOf('conflict') > -1) {
                        //Conflict encountered, so rollback, reload and then save the record with the changed attributes.
                        self.rollback();
                        self.reload().then(function(record) {
                            for (var attribute in changedAttributes) {
                                record.set(attribute, changedAttributes[attribute][1]);
                            }
                            record.save(true).then(function(results) {
                                Ember.run(null, resolve, results);
                            }, function(err) {
                                Ember.run(null, reject, err);
                            });

                        }, function(err) {
                            Ember.run(null, reject, err);
                        });
                    } else {
                        Ember.run(null, reject, error);
                    }
                }
            });
        });
    }
});<commit_msg>Moving validations to model instead of controllers.<commit_after>export default DS.Model.extend(Ember.Validations.Mixin, {
    lastModified: DS.attr('date'),
    modifiedBy: DS.attr(),
    modifiedFields: DS.attr(),

    /**
    * Before saving the record, update the modifiedFields attribute to denote what fields were changed when.
    * Also, if the save failed because of a conflict, reload the record and reapply the changed attributes and
    * attempt to save again.
    */
    save: function(retry) {
        var attribute,
            changedAttributes = this.changedAttributes(),
            modifiedDate = new Date(),
            modifiedFields = this.get('modifiedFields'),
            self = this;
        if (this.get('isDirty') && !this.get('isDeleted')) {
            if (Ember.isEmpty(modifiedFields)) {
                modifiedFields = {};
            }
            this.set('lastModified', modifiedDate);
            for (attribute in changedAttributes) {
                modifiedFields[attribute] = modifiedDate;
            }
            this.set('modifiedFields', modifiedFields);
        }

        return new Ember.RSVP.Promise(function(resolve, reject){
            self._super().then(function(results) {
                Ember.run(null, resolve, results);
            }, function(error) {
                if (retry) {
                    //We failed on the second attempt to save the record, so reject the save.
                    Ember.run(null, reject, error);
                } else {
                    if (error.indexOf('conflict') > -1) {
                        //Conflict encountered, so rollback, reload and then save the record with the changed attributes.
                        self.rollback();
                        self.reload().then(function(record) {
                            for (var attribute in changedAttributes) {
                                record.set(attribute, changedAttributes[attribute][1]);
                            }
                            record.save(true).then(function(results) {
                                Ember.run(null, resolve, results);
                            }, function(err) {
                                Ember.run(null, reject, err);
                            });

                        }, function(err) {
                            Ember.run(null, reject, err);
                        });
                    } else {
                        Ember.run(null, reject, error);
                    }
                }
            });
        });
    }
});<|endoftext|>


<commit_before>const child = require('child_process');
const inquirer = require('inquirer');

async function run() {
    await asyncExec('git fetch --tags');
    const { stdout, stderr } = await asyncExec('git tag');

    if (stderr) {
        console.error(stderr);
        process.exit(1);
    }

    const versions = new Map();

    const tags = stdout
        .split(/\n+/)
        .map(tag => tag.trim())
        .filter(tag => tag.length);

    for (let tag of tags) {
        const match = tag.match(/^(\d+)\.(\d+)/);

        if (match) {
            const major = Number(match[1]);
            const minor = Number(match[2]);

            if (major > 0) {
                const info = versions.get(major);

                if (!info || info.minor < minor) {
                    versions.set(major, {
                        minor,
                        tag,
                    });
                }
            }
        }
    }

    const orderedVersions = [...versions.keys()].sort();

    const options = orderedVersions.map(version => {
        const { minor, tag } = versions.get(version);

        return `${`${version}.${minor + 1}`.padEnd(
            10,
            ' '
        )} latest tag: ${tag}`;
    });

    const result = await inquirer.prompt([
        {
            type: 'list',
            name: 'version',
            message: 'Which version do you want to use?',
            choices: options,
            default: options[options.length - 1],
        },
        {
            type: 'list',
            name: 'type',
            message: 'Which type of tag?',
            choices: ['--EMPTY--', 'SANDBOX', 'RC'],
            default: 'SANDBOX',
        },
        {
            type: 'input',
            name: 'comment',
            message: 'Enter the comment',
            validate(msg) {
                if (msg.length > 0) {
                    return true;
                }

                return 'Required';
            },
        },
    ]);

    const index = options.findIndex(o => o === result.version);

    const ver = orderedVersions[index];
    const minor = versions.get(ver).minor + 1;

    let tagName = `${ver}.${minor}`;

    if (result.type && result.type !== '--EMPTY--') {
        tagName += '.' + result.type;
    }

    const commands = [
        `git tag -a '${tagName}' -m '${result.comment}'`,
        `git push origin '${tagName}'`,
    ];

    const confirm = await inquirer.prompt({
        message: `Do you want to execute?\n${commands.join(' && ')}`,
        type: 'confirm',
        name: 'ok',
    });

    if (confirm.ok) {
        for (let command of commands) {
            const { stdout, stderr } = await asyncExec(command);

            if (stderr) {
                console.error(stderr);
                process.exit(1);
            } else {
                console.log(stdout);
            }
        }
    }
}

function asyncExec(command) {
    return new Promise((resolve, reject) => {
        child.exec(command, (err, stdout, stderr) => {
            if (err) {
                reject(err);
            } else {
                resolve({ stdout, stderr });
            }
        });
    });
}

run().catch(err => {
    console.error(err);
});
<commit_msg>fix release_tag script<commit_after>const child = require('child_process');
const inquirer = require('inquirer');

async function run() {
    await asyncExec('git fetch --tags');
    const { stdout, stderr } = await asyncExec('git tag');

    if (stderr) {
        console.error(stderr);
        process.exit(1);
    }

    const versions = new Map();

    const tags = stdout
        .split(/\n+/)
        .map(tag => tag.trim())
        .filter(tag => tag.length);

    for (let tag of tags) {
        const match = tag.match(/^(\d+)\.(\d+)/);

        if (match) {
            const major = Number(match[1]);
            const minor = Number(match[2]);

            if (major > 0) {
                const info = versions.get(major);

                if (!info || info.minor < minor) {
                    versions.set(major, {
                        minor,
                        tag,
                    });
                }
            }
        }
    }

    const orderedVersions = [...versions.keys()].sort();

    const options = orderedVersions.map(version => {
        const { minor, tag } = versions.get(version);

        return `${`${version}.${minor + 1}`.padEnd(
            10,
            ' '
        )} latest tag: ${tag}`;
    });

    const result = await inquirer.prompt([
        {
            type: 'list',
            name: 'version',
            message: 'Which version do you want to use?',
            choices: options,
            default: options[options.length - 1],
        },
        {
            type: 'list',
            name: 'type',
            message: 'Which type of tag?',
            choices: ['--EMPTY--', 'SANDBOX', 'RC'],
            default: 'SANDBOX',
        },
        {
            type: 'input',
            name: 'comment',
            message: 'Enter the comment',
            validate(msg) {
                if (msg.length > 0) {
                    return true;
                }

                return 'Required';
            },
        },
    ]);

    const index = options.findIndex(o => o === result.version);

    const ver = orderedVersions[index];
    const minor = versions.get(ver).minor + 1;

    let tagName = `${ver}.${minor}`;

    if (result.type && result.type !== '--EMPTY--') {
        tagName += '_' + result.type;
    }

    const commands = [
        `git tag -a '${tagName}' -m '${result.comment}'`,
        `git push origin '${tagName}'`,
    ];

    const confirm = await inquirer.prompt({
        message: `Do you want to execute?\n${commands.join(' && ')}`,
        type: 'confirm',
        name: 'ok',
    });

    if (confirm.ok) {
        for (let command of commands) {
            const { stdout, stderr } = await asyncExec(command);

            if (stderr) {
                console.error(stderr);
                process.exit(1);
            } else {
                console.log(stdout);
            }
        }
    }
}

function asyncExec(command) {
    return new Promise((resolve, reject) => {
        child.exec(command, (err, stdout, stderr) => {
            if (err) {
                reject(err);
            } else {
                resolve({ stdout: stdout.trim(), stderr: stderr.trim() });
            }
        });
    });
}

run().catch(err => {
    console.error(err);
});
<|endoftext|>


<commit_before>var mongoose = require('mongoose');

var User = require('./user');

//Facebook schema
var facebookSchema = mongoose.Schema({
    id: String,
    uid: String,
    name: String,
    username: String,
    email: String,
    token: String,
    user: {
        type: mongoose.Schema.Types.ObjectId,
        ref: 'User'
    }
});

module.exports = mongoose.model('Facebook', facebookSchema);
<commit_msg>Unused field changed<commit_after>var mongoose = require('mongoose');

var User = require('./user');

//Facebook schema
var facebookSchema = mongoose.Schema({
    id: String,
    uid: String,
    name: String,
    email: String,
    token: String,
    user: {
        type: mongoose.Schema.Types.ObjectId,
        ref: 'User'
    }
});

module.exports = mongoose.model('Facebook', facebookSchema);
<|endoftext|>


<commit_before>import DS from 'ember-data';

export default DS.Model.extend({
  nombre: DS.attr('string'),
  calle: DS.attr('string'),
  colonia: DS.attr('string'),
  ciudadEstado: DS.attr('string'),
  telefono: DS.attr('string'),
  telefono2: DS.attr('string'),
  estadoCivil: DS.attr('string'),
  enviadoPor: DS.attr('string'),
  afiliacion: DS.attr('string'),
  empleado: DS.attr('string'),
  fechaIngreso: DS.attr('date'),
  edad: DS.attr('number'),
  fechaNacimiente: DS.attr('date'),
  sexo: DS.attr('string'),
  procedencia: DS.attr('string'),
  motivo1: DS.attr('string'),
  motivo2: DS.attr('string'),
  ocupacion: DS.attr('string'),
  factura: DS.attr('boolean')
});
<commit_msg>Renames factor to hasFactura to make it sounds like a boolean<commit_after>import DS from 'ember-data';

export default DS.Model.extend({
  nombre: DS.attr('string'),
  calle: DS.attr('string'),
  colonia: DS.attr('string'),
  ciudadEstado: DS.attr('string'),
  telefono: DS.attr('string'),
  telefono2: DS.attr('string'),
  estadoCivil: DS.attr('string'),
  enviadoPor: DS.attr('string'),
  afiliacion: DS.attr('string'),
  empleado: DS.attr('string'),
  fechaIngreso: DS.attr('date'),
  edad: DS.attr('number'),
  fechaNacimiente: DS.attr('date'),
  sexo: DS.attr('string'),
  procedencia: DS.attr('string'),
  motivo1: DS.attr('string'),
  motivo2: DS.attr('string'),
  ocupacion: DS.attr('string'),
  hasFactura: DS.attr('boolean')
});
<|endoftext|>


<commit_before>"use strict";

var Promise = require('bluebird')
  , uuid = require('uuid')
  , inherits = require("util").inherits
  , models = require('../models')
  , AbstractModel = models.AbstractModel
  , FeedFactory = models.FeedFactory
  , Post = models.Post
  , mkKey = require("../support/models").mkKey
  , pubSub = models.PubSub
  , _ = require('lodash')

exports.addModel = function(database) {
  /**
   * @constructor
   */
  var Timeline = function(params) {
    Timeline.super_.call(this)

    this.id = params.id
    this.name = params.name
    this.userId = params.userId
    if (parseInt(params.createdAt, 10))
      this.createdAt = params.createdAt
    if (parseInt(params.updatedAt, 10))
      this.updatedAt = params.updatedAt
    this.offset = parseInt(params.offset, 10) || 0
    this.limit = parseInt(params.limit, 10) || 30
    this.currentUser = params.currentUser
  }

  inherits(Timeline, AbstractModel)

  Timeline.className = Timeline
  Timeline.namespace = "timeline"
  Timeline.findById = Timeline.super_.findById

  Object.defineProperty(Timeline.prototype, 'name', {
    get: function() { return this.name_ },
    set: function(newValue) {
      newValue ? this.name_ = newValue.trim() : this.name_ = ''
    }
  })

  /**
   * Adds the specified post to all timelines where it needs to appear
   * (the timelines of the feeds to which it is posted, the River of News
   * timeline of the posting user and the River of News timelines of all
   * subscribers of the feeds to which it is posted).
   */
  Timeline.publishPost = function(post) {
    var that = this
    var currentTime = new Date().getTime()

    // We can use post.timelineIds here instead of post.getPostedToIds
    // because we are about to create that post and have just received
    // a request from user, so postedToIds == timelineIds here
    return Promise.map(post.timelineIds, function(timelineId) {
      return Timeline.findById(timelineId)
    })
      .then(function(timelines) {
        return Promise.map(timelines, function(timeline) {
          return timeline.getUser()
            .then(function(feed) { return feed.updateLastActivityAt() })
            .then(function() { return timeline.getSubscribedTimelineIds() })
        })
      })
      .then(function(allSubscribedTimelineIds) {
        var allTimelines = _.uniq(
          _.union(post.timelineIds, _.flatten(allSubscribedTimelineIds)))
        return Promise.map(allTimelines, function(timelineId) {
          return Promise.all([
            database.zaddAsync(mkKey(['timeline', timelineId, 'posts']), currentTime, post.id),
            database.hsetAsync(mkKey(['post', post.id]), 'updatedAt', currentTime),
            database.saddAsync(mkKey(['post', post.id, 'timelines']), timelineId)
          ])
        })
      })
      .then(function() { return pubSub.newPost(post.id) })
  }

  Timeline.prototype.validate = function() {
    return new Promise(function(resolve, reject) {
      var valid

      valid = this.name
        && this.name.length > 0
        && this.userId
        && this.userId.length > 0

      valid ? resolve(valid) : reject(new Error("Invalid"))
    }.bind(this))
  }

  Timeline.prototype.validateOnCreate = function() {
    var that = this

    return new Promise(function(resolve, reject) {
      Promise.join(that.validate(),
                   that.validateUniquness(mkKey(['timeline', that.id])),
                   function(valid, idIsUnique) {
                     resolve(that)
                   })
        .catch(function(e) { reject(e) })
      })
  }

  Timeline.prototype.create = function() {
    var that = this

    return new Promise(function(resolve, reject) {
      that.createdAt = new Date().getTime()
      that.updatedAt = new Date().getTime()
      if (!that.id)
        that.id = uuid.v4()

      that.validateOnCreate()
        .then(function(timeline) {
          return Promise.all([
            database.hmsetAsync(mkKey(['user', that.userId, 'timelines']),
                                that.name, that.id),
            database.hmsetAsync(mkKey(['timeline', that.id]),
                                { 'name': that.name,
                                  'userId': that.userId,
                                  'createdAt': that.createdAt.toString(),
                                  'updatedAt': that.updatedAt.toString()
                                })
          ])
        })
        .then(function(res) { resolve(that) })
        .catch(function(e) { reject(e) })
    })
  }

  Timeline.prototype.getPostIds = function(offset, limit) {
    if (_.isUndefined(offset))
      offset = this.offset
    else
      if (offset < 0) offset = 0
    // -1 = special magic number, meaning “do not use limit defaults,
    // do not use passed in value, use 0 instead". this is at the very least
    // used in Timeline.mergeTo()
    if (_.isUndefined(limit))
      limit = this.limit
    else
      if (limit < 0) limit = 0

    var that = this
    return new Promise(function(resolve, reject) {
      that.validateCanShow(that.currentUser)
        .then(function(valid) {
          // this is a private timeline, you shall not pass
          if (!valid)
            return resolve([])

          return database.zrevrangeAsync(mkKey(['timeline', that.id, 'posts']), offset, offset+limit-1)
        })
        .then(function(postIds) {
          that.postIds = postIds
          resolve(that.postIds)
        })
    })
  }

  Timeline.prototype.getPostIdsByScore = function(min, max) {
    var that = this

    return new Promise(function(resolve, reject) {
      database.zrevrangebyscoreAsync(mkKey(['timeline', that.id, 'posts']), min, max)
        .then(function(postIds) {
          that.postIds = postIds
          resolve(that.postIds)
        })
    })
  }

  Timeline.prototype.getPosts = async function(offset, limit) {
    if (_.isUndefined(offset))
      offset = this.offset
    else if (offset < 0)
      offset = 0

    if (_.isUndefined(limit))
      limit = this.limit
    else if (limit < 0)
      limit = 0

    let user = this.currentUser ? (await models.User.findById(this.currentUser)) : null
    let banIds = user ? (await user.getBanIds()) : []

    let postIds = await this.getPostIds(offset, limit)
    let posts = await* postIds.map(postId => Post.findById(postId, { currentUser: this.currentUser }))

    posts = await* posts.filter(Boolean).map(async (post) => {
      let user = await models.User.findById(post.userId)
      let reverseBanIds = await user.getBanIds()

      return ((banIds.indexOf(post.userId) >= 0) || (reverseBanIds.indexOf(this.currentUser) >= 0)) ? null : post
    })

    this.posts = posts.filter(Boolean)

    return this.posts
  }

  /**
   * Merges contents of this timeline into timeline specified by id
   * @param timelineId
   */
  Timeline.prototype.mergeTo = async function(timelineId) {
    await database.zunionstoreAsync(
      mkKey(['timeline', timelineId, 'posts']), 2,
      mkKey(['timeline', timelineId, 'posts']),
      mkKey(['timeline', this.id, 'posts']),
      'AGGREGATE', 'MAX'
    )

    let timeline = await Timeline.findById(timelineId)
    let postIds = await timeline.getPostIds(0, -1)

    let promises = postIds.map(postId => database.saddAsync(mkKey(['post', postId, 'timelines']), timelineId))

    await* promises
  }

  Timeline.prototype.unmerge = async function(timelineId) {
    // zinterstore saves results to a key. so we have to
    // create a temporary storage
    var randomKey = mkKey(['timeline', this.id, 'random', uuid.v4()])

    await database.zinterstoreAsync(
      randomKey,
      2,
      mkKey(['timeline', timelineId, 'posts']),
      mkKey(['timeline', this.id, 'posts']),
      'AGGREGATE', 'MAX')

    var postIds = await database.zrangeAsync(randomKey, 0, -1)
    await* _.flatten(postIds.map((postId) => [
      database.sremAsync(mkKey(['post', postId, 'timelines']), timelineId),
      database.zremAsync(mkKey(['timeline', timelineId, 'posts']), postId)
    ]))

    return database.delAsync(randomKey)
  }

  Timeline.prototype.getUser = function() {
    return models.FeedFactory.findById(this.userId)
  }

  /**
   * Returns the IDs of users subscribed to this timeline, as a promise.
   */
  Timeline.prototype.getSubscriberIds = function(includeSelf) {
    var that = this

    return new Promise(function(resolve, reject) {
      database.zrevrangeAsync(mkKey(['timeline', that.id, 'subscribers']), 0, -1)
        .then(function(userIds) {
          // A user is always subscribed to their own posts timeline.
          if (includeSelf && (that.isPosts() || that.isDirects())) {
            userIds = _.uniq(userIds.concat([that.userId]))
          }
          that.subscriberIds = userIds
          resolve(userIds)
        })
    })
  }

  Timeline.prototype.getSubscribers = async function(includeSelf) {
    var userIds = await this.getSubscriberIds(includeSelf)
    var promises = userIds.map((userId) => models.User.findById(userId))

    this.subscribers = await* promises

    return this.subscribers
  }

  /**
   * Returns the list of the 'River of News' timelines of all subscribers to this
   * timeline.
   */
  Timeline.prototype.getSubscribedTimelineIds = async function() {
    var subscribers = await this.getSubscribers(true);
    return await* subscribers.map((subscriber) => subscriber.getRiverOfNewsTimelineId())
  }

  Timeline.prototype.isRiverOfNews = function() {
    return this.name === "RiverOfNews"
  }

  Timeline.prototype.isPosts = function() {
    return this.name === "Posts"
  }

  Timeline.prototype.isLikes = function() {
    return this.name === "Likes"
  }

  Timeline.prototype.isComments = function() {
    return this.name === "Comments"
  }

  Timeline.prototype.isDirects = function() {
    return this.name === "Directs"
  }

  Timeline.prototype.isHides = function() {
    return this.name === "Hides"
  }

  Timeline.prototype.updatePost = async function(postId, action) {
    var currentTime = new Date().getTime()

    var score = await database.zscoreAsync(mkKey(['timeline', this.id, 'posts']), postId)

    // For the time being like does not bump post
    if (action === "like" && score != null)
      return

    await* [
      database.zaddAsync(mkKey(['timeline', this.id, 'posts']), currentTime, postId),
      database.saddAsync(mkKey(['post', postId, 'timelines']), this.id),
      database.hsetAsync(mkKey(['post', postId]), 'updatedAt', currentTime)
    ]

    var feed = await this.getUser()

    // does not update lastActivity on like
    if (action === 'like')
      return null
    else
      return feed.updateLastActivityAt()
  }

  Timeline.prototype.turnIntoPrivate = function() {
    this.posts = []
    this.postIds = []
    this.limit = 0

    return this
  }

  Timeline.prototype.validateCanShow = async function(userId) {
    // owner can read her posts
    if (this.userId === userId)
      return true

    // if post is already in user's feed then she can read it
    if (this.isDirects())
      return this.userId === userId

    // this is a public feed, anyone can read public posts, this is
    // a free country
    var user = await this.getUser()
    if (user && user.isPrivate !== '1')
      return true

    // otherwise user can view post if and only if she is subscriber
    var userIds = await this.getSubscriberIds()
    return userIds.indexOf(userId) >= 0
  }

  return Timeline
}
<commit_msg>code is more readable<commit_after>"use strict";

var Promise = require('bluebird')
  , uuid = require('uuid')
  , inherits = require("util").inherits
  , models = require('../models')
  , AbstractModel = models.AbstractModel
  , FeedFactory = models.FeedFactory
  , Post = models.Post
  , mkKey = require("../support/models").mkKey
  , pubSub = models.PubSub
  , _ = require('lodash')

exports.addModel = function(database) {
  /**
   * @constructor
   */
  var Timeline = function(params) {
    Timeline.super_.call(this)

    this.id = params.id
    this.name = params.name
    this.userId = params.userId
    if (parseInt(params.createdAt, 10))
      this.createdAt = params.createdAt
    if (parseInt(params.updatedAt, 10))
      this.updatedAt = params.updatedAt
    this.offset = parseInt(params.offset, 10) || 0
    this.limit = parseInt(params.limit, 10) || 30
    this.currentUser = params.currentUser
  }

  inherits(Timeline, AbstractModel)

  Timeline.className = Timeline
  Timeline.namespace = "timeline"
  Timeline.findById = Timeline.super_.findById

  Object.defineProperty(Timeline.prototype, 'name', {
    get: function() { return this.name_ },
    set: function(newValue) {
      newValue ? this.name_ = newValue.trim() : this.name_ = ''
    }
  })

  /**
   * Adds the specified post to all timelines where it needs to appear
   * (the timelines of the feeds to which it is posted, the River of News
   * timeline of the posting user and the River of News timelines of all
   * subscribers of the feeds to which it is posted).
   */
  Timeline.publishPost = function(post) {
    var that = this
    var currentTime = new Date().getTime()

    // We can use post.timelineIds here instead of post.getPostedToIds
    // because we are about to create that post and have just received
    // a request from user, so postedToIds == timelineIds here
    return Promise.map(post.timelineIds, function(timelineId) {
      return Timeline.findById(timelineId)
    })
      .then(function(timelines) {
        return Promise.map(timelines, function(timeline) {
          return timeline.getUser()
            .then(function(feed) { return feed.updateLastActivityAt() })
            .then(function() { return timeline.getSubscribedTimelineIds() })
        })
      })
      .then(function(allSubscribedTimelineIds) {
        var allTimelines = _.uniq(
          _.union(post.timelineIds, _.flatten(allSubscribedTimelineIds)))
        return Promise.map(allTimelines, function(timelineId) {
          return Promise.all([
            database.zaddAsync(mkKey(['timeline', timelineId, 'posts']), currentTime, post.id),
            database.hsetAsync(mkKey(['post', post.id]), 'updatedAt', currentTime),
            database.saddAsync(mkKey(['post', post.id, 'timelines']), timelineId)
          ])
        })
      })
      .then(function() { return pubSub.newPost(post.id) })
  }

  Timeline.prototype.validate = function() {
    return new Promise(function(resolve, reject) {
      var valid

      valid = this.name
        && this.name.length > 0
        && this.userId
        && this.userId.length > 0

      valid ? resolve(valid) : reject(new Error("Invalid"))
    }.bind(this))
  }

  Timeline.prototype.validateOnCreate = function() {
    var that = this

    return new Promise(function(resolve, reject) {
      Promise.join(that.validate(),
                   that.validateUniquness(mkKey(['timeline', that.id])),
                   function(valid, idIsUnique) {
                     resolve(that)
                   })
        .catch(function(e) { reject(e) })
      })
  }

  Timeline.prototype.create = function() {
    var that = this

    return new Promise(function(resolve, reject) {
      that.createdAt = new Date().getTime()
      that.updatedAt = new Date().getTime()
      if (!that.id)
        that.id = uuid.v4()

      that.validateOnCreate()
        .then(function(timeline) {
          return Promise.all([
            database.hmsetAsync(mkKey(['user', that.userId, 'timelines']),
                                that.name, that.id),
            database.hmsetAsync(mkKey(['timeline', that.id]),
                                { 'name': that.name,
                                  'userId': that.userId,
                                  'createdAt': that.createdAt.toString(),
                                  'updatedAt': that.updatedAt.toString()
                                })
          ])
        })
        .then(function(res) { resolve(that) })
        .catch(function(e) { reject(e) })
    })
  }

  Timeline.prototype.getPostIds = function(offset, limit) {
    if (_.isUndefined(offset))
      offset = this.offset
    else
      if (offset < 0) offset = 0
    // -1 = special magic number, meaning “do not use limit defaults,
    // do not use passed in value, use 0 instead". this is at the very least
    // used in Timeline.mergeTo()
    if (_.isUndefined(limit))
      limit = this.limit
    else
      if (limit < 0) limit = 0

    var that = this
    return new Promise(function(resolve, reject) {
      that.validateCanShow(that.currentUser)
        .then(function(valid) {
          // this is a private timeline, you shall not pass
          if (!valid)
            return resolve([])

          return database.zrevrangeAsync(mkKey(['timeline', that.id, 'posts']), offset, offset+limit-1)
        })
        .then(function(postIds) {
          that.postIds = postIds
          resolve(that.postIds)
        })
    })
  }

  Timeline.prototype.getPostIdsByScore = function(min, max) {
    var that = this

    return new Promise(function(resolve, reject) {
      database.zrevrangebyscoreAsync(mkKey(['timeline', that.id, 'posts']), min, max)
        .then(function(postIds) {
          that.postIds = postIds
          resolve(that.postIds)
        })
    })
  }

  Timeline.prototype.getPosts = async function(offset, limit) {
    if (_.isUndefined(offset))
      offset = this.offset
    else if (offset < 0)
      offset = 0

    if (_.isUndefined(limit))
      limit = this.limit
    else if (limit < 0)
      limit = 0

    let reader = this.currentUser ? (await models.User.findById(this.currentUser)) : null
    let banIds = reader ? (await reader.getBanIds()) : []

    let postIds = await this.getPostIds(offset, limit)
    let posts = (await* postIds.map(postId => Post.findById(postId, { currentUser: this.currentUser }))).filter(Boolean)

    posts = await* posts.map(async (post) => {
      if (post.userId === this.currentUser) {
        // shortcut for the author
        return post
      }

      let author = await models.User.findById(post.userId)
      let reverseBanIds = await author.getBanIds()

      let readerBannedAuthor = (banIds.indexOf(post.userId) >= 0)
      let authorBannedReader = (reverseBanIds.indexOf(this.currentUser) >= 0)

      if (readerBannedAuthor || authorBannedReader)
        return null

      return post
    })

    this.posts = posts.filter(Boolean)

    return this.posts
  }

  /**
   * Merges contents of this timeline into timeline specified by id
   * @param timelineId
   */
  Timeline.prototype.mergeTo = async function(timelineId) {
    await database.zunionstoreAsync(
      mkKey(['timeline', timelineId, 'posts']), 2,
      mkKey(['timeline', timelineId, 'posts']),
      mkKey(['timeline', this.id, 'posts']),
      'AGGREGATE', 'MAX'
    )

    let timeline = await Timeline.findById(timelineId)
    let postIds = await timeline.getPostIds(0, -1)

    let promises = postIds.map(postId => database.saddAsync(mkKey(['post', postId, 'timelines']), timelineId))

    await* promises
  }

  Timeline.prototype.unmerge = async function(timelineId) {
    // zinterstore saves results to a key. so we have to
    // create a temporary storage
    var randomKey = mkKey(['timeline', this.id, 'random', uuid.v4()])

    await database.zinterstoreAsync(
      randomKey,
      2,
      mkKey(['timeline', timelineId, 'posts']),
      mkKey(['timeline', this.id, 'posts']),
      'AGGREGATE', 'MAX')

    var postIds = await database.zrangeAsync(randomKey, 0, -1)
    await* _.flatten(postIds.map((postId) => [
      database.sremAsync(mkKey(['post', postId, 'timelines']), timelineId),
      database.zremAsync(mkKey(['timeline', timelineId, 'posts']), postId)
    ]))

    return database.delAsync(randomKey)
  }

  Timeline.prototype.getUser = function() {
    return models.FeedFactory.findById(this.userId)
  }

  /**
   * Returns the IDs of users subscribed to this timeline, as a promise.
   */
  Timeline.prototype.getSubscriberIds = function(includeSelf) {
    var that = this

    return new Promise(function(resolve, reject) {
      database.zrevrangeAsync(mkKey(['timeline', that.id, 'subscribers']), 0, -1)
        .then(function(userIds) {
          // A user is always subscribed to their own posts timeline.
          if (includeSelf && (that.isPosts() || that.isDirects())) {
            userIds = _.uniq(userIds.concat([that.userId]))
          }
          that.subscriberIds = userIds
          resolve(userIds)
        })
    })
  }

  Timeline.prototype.getSubscribers = async function(includeSelf) {
    var userIds = await this.getSubscriberIds(includeSelf)
    var promises = userIds.map((userId) => models.User.findById(userId))

    this.subscribers = await* promises

    return this.subscribers
  }

  /**
   * Returns the list of the 'River of News' timelines of all subscribers to this
   * timeline.
   */
  Timeline.prototype.getSubscribedTimelineIds = async function() {
    var subscribers = await this.getSubscribers(true);
    return await* subscribers.map((subscriber) => subscriber.getRiverOfNewsTimelineId())
  }

  Timeline.prototype.isRiverOfNews = function() {
    return this.name === "RiverOfNews"
  }

  Timeline.prototype.isPosts = function() {
    return this.name === "Posts"
  }

  Timeline.prototype.isLikes = function() {
    return this.name === "Likes"
  }

  Timeline.prototype.isComments = function() {
    return this.name === "Comments"
  }

  Timeline.prototype.isDirects = function() {
    return this.name === "Directs"
  }

  Timeline.prototype.isHides = function() {
    return this.name === "Hides"
  }

  Timeline.prototype.updatePost = async function(postId, action) {
    var currentTime = new Date().getTime()

    var score = await database.zscoreAsync(mkKey(['timeline', this.id, 'posts']), postId)

    // For the time being like does not bump post
    if (action === "like" && score != null)
      return

    await* [
      database.zaddAsync(mkKey(['timeline', this.id, 'posts']), currentTime, postId),
      database.saddAsync(mkKey(['post', postId, 'timelines']), this.id),
      database.hsetAsync(mkKey(['post', postId]), 'updatedAt', currentTime)
    ]

    var feed = await this.getUser()

    // does not update lastActivity on like
    if (action === 'like')
      return null
    else
      return feed.updateLastActivityAt()
  }

  Timeline.prototype.turnIntoPrivate = function() {
    this.posts = []
    this.postIds = []
    this.limit = 0

    return this
  }

  Timeline.prototype.validateCanShow = async function(userId) {
    // owner can read her posts
    if (this.userId === userId)
      return true

    // if post is already in user's feed then she can read it
    if (this.isDirects())
      return this.userId === userId

    // this is a public feed, anyone can read public posts, this is
    // a free country
    var user = await this.getUser()
    if (user && user.isPrivate !== '1')
      return true

    // otherwise user can view post if and only if she is subscriber
    var userIds = await this.getSubscriberIds()
    return userIds.indexOf(userId) >= 0
  }

  return Timeline
}
<|endoftext|>


<commit_before>angular.module( 'App.profile', [
  'ui.router',
  'angularFileUpload',
  'ngImgCrop'
])
.config(function config( $stateProvider ) {
  $stateProvider.state( 'editProfile', {
    url: '/profile/edit?webid',
    views: {
      "main": {
        controller: 'EditProfileCtrl',
        templateUrl: 'app/profile/edit.tpl.html'
      }
    },
    data:{ pageTitle: 'Edit profile' }
  });
})
.config(function config( $stateProvider ) {
  $stateProvider.state( 'viewProfile', {
    url: '/profile/view?webid',
    views: {
      "main": {
        controller: 'ViewCtrl',
        templateUrl: 'app/profile/view.tpl.html'
      }
    },
    data:{ pageTitle: 'View profile' }
  });
})
.controller( 'ViewCtrl', function ViewCtrl( $scope, $location, $state, $stateParams ) {
  $scope.form = {};

  $scope.viewProfile = function(webid) {
    if (!$scope.$parent.profiles) {
      $scope.$parent.profiles = [];
    }
    var webid = (webid)?webid:$scope.form.webid;
    if (!$scope.$parent.profiles[webid]) {
      $scope.$parent.profiles[webid] = {};
      $scope.$parent.getProfile(webid, false);
    }
    $scope.profile = $scope.$parent.profiles[webid];
    $scope.$parent.toWebID = $scope.profile.webid;
    $scope.$parent.toLoc = '/profile/edit';
    $location.path("/profile/view").search({'webid': webid}).replace();
    $scope.form = {};
  };

  if ($stateParams['webid']) {
    var webid = $stateParams['webid'];
    // check if it's the authenticated user
    if ($scope.$parent.profile && $scope.$parent.profile.webid == webid) {
      $scope.profile = $scope.$parent.profile;
    } else if ($scope.$parent.profiles[webid] && $scope.$parent.profiles[webid].webid == webid) {
      // load previous profile if it exists
      $scope.profile = $scope.$parent.profiles[webid];
    } else {
      $scope.viewProfile(webid);
    }
  }
  $scope.$parent.toWebID = $scope.profile.webid;
  $scope.$parent.toLoc = '/profile/edit';
})
.controller( 'EditProfileCtrl', function EditProfileCtrl( $scope, $state, $location, $upload, $stateParams ) {
  // blank
  $scope.form = {};
  $scope.pictureFile = {};
  $scope.bgFile = {};
  $scope.profile = {};
  $scope.overlay = $scope.$parent.overlay;

  // Adds
  $scope.addPhone = function() {
    if (!$scope.profile.phones) {
      $scope.profile.phones = [];
    }
    var newPhone = new $scope.$parent.ProfileElement(
      $rdf.st(
        $rdf.sym($scope.profile.webid),
        FOAF('phone'),
        $rdf.sym(''),
        $rdf.sym('')
      )
    );
    $scope.profile.phones.push(newPhone);
  };
  $scope.addEmail = function() {
    if (!$scope.profile.emails) {
      $scope.profile.emails = [];
    }
    var newEmail = new $scope.$parent.ProfileElement(
      $rdf.st(
        $rdf.sym($scope.profile.webid),
        FOAF('mbox'),
        $rdf.sym(''),
        $rdf.sym('')
      )
    );
    $scope.profile.emails.push(newEmail);
  };
  $scope.addBlog = function() {
    if (!$scope.profile.blogs) {
      $scope.profile.blogs = [];
    }
    var newBlog = new $scope.$parent.ProfileElement(
      $rdf.st(
        $rdf.sym($scope.profile.webid),
        FOAF('weblog'),
        $rdf.sym(''),
        $rdf.sym('')
      )
    );
    $scope.profile.blogs.push(newBlog);
  };
  $scope.addHomepage = function() {
    if (!$scope.profile.homepages) {
      $scope.profile.homepages = [];
    }
    var newHomepage = new $scope.$parent.ProfileElement(
      $rdf.st(
        $rdf.sym($scope.profile.webid),
        FOAF('homepage'),
        $rdf.sym(''),
        $rdf.sym('')
      )
    );
    $scope.profile.homepages.push(newHomepage);
  };
  $scope.addWorkpage = function() {
    if (!$scope.profile.workpages) {
      $scope.profile.workpages = [];
    }
    var newWorkpage = new $scope.$parent.ProfileElement(
      $rdf.st(
        $rdf.sym($scope.profile.webid),
        FOAF('workplaceHomepage'),
        $rdf.sym(''),
        $rdf.sym('')
      )
    );
    $scope.profile.workpages.push(newWorkpage);
  };

  // Deletes
  $scope.deletePhone = function(id) {
    $scope.profile.phones[id].value = '';
    $scope.updateObject($scope.profile.phones[id]);
    $scope.profile.phones.splice(id, 1);
  };
  $scope.deleteEmail = function(id) {
    $scope.profile.emails[id].value = '';
    $scope.updateObject($scope.profile.emails[id]);
    $scope.profile.emails.splice(id, 1);
  };
  $scope.deleteBlog = function(id) {
    $scope.profile.blogs[id].value = '';
    $scope.updateObject($scope.profile.blogs[id]);
    $scope.profile.blogs.splice(id, 1);
  };
  $scope.deleteHomepage = function(id) {
    $scope.profile.homepages[id].value = '';
    $scope.updateObject($scope.profile.homepages[id]);
    $scope.profile.homepages.splice(id, 1);
  };
  $scope.deleteWorkpage = function(id) {
    $scope.profile.workpages[id].value = '';
    $scope.updateObject($scope.profile.workpages[id]);
    $scope.profile.workpages.splice(id, 1);
  };

  $scope.deletePicture = function() {
    $scope.profile.picture.value = '';
    $scope.updateObject($scope.profile.picture);
  };

  $scope.login = function() {
    $scope.$parent.login(false);
  };

  // Updates
  // select file for picture
  $scope.handleFileSelect = function(file) {
    if (file) {
      $scope.pictureName = file.name;
      $scope.imageType = file.type;
      var reader = new FileReader();
      reader.onload = function (evt) {
        $scope.$apply(function($scope){
          $scope.originalImage=evt.target.result;
        });
      };
      reader.readAsDataURL(file);
    }
  };

  $scope.dataURItoBlob = function(dataURI) {
    var data = dataURI.split(',')[1];
    // var binary = atob(data);
    var binary;
    if (dataURI.split(',')[0].indexOf('base64') >= 0)
        binary = atob(data);
    else
        binary = decodeURI(data);

    var buffer = new ArrayBuffer(binary.length);
    var ia = new Uint8Array(buffer);
    for (var i = 0; i < binary.length; i++) {
        ia[i] = binary.charCodeAt(i);
    }
    var blob = new Blob([ia], {type: $scope.imageType});

    return blob;
    // new File() is not supported by Safari for now
    // return new File([blob.buffer], $scope.pictureName, {
    //   lastModified: new Date(0),
    //   type: $scope.imageType
    // });
  };

  // update a value and patch profile
  $scope.updateObject = function (obj, force) {
    // update object and also patch graph
    if (obj.value && obj.statement.why.value.length == 0 && $scope.profile.sources.length > 0) {
      obj.picker = true;
    } else {
      obj.updateObject(true, force);
    }
  };

  $scope.uploadPicture = function (obj, file, filename) {
    if (obj && file && filename) {
      var newPicURL = '';
      newPicContainer = dirname(obj.statement.why.value)+'/';
      obj.uploading = true;
      $upload.upload({
          method: 'POST',
          url: newPicContainer,
          withCredentials: true,
          file: file,
          fileName: filename
      }).success(function (data, status, headers, config) {
        var pic = headers("Location");
        obj.value = pic;
        $scope.updateObject(obj, true);
      }).error(function (data, status, headers, config) {
        $scope.uploading = false;
        Notifier.error('Could not upload picture -- HTTP '+status);
      });
    }
  };

  $scope.savePicture = function() {
    console.log("Saving picture...");
    var newImg = $scope.dataURItoBlob($scope.croppedImage);
    if ($scope.profile.picture.statement.why.value.length == 0 && $scope.profile.sources.length > 0) {
      $scope.profile.picture.picker = true;
    } else {
      $scope.uploadPicture($scope.profile.picture, newImg, $scope.pictureName);
    }
  };
  $scope.saveBackground = function() {
    console.log("Saving background picture...");
    if ($scope.profile.bgpicture.statement.why.value.length == 0 && $scope.profile.sources.length > 0) {
      $scope.profile.bgpicture.picker = true;
    } else if ($scope.bgFile.file[0]) {
      $scope.uploadPicture($scope.profile.bgpicture, $scope.bgFile.file[0], $scope.bgFile.file[0].name);
    }
  }

  // replace white spaces with dashes (for phone numbers)
  $scope.space2dash = function(obj) {
    obj.value = (!obj.value) ? '' : obj.value.replace(/\s+/g, '-');
  };

  $scope.editProfile = function(webid) {
    if (!$scope.$parent.profiles) {
      $scope.$parent.profiles = [];
    }
    var webid = (webid)?webid:$scope.form.webid;
    if (!$scope.$parent.profiles[webid]) {
      console.log("No profile exists for "+webid);
      $scope.$parent.profiles[webid] = {};
      $scope.$parent.getProfile(webid, false, false);
    }
    $scope.profile = $scope.$parent.profiles[webid];
    $scope.$parent.toWebID = $scope.profile.webid;
    $scope.$parent.toLoc = '/profile/view';
    $location.path("/profile/edit").search({'webid': webid}).replace();
    console.log($scope.profile);
  }

  if (!$scope.profile.webid) {
    if ($stateParams['webid']) {
      var webid = $scope.form.webid = $stateParams['webid'];
      // check if it's the authenticated user
      if ($scope.$parent.profile && $scope.$parent.profile.webid == webid) {
        $scope.profile = $scope.$parent.profile;
      } else if ($scope.$parent.profiles[webid] && $scope.$parent.profiles[webid].webid == webid) {
        // load previous existing profile
        $scope.profile = $scope.$parent.profiles[webid];
      } else {
        $scope.editProfile(webid);
      }
    } else {
      $scope.profile = $scope.$parent.profile;
    }
  }
  $scope.$parent.toWebID = $scope.profile.webid;
  $scope.$parent.toLoc = '/profile/view';

  $scope.$watch('pictureFile.file', function (newFile, oldFile) {
    if (newFile != undefined) {
      $scope.originalImage = '';
      $scope.imageName = '';
      $scope.croppedImage = '2';
      $scope.handleFileSelect(newFile[0]);
      $('#picture-cropper').openModal();
    }
  });
  $scope.$watch('bgFile.file', function (newFile, oldFile) {
    if (newFile != undefined) {
      $scope.saveBackground();
    }
  });
})
.directive('pickSource', function () {
    return {
        restrict: 'AE',
        scope: {
          obj: '='
        },
        transclude: true,
        template: '
'+
                  '
'+
                  '
'+
                  '
Put this information in:
'+
                  '
'+
                  '
'+
                  '
'+
                  '
'+
                  '          '+
                  '          {{src.name}}'+
                  '
'+
                  '
'+
                  '
'+
                  '
'+
                  '
'+
                  '        Cancel'+
                  '
'+
                  '
'+
                  '
'+
                  '
',
        link: function($scope, $element, $attrs) {
          $('#location-picker').openModal();
          $scope.$parent.overlay = true;

          $scope.setWhy = function(uri) {
            $scope.obj.statement['why']['uri'] = $scope.obj.statement['why']['value'] = uri;
            console.log("Set Why to:"+uri);
            console.log($scope.obj.statement);
            if ($scope.obj.statement.predicate.value == FOAF('img').value || $scope.obj.statement.predicate.value == FOAF('depiction').value) {
              console.log("Supposed to save picture");
              $scope.$parent.savePicture();
            } else if ($scope.obj.statement.predicate.value == UI('backgroundImage').value) {
              console.log("Supposed to save bgpicture");
              $scope.$parent.saveBackground();
            } else {
              console.log("Supposed to update obj");
              $scope.$parent.updateObject($scope.obj);
            }
            $scope.cancel();
          }
          $scope.cancel = function() {
            $scope.$parent.overlay = false;
            $scope.obj.picker = false;
            $('#location-picker').closeModal();
          }
        }
    };
});
<commit_msg>Use PUT instead of multipart upload<commit_after>angular.module( 'App.profile', [
  'ui.router',
  'angularFileUpload',
  'ngImgCrop'
])
.config(function config( $stateProvider ) {
  $stateProvider.state( 'editProfile', {
    url: '/profile/edit?webid',
    views: {
      "main": {
        controller: 'EditProfileCtrl',
        templateUrl: 'app/profile/edit.tpl.html'
      }
    },
    data:{ pageTitle: 'Edit profile' }
  });
})
.config(function config( $stateProvider ) {
  $stateProvider.state( 'viewProfile', {
    url: '/profile/view?webid',
    views: {
      "main": {
        controller: 'ViewCtrl',
        templateUrl: 'app/profile/view.tpl.html'
      }
    },
    data:{ pageTitle: 'View profile' }
  });
})
.controller( 'ViewCtrl', function ViewCtrl( $scope, $location, $state, $stateParams ) {
  $scope.form = {};

  $scope.viewProfile = function(webid) {
    if (!$scope.$parent.profiles) {
      $scope.$parent.profiles = [];
    }
    var webid = (webid)?webid:$scope.form.webid;
    if (!$scope.$parent.profiles[webid]) {
      $scope.$parent.profiles[webid] = {};
      $scope.$parent.getProfile(webid, false);
    }
    $scope.profile = $scope.$parent.profiles[webid];
    $scope.$parent.toWebID = $scope.profile.webid;
    $scope.$parent.toLoc = '/profile/edit';
    $location.path("/profile/view").search({'webid': webid}).replace();
    $scope.form = {};
  };

  if ($stateParams['webid']) {
    var webid = $stateParams['webid'];
    // check if it's the authenticated user
    if ($scope.$parent.profile && $scope.$parent.profile.webid == webid) {
      $scope.profile = $scope.$parent.profile;
    } else if ($scope.$parent.profiles[webid] && $scope.$parent.profiles[webid].webid == webid) {
      // load previous profile if it exists
      $scope.profile = $scope.$parent.profiles[webid];
    } else {
      $scope.viewProfile(webid);
    }
  }
  $scope.$parent.toWebID = $scope.profile.webid;
  $scope.$parent.toLoc = '/profile/edit';
})
.controller( 'EditProfileCtrl', function EditProfileCtrl( $scope, $state, $location, $http, $stateParams ) {
  // blank
  $scope.form = {};
  $scope.pictureFile = {};
  $scope.bgFile = {};
  $scope.profile = {};
  $scope.overlay = $scope.$parent.overlay;

  // Adds
  $scope.addPhone = function() {
    if (!$scope.profile.phones) {
      $scope.profile.phones = [];
    }
    var newPhone = new $scope.$parent.ProfileElement(
      $rdf.st(
        $rdf.sym($scope.profile.webid),
        FOAF('phone'),
        $rdf.sym(''),
        $rdf.sym('')
      )
    );
    $scope.profile.phones.push(newPhone);
  };
  $scope.addEmail = function() {
    if (!$scope.profile.emails) {
      $scope.profile.emails = [];
    }
    var newEmail = new $scope.$parent.ProfileElement(
      $rdf.st(
        $rdf.sym($scope.profile.webid),
        FOAF('mbox'),
        $rdf.sym(''),
        $rdf.sym('')
      )
    );
    $scope.profile.emails.push(newEmail);
  };
  $scope.addBlog = function() {
    if (!$scope.profile.blogs) {
      $scope.profile.blogs = [];
    }
    var newBlog = new $scope.$parent.ProfileElement(
      $rdf.st(
        $rdf.sym($scope.profile.webid),
        FOAF('weblog'),
        $rdf.sym(''),
        $rdf.sym('')
      )
    );
    $scope.profile.blogs.push(newBlog);
  };
  $scope.addHomepage = function() {
    if (!$scope.profile.homepages) {
      $scope.profile.homepages = [];
    }
    var newHomepage = new $scope.$parent.ProfileElement(
      $rdf.st(
        $rdf.sym($scope.profile.webid),
        FOAF('homepage'),
        $rdf.sym(''),
        $rdf.sym('')
      )
    );
    $scope.profile.homepages.push(newHomepage);
  };
  $scope.addWorkpage = function() {
    if (!$scope.profile.workpages) {
      $scope.profile.workpages = [];
    }
    var newWorkpage = new $scope.$parent.ProfileElement(
      $rdf.st(
        $rdf.sym($scope.profile.webid),
        FOAF('workplaceHomepage'),
        $rdf.sym(''),
        $rdf.sym('')
      )
    );
    $scope.profile.workpages.push(newWorkpage);
  };

  // Deletes
  $scope.deletePhone = function(id) {
    $scope.profile.phones[id].value = '';
    $scope.updateObject($scope.profile.phones[id]);
    $scope.profile.phones.splice(id, 1);
  };
  $scope.deleteEmail = function(id) {
    $scope.profile.emails[id].value = '';
    $scope.updateObject($scope.profile.emails[id]);
    $scope.profile.emails.splice(id, 1);
  };
  $scope.deleteBlog = function(id) {
    $scope.profile.blogs[id].value = '';
    $scope.updateObject($scope.profile.blogs[id]);
    $scope.profile.blogs.splice(id, 1);
  };
  $scope.deleteHomepage = function(id) {
    $scope.profile.homepages[id].value = '';
    $scope.updateObject($scope.profile.homepages[id]);
    $scope.profile.homepages.splice(id, 1);
  };
  $scope.deleteWorkpage = function(id) {
    $scope.profile.workpages[id].value = '';
    $scope.updateObject($scope.profile.workpages[id]);
    $scope.profile.workpages.splice(id, 1);
  };

  $scope.deletePicture = function() {
    $scope.profile.picture.value = '';
    $scope.updateObject($scope.profile.picture);
  };

  $scope.login = function() {
    $scope.$parent.login(false);
  };

  // Updates
  // select file for picture
  $scope.handleFileSelect = function(file) {
    if (file) {
      $scope.pictureName = file.name;
      $scope.imageType = file.type;
      var reader = new FileReader();
      reader.onload = function (evt) {
        $scope.$apply(function($scope){
          $scope.originalImage=evt.target.result;
        });
      };
      reader.readAsDataURL(file);
    }
  };

  $scope.dataURItoBlob = function(dataURI) {
    var data = dataURI.split(',')[1];
    // var binary = atob(data);
    var binary;
    if (dataURI.split(',')[0].indexOf('base64') >= 0)
        binary = atob(data);
    else
        binary = decodeURI(data);

    var buffer = new ArrayBuffer(binary.length);
    var ia = new Uint8Array(buffer);
    for (var i = 0; i < binary.length; i++) {
        ia[i] = binary.charCodeAt(i);
    }
    var blob = new Blob([ia], {type: $scope.imageType});

    return blob;
    // new File() is not supported by Safari for now
    // return new File([blob.buffer], $scope.pictureName, {
    //   lastModified: new Date(0),
    //   type: $scope.imageType
    // });
  };

  // update a value and patch profile
  $scope.updateObject = function (obj, force) {
    // update object and also patch graph
    if (obj.value && obj.statement.why.value.length == 0 && $scope.profile.sources.length > 0) {
      obj.picker = true;
    } else {
      obj.updateObject(true, force);
    }
  };

  $scope.uploadPicture = function (obj, file, filename) {
    if (obj && file && filename) {
      newPicContainer = dirname(obj.statement.why.value)+'/';
      obj.uploading = true;
      var newPicURL = newPicContainer + filename;
      $http({
        method: 'PUT',
        url: newPicURL,
        headers: {
          'Content-Type': $scope.imageType
        },
        withCredentials: true,
        data: file
      }).success(function () {
        obj.value = newPicURL;
        $scope.updateObject(obj, true);
      }).error(function () {
        $scope.uploading = false;
        Notifier.error('Could not upload picture to ' + newPicURL + ' -- HTTP '+status);
      });
    }
  };

  $scope.savePicture = function() {
    console.log("Saving picture...");
    var newImg = $scope.dataURItoBlob($scope.croppedImage);
    if ($scope.profile.picture.statement.why.value.length == 0 && $scope.profile.sources.length > 0) {
      $scope.profile.picture.picker = true;
    } else {
      $scope.uploadPicture($scope.profile.picture, newImg, $scope.pictureName);
    }
  };
  $scope.saveBackground = function() {
    console.log("Saving background picture...");
    if ($scope.profile.bgpicture.statement.why.value.length == 0 && $scope.profile.sources.length > 0) {
      $scope.profile.bgpicture.picker = true;
    } else if ($scope.bgFile.file[0]) {
      $scope.uploadPicture($scope.profile.bgpicture, $scope.bgFile.file[0], $scope.bgFile.file[0].name);
    }
  }

  // replace white spaces with dashes (for phone numbers)
  $scope.space2dash = function(obj) {
    obj.value = (!obj.value) ? '' : obj.value.replace(/\s+/g, '-');
  };

  $scope.editProfile = function(webid) {
    if (!$scope.$parent.profiles) {
      $scope.$parent.profiles = [];
    }
    var webid = (webid)?webid:$scope.form.webid;
    if (!$scope.$parent.profiles[webid]) {
      console.log("No profile exists for "+webid);
      $scope.$parent.profiles[webid] = {};
      $scope.$parent.getProfile(webid, false, false);
    }
    $scope.profile = $scope.$parent.profiles[webid];
    $scope.$parent.toWebID = $scope.profile.webid;
    $scope.$parent.toLoc = '/profile/view';
    $location.path("/profile/edit").search({'webid': webid}).replace();
    console.log($scope.profile);
  }

  if (!$scope.profile.webid) {
    if ($stateParams['webid']) {
      var webid = $scope.form.webid = $stateParams['webid'];
      // check if it's the authenticated user
      if ($scope.$parent.profile && $scope.$parent.profile.webid == webid) {
        $scope.profile = $scope.$parent.profile;
      } else if ($scope.$parent.profiles[webid] && $scope.$parent.profiles[webid].webid == webid) {
        // load previous existing profile
        $scope.profile = $scope.$parent.profiles[webid];
      } else {
        $scope.editProfile(webid);
      }
    } else {
      $scope.profile = $scope.$parent.profile;
    }
  }
  $scope.$parent.toWebID = $scope.profile.webid;
  $scope.$parent.toLoc = '/profile/view';

  $scope.$watch('pictureFile.file', function (newFile, oldFile) {
    if (newFile != undefined) {
      $scope.originalImage = '';
      $scope.imageName = '';
      $scope.croppedImage = '2';
      $scope.handleFileSelect(newFile[0]);
      $('#picture-cropper').openModal();
    }
  });
  $scope.$watch('bgFile.file', function (newFile, oldFile) {
    if (newFile != undefined) {
      $scope.saveBackground();
    }
  });
})
.directive('pickSource', function () {
    return {
        restrict: 'AE',
        scope: {
          obj: '='
        },
        transclude: true,
        template: '
'+
                  '
'+
                  '
'+
                  '
Put this information in:
'+
                  '
'+
                  '
'+
                  '
'+
                  '
'+
                  '          '+
                  '          {{src.name}}'+
                  '
'+
                  '
'+
                  '
'+
                  '
'+
                  '
'+
                  '        Cancel'+
                  '
'+
                  '
'+
                  '
'+
                  '
',
        link: function($scope, $element, $attrs) {
          $('#location-picker').openModal();
          $scope.$parent.overlay = true;

          $scope.setWhy = function(uri) {
            $scope.obj.statement['why']['uri'] = $scope.obj.statement['why']['value'] = uri;
            console.log("Set Why to:"+uri);
            console.log($scope.obj.statement);
            if ($scope.obj.statement.predicate.value == FOAF('img').value || $scope.obj.statement.predicate.value == FOAF('depiction').value) {
              console.log("Supposed to save picture");
              $scope.$parent.savePicture();
            } else if ($scope.obj.statement.predicate.value == UI('backgroundImage').value) {
              console.log("Supposed to save bgpicture");
              $scope.$parent.saveBackground();
            } else {
              console.log("Supposed to update obj");
              $scope.$parent.updateObject($scope.obj);
            }
            $scope.cancel();
          }
          $scope.cancel = function() {
            $scope.$parent.overlay = false;
            $scope.obj.picker = false;
            $('#location-picker').closeModal();
          }
        }
    };
});
<|endoftext|>