<commit_before>/*!
Name: Reading Time
Dependencies: jQuery
Author: Michael Lynch
Author URL: http://michaelynch.com
Date Created: August 14, 2013
Date Updated: February 28, 2018
Licensed under the MIT license
*/

;(function($) {

	$.fn.readingTime = function(options) {

		// define default parameters
		const defaults = {
			readingTimeTarget: '.eta',
			readingTimeAsNumber: false,
			wordCountTarget: null,
			wordsPerMinute: 270,
			round: true,
			lang: 'en',
			lessThanAMinuteString: '',
			prependTimeString: '',
			prependWordString: '',
			remotePath: null,
			remoteTarget: null,
			success: function() {},
			error: function() {}
		};

		const plugin = this;
		const el = $(this);

		let wordsPerSecond;
		let lessThanAMinute;
		let minShortForm;

		let totalWords;
		let totalReadingTimeSeconds;

		let readingTimeMinutes;
		let readingTimeSeconds;
		let readingTime;
		let readingTimeObj;

		// merge defaults and options
		plugin.settings = $.extend({}, defaults, options);

		// define vars
		const s = plugin.settings;

		const setTime = function(o) {

			if(o.text !== '') {

				//split text by spaces to define total words
				totalWords = o.text.trim().split(/\s+/g).length;

				//define words per second based on words per minute (s.wordsPerMinute)
				wordsPerSecond = s.wordsPerMinute / 60;

				//define total reading time in seconds
				totalReadingTimeSeconds = totalWords / wordsPerSecond;

				// define reading time
				readingTimeMinutes = Math.floor(totalReadingTimeSeconds / 60);

				// define remaining reading time seconds
				readingTimeSeconds = Math.round(totalReadingTimeSeconds - (readingTimeMinutes * 60));

				// format reading time
				readingTime = `${readingTimeMinutes}:${readingTimeSeconds}`;

				// if s.round
				if(s.round) {

					// if minutes are greater than 0
					if(readingTimeMinutes > 0) {

						// set reading time by the minute
						$(s.readingTimeTarget).text(s.prependTimeString + readingTimeMinutes + ((!s.readingTimeAsNumber) ? ' ' + minShortForm : ''));

					} else {

						// set reading time as less than a minute
						$(s.readingTimeTarget).text((!s.readingTimeAsNumber) ? s.prependTimeString + lessThanAMinute : readingTimeMinutes);
					}

				} else {

					// set reading time in minutes and seconds
					$(s.readingTimeTarget).text(s.prependTimeString + readingTime);
				}

				// if word count container isn't blank or undefined
				if(s.wordCountTarget !== '' && s.wordCountTarget !== undefined) {

					// set word count
					$(s.wordCountTarget).text(s.prependWordString + totalWords);
				}

				readingTimeObj = {
					wpm: s.wordsPerMinute,
					words: totalWords,
					eta: {
						time: readingTime,
						minutes: readingTimeMinutes,
						seconds: totalReadingTimeSeconds
					}
				};

				// run success callback
				s.success.call(this, readingTimeObj);

			} else {

				// run error callback
				s.error.call(this, {
					error: 'The element does not contain any text'
				});
			}
		};

		// if no element was bound
		if(!this.length) {

			// run error callback
			s.error.call(this, {
				error: 'The element could not be found'
			});

			// return so chained events can continue
			return this;
		}

		// if s.lang is Arabic
		if (s.lang == 'ar') {
			lessThanAMinute = s.lessThanAMinuteString || "أقل من دقيقة";
			minShortForm = 'دقيقة';

		// if s.lang is Czech
		} else if(s.lang == 'cz') {
			lessThanAMinute = s.lessThanAMinuteString || "Méně než minutu";
			minShortForm = 'min';

		// if s.lang is Danish
		} else if(s.lang == 'da') {
			lessThanAMinute = s.lessThanAMinuteString || "Mindre end et minut";
			minShortForm = 'min';

		// if s.lang is German
		} else if(s.lang == 'de') {
			lessThanAMinute = s.lessThanAMinuteString || "Weniger als eine Minute";
			minShortForm = 'min';

		// if s.lang is Spanish
		} else if(s.lang == 'es') {
			lessThanAMinute = s.lessThanAMinuteString || "Menos de un minuto";
			minShortForm = 'min';

		// if s.lang is French
		} else if(s.lang == 'fr') {
			lessThanAMinute = s.lessThanAMinuteString || "Moins d'une minute";
			minShortForm = 'min';

		// if s.lang is Hungarian
		} else if(s.lang == 'hu') {
			lessThanAMinute = s.lessThanAMinuteString || "Kevesebb mint egy perc";
			minShortForm = 'perc';

		// if s.lang is Icelandic
		} else if(s.lang == 'is') {
			lessThanAMinute = s.lessThanAMinuteString || "Minna en eina mínútu";
			minShortForm = 'min';

		// if s.lang is Italian
		} else if(s.lang == 'it') {
			lessThanAMinute = s.lessThanAMinuteString || "Meno di un minuto";
			minShortForm = 'min';

		// if s.lang is Dutch
		} else if(s.lang == 'nl') {
			lessThanAMinute = s.lessThanAMinuteString || "Minder dan een minuut";
			minShortForm = 'min';

		// if s.lang is Norwegian
		} else if(s.lang == 'no') {
			lessThanAMinute = s.lessThanAMinuteString || "Mindre enn ett minutt";
			minShortForm = 'min';

		// if s.lang is Polish
		} else if(s.lang == 'pl') {
			lessThanAMinute = s.lessThanAMinuteString || "Mniej niż minutę";
			minShortForm = 'min';

		// if s.lang is Russian
		} else if(s.lang == 'ru') {
			lessThanAMinute = s.lessThanAMinuteString || "Меньше минуты";
			minShortForm = 'мин';

		// if s.lang is Slovak
		} else if(s.lang == 'sk') {
			lessThanAMinute = s.lessThanAMinuteString || "Menej než minútu";
			minShortForm = 'min';

		// if s.lang is Swedish
		} else if(s.lang == 'sv') {
			lessThanAMinute = s.lessThanAMinuteString || "Mindre än en minut";
			minShortForm = 'min';

		// if s.lang is Turkish
		} else if(s.lang == 'tr') {
			lessThanAMinute = s.lessThanAMinuteString || "Bir dakikadan az";
			minShortForm = 'dk';

		// default s.lang is english
		} else {
			lessThanAMinute = s.lessThanAMinuteString || 'Less than a minute';
			minShortForm = 'min';
		}

		// for each element
		el.each(function(index) {

			// if s.remotePath and s.remoteTarget aren't null
			if(s.remotePath != null && s.remoteTarget != null) {

				// get contents of remote file
				$.get(s.remotePath, function(data) {

					// set time using the remote target found in the remote file
					setTime({
						text: $('<div>').html(data).find(s.remoteTarget).text()
					});
				});

			} else {

				// set time using the targeted element
				setTime({
					text: el.text()
				});
			}
		});

		return true;
	}
})(jQuery);
<commit_msg>Add Ukrainian localization<commit_after>/*!
Name: Reading Time
Dependencies: jQuery
Author: Michael Lynch
Author URL: http://michaelynch.com
Date Created: August 14, 2013
Date Updated: February 28, 2018
Licensed under the MIT license
*/

;(function($) {

	$.fn.readingTime = function(options) {

		// define default parameters
		const defaults = {
			readingTimeTarget: '.eta',
			readingTimeAsNumber: false,
			wordCountTarget: null,
			wordsPerMinute: 270,
			round: true,
			lang: 'en',
			lessThanAMinuteString: '',
			prependTimeString: '',
			prependWordString: '',
			remotePath: null,
			remoteTarget: null,
			success: function() {},
			error: function() {}
		};

		const plugin = this;
		const el = $(this);

		let wordsPerSecond;
		let lessThanAMinute;
		let minShortForm;

		let totalWords;
		let totalReadingTimeSeconds;

		let readingTimeMinutes;
		let readingTimeSeconds;
		let readingTime;
		let readingTimeObj;

		// merge defaults and options
		plugin.settings = $.extend({}, defaults, options);

		// define vars
		const s = plugin.settings;

		const setTime = function(o) {

			if(o.text !== '') {

				//split text by spaces to define total words
				totalWords = o.text.trim().split(/\s+/g).length;

				//define words per second based on words per minute (s.wordsPerMinute)
				wordsPerSecond = s.wordsPerMinute / 60;

				//define total reading time in seconds
				totalReadingTimeSeconds = totalWords / wordsPerSecond;

				// define reading time
				readingTimeMinutes = Math.floor(totalReadingTimeSeconds / 60);

				// define remaining reading time seconds
				readingTimeSeconds = Math.round(totalReadingTimeSeconds - (readingTimeMinutes * 60));

				// format reading time
				readingTime = `${readingTimeMinutes}:${readingTimeSeconds}`;

				// if s.round
				if(s.round) {

					// if minutes are greater than 0
					if(readingTimeMinutes > 0) {

						// set reading time by the minute
						$(s.readingTimeTarget).text(s.prependTimeString + readingTimeMinutes + ((!s.readingTimeAsNumber) ? ' ' + minShortForm : ''));

					} else {

						// set reading time as less than a minute
						$(s.readingTimeTarget).text((!s.readingTimeAsNumber) ? s.prependTimeString + lessThanAMinute : readingTimeMinutes);
					}

				} else {

					// set reading time in minutes and seconds
					$(s.readingTimeTarget).text(s.prependTimeString + readingTime);
				}

				// if word count container isn't blank or undefined
				if(s.wordCountTarget !== '' && s.wordCountTarget !== undefined) {

					// set word count
					$(s.wordCountTarget).text(s.prependWordString + totalWords);
				}

				readingTimeObj = {
					wpm: s.wordsPerMinute,
					words: totalWords,
					eta: {
						time: readingTime,
						minutes: readingTimeMinutes,
						seconds: totalReadingTimeSeconds
					}
				};

				// run success callback
				s.success.call(this, readingTimeObj);

			} else {

				// run error callback
				s.error.call(this, {
					error: 'The element does not contain any text'
				});
			}
		};

		// if no element was bound
		if(!this.length) {

			// run error callback
			s.error.call(this, {
				error: 'The element could not be found'
			});

			// return so chained events can continue
			return this;
		}

		// if s.lang is Arabic
		if (s.lang == 'ar') {
			lessThanAMinute = s.lessThanAMinuteString || "أقل من دقيقة";
			minShortForm = 'دقيقة';

		// if s.lang is Czech
		} else if(s.lang == 'cz') {
			lessThanAMinute = s.lessThanAMinuteString || "Méně než minutu";
			minShortForm = 'min';

		// if s.lang is Danish
		} else if(s.lang == 'da') {
			lessThanAMinute = s.lessThanAMinuteString || "Mindre end et minut";
			minShortForm = 'min';

		// if s.lang is German
		} else if(s.lang == 'de') {
			lessThanAMinute = s.lessThanAMinuteString || "Weniger als eine Minute";
			minShortForm = 'min';

		// if s.lang is Spanish
		} else if(s.lang == 'es') {
			lessThanAMinute = s.lessThanAMinuteString || "Menos de un minuto";
			minShortForm = 'min';

		// if s.lang is French
		} else if(s.lang == 'fr') {
			lessThanAMinute = s.lessThanAMinuteString || "Moins d'une minute";
			minShortForm = 'min';

		// if s.lang is Hungarian
		} else if(s.lang == 'hu') {
			lessThanAMinute = s.lessThanAMinuteString || "Kevesebb mint egy perc";
			minShortForm = 'perc';

		// if s.lang is Icelandic
		} else if(s.lang == 'is') {
			lessThanAMinute = s.lessThanAMinuteString || "Minna en eina mínútu";
			minShortForm = 'min';

		// if s.lang is Italian
		} else if(s.lang == 'it') {
			lessThanAMinute = s.lessThanAMinuteString || "Meno di un minuto";
			minShortForm = 'min';

		// if s.lang is Dutch
		} else if(s.lang == 'nl') {
			lessThanAMinute = s.lessThanAMinuteString || "Minder dan een minuut";
			minShortForm = 'min';

		// if s.lang is Norwegian
		} else if(s.lang == 'no') {
			lessThanAMinute = s.lessThanAMinuteString || "Mindre enn ett minutt";
			minShortForm = 'min';

		// if s.lang is Polish
		} else if(s.lang == 'pl') {
			lessThanAMinute = s.lessThanAMinuteString || "Mniej niż minutę";
			minShortForm = 'min';

		// if s.lang is Russian
		} else if(s.lang == 'ru') {
			lessThanAMinute = s.lessThanAMinuteString || "Меньше минуты";
			minShortForm = 'мин';

		// if s.lang is Slovak
		} else if(s.lang == 'sk') {
			lessThanAMinute = s.lessThanAMinuteString || "Menej než minútu";
			minShortForm = 'min';

		// if s.lang is Swedish
		} else if(s.lang == 'sv') {
			lessThanAMinute = s.lessThanAMinuteString || "Mindre än en minut";
			minShortForm = 'min';

		// if s.lang is Turkish
		} else if(s.lang == 'tr') {
			lessThanAMinute = s.lessThanAMinuteString || "Bir dakikadan az";
			minShortForm = 'dk';

		// if s.lang is Ukrainian
		} else if(s.lang == 'uk') {
			lessThanAMinute = s.lessThanAMinuteString || "Менше хвилини";
			minShortForm = 'хв';

		// default s.lang is english
		} else {
			lessThanAMinute = s.lessThanAMinuteString || 'Less than a minute';
			minShortForm = 'min';
		}

		// for each element
		el.each(function(index) {

			// if s.remotePath and s.remoteTarget aren't null
			if(s.remotePath != null && s.remoteTarget != null) {

				// get contents of remote file
				$.get(s.remotePath, function(data) {

					// set time using the remote target found in the remote file
					setTime({
						text: $('<div>').html(data).find(s.remoteTarget).text()
					});
				});

			} else {

				// set time using the targeted element
				setTime({
					text: el.text()
				});
			}
		});

		return true;
	}
})(jQuery);
<|endoftext|>


<commit_before>
(function(){

  var root = this;

  // Native extension
  if (typeof exports !== 'undefined') {
    var redis = require('../lib/redis');
    var errors = require('../../util/errors');
    var _ = require('underscore');
  } else {
    var redis = root.redis;
    var errors = root.Substance.errors;
    var _ = root._;
  }

  // TODO: discuss about error codes
  errors.define('RedisStoreError', -1);

  var RedisStore = function(settings) {

    // Initialization
    // --------

    // reference to this for use within instance methods
    var self = this;

    this.redis = redis.RedisAccess.Create(0);

    var defaults = {
      host: "127.0.0.1",
      port: 6379,
      scope: "substance"
    };

    var settings = _.extend(defaults, settings);

    this.redis.setHost(settings.host);
    this.redis.setPort(settings.port);

    // the scope is useful to keep parts of the redis db separated
    // e.g. tests would use its own, or one could separate user spaces
    self.redis.setScope(settings.scope);
    self.redis.connect();

    var documents = self.redis.asHash("documents");
    var blobs = self.redis.asHash("blobs");
    var deletedDocuments = self.redis.asHash("deleted-documents");

    // Private Methods
    // ========

    function markAsDeleted(id) {
      deletedDocuments.set(id, id);
      return true;
    }

    this.snapshotKey = function(id) {
      return id + ":snapshots"
    };

    // Public Interface
    // ========

    // Checks if a document exists
    // --------

    this.exists = function (id, cb) {
      var result = documents.contains(id);
      if (cb) cb(null, result);
      return result;
    };

    // Creates a new document with the provided id
    // --------

    this.create = function (id, cb) {
      if(self.exists(id) && cb) {
        return cb(new errors.RedisStoreError("Document already exists."));
      }

      var doc = {
        "id": id,
        "meta": {
          "created_at": new Date(),
          "updated_at": new Date()
        }
      };

      // TODO: what to do if this fails?
      // TODO: create initial list for commits
      documents.set(id, doc);

      if (cb) cb(null, doc);
      return doc;
    };

    // Updates an existing document with the provided metadata object
    // --------
    //
    // meta:  object containing all metadata

    this.updateMeta = function(id, meta, cb) {
      if (!meta) {
        if (cb) cb(null);
        return true;
      }

      if (!self.exists(id) && cb) {
        cb(new errors.RedisStoreError("Document does not exist."));
        return false;
      }

      var doc = {
        "id": id,
        "meta": meta
      };

      documents.set(id, doc);
      if (cb) cb(null, doc);

      return true;
    };

    // Get document info (no contents)
    // --------

    this.getInfo = function(id, cb) {
      var doc = documents.getJSON(id);
      doc.refs = this.getRefs(id);
      if (cb) cb(null, doc);
      return doc;
    };

    /**
     * List all documents complete with metadata
     */

    this.list = function (cb) {
      var docIds = documents.getKeys();
      var docs = [];

      for (var idx = 0; idx < docIds.length; ++idx) {
        var doc = documents.getJSON(docIds[idx]);
        doc.refs = self.getRefs(docIds[idx]);
        docs.push(doc);
      }

      // sort the documents in descending order with respect to the time of the last update
      docs.sort(function(a, b) {
        return new Date(b.meta.updated_at) - new Date(a.meta.updated_at);
      });

      if(cb) cb(null, docs);
      return docs;
    };

    /**
     *  Permanently deletes a document
     *  @param cb callback
     */

    this.delete = function (id, cb) {
      documents.remove(id);
      self.redis.removeWithPrefix(id);
      markAsDeleted(id);
      if (cb) cb(null);
      return true;
    };

    /**
     *  Stores a sequence of commits for a given document id.
     *
     *  @param newCommits an array of commit objects
     *  @param cb callback
     */
    function updateCommits(id, newCommits, cb) {

      // TODO: this does not to be asynchronous anymore as it is used privately only

      // No commits supplied. Go ahead
      if (newCommits.length === 0) {
        cb(null);
        return true;
      }

      var commitsKey = id + ":commits";
      var commits = self.redis.asList(commitsKey);

      // Note: we allow to update the document with commits pointing
      //       to a commit that does not need to be the last one.
      //       E.g. this happens after undoing commits and adding new changes.
      //       Instead of deleting the undone commits we keep them as detached refs
      //       which allows to recover such versions.

      // Find the parent commit
      var lastSha = newCommits[0].parent;
      if (lastSha && !self.redis.exists(commitsKey + ":" + lastSha)) {
        var msg = "Parent commit not found.";
        cb ? cb(new errors.RedisStoreError(msg)) : console.log(msg);
        return false;
      }

      for(var idx=0; idx<newCommits.length; idx++) {

        var commit = newCommits[idx];

        // commit must be in proper order
        if (lastSha && commit.parent != lastSha) {
          var err = new errors.RedisStoreError("Invalid commit chain.");
          cb ? cb(err) : console.log(err);
          return false;
        }

        lastSha = commit.sha;
      }

      // save the commits after knowing that everything is fine
      for (var idx = 0; idx < newCommits.length; idx++) {
        var commit = newCommits[idx];
        if (!_.isObject(commit)) {
          var err = new errors.RedisStoreError("Can not store empty commit.");
          if (cb) cb(err); else console.log(err);
          return false;
        }

        commits.addAsString(commit.sha);
        // store the commit's data into an own field
        self.redis.set(commitsKey + ":" + newCommits[idx].sha, commit);
      }

      // console.log('Stored these commits in the database', newCommits);

      if (cb) cb(null);
      return true;
    };

    // TODO: obsolete now
    function updateRefs(id, refs, cb) {
      self.setRefs(id, refs, cb);
      return true;
    }

    this.update_new = function(id, options, cb) {
      // TODO: remove this legacy dispatcher as soon we are stable again
      var success = true;

      // Note: providing a special error callback to pass through detailed error messages
      // but not messages on success which will be done once for all
      function errCb(err) {
        // don't call on success
        if(err) {
          console.log(err);
          if (cb) cb(err);
        }
      }

      // update the document depending on available data and stop if an error occurs
      if(options.commits) {
        success = updateCommits(id, options.commits, errCb);
      }
      if(success && options.meta) {
        success = self.updateMeta(id, options.meta, errCb);
      }
      if(success && options.refs) {
        success = updateRefs(id, options.refs, errCb);
      }

      if(cb) cb(null, success);
      return success;

    }

    // TODO: remove this legacy dispatcher as soon as we are stable again
    this.update = function(id, newCommits, cb_or_meta, refs, cb) {
      var meta = arguments.length < 4 ? null : cb_or_meta;
      var cb = arguments.length < 4 ? cb_or_meta : cb;
      var options = {
        commits: newCommits,
        meta: meta,
        refs: refs
      };
      return this.update_new(id, options, cb);
    };

    this.setRefs = function(id, branch_or_refs, refs_or_cb, cb) {
      var references = self.redis.asHash(id + ":refs");
      if (arguments.length == 2 || _.isFunction(refs_or_cb)) {
        cb = refs_or_cb;
        var refs = branch_or_refs;
        _.each(refs, function(branchRefs, branch) {
          var tmp = references.getJSON(branch) || {};
          branchRefs = _.extend(tmp, branchRefs);
          references.set(branch, branchRefs);
        });
      } else {
        var branch = branch_or_refs;
        var refs = refs_or_cb;
        var tmp = references.getJSON(branch) || {};
        refs = _.extend(tmp, refs);
        references.set(branch, refs);
      }
      if (cb) cb(null);
      return true;
    };

    this.getRefs = function(id, branch_or_cb, cb) {
      var references = self.redis.asHash(id + ":refs");
      var res;
      // return all refs if not branch is specified
      if (arguments.length == 1 || _.isFunction(branch_or_cb)) {
        cb = branch_or_cb;
        res = {}
        var branches = references.getKeys();
        _.each(branches, function(branch) {
          res[branch] = references.getJSON(branch);
        });
      } else {
        res = references.getJSON(branch_or_cb);
      }
      if(cb) cb(null, res);
      return res;
    };

    this.setSnapshot = function (id, data, title, cb) {
      var snapshots = self.redis.asHash(self.snapshotKey(id));
      snapshots.set(title, data);
      if(cb) cb(null);
    };

    /**
     * Retrieves a range of the document's commits
     *
     * @param id the document's id
     * @param head where to start traversing the commits
     * @param stop the commit that is excluded
     */

    this.commits = function(id, head, stop, cb) {

      function getCommit(sha) {
        return self.redis.getJSON( id + ":commits:" + sha);
      }

      if (head === stop) {
        if (cb) cb(null, []);
        return [];
      }
      var commit = getCommit(head);

      if (!commit) {
        if (cb) cb(null, []);
        return [];
      }

      commit.sha = head;

      var commits = [commit];
      var prev = commit;

      while (commit = getCommit(commit.parent)) {
        if (stop && commit.sha === stop) break;
        commit.sha = prev.parent;
        commits.push(commit);
        prev = commit;
      }

      commits = commits.reverse();
      if (cb) cb(null, commits);
      return commits;
    };

    /**
     * Retrieves a document
     *
     * @param id the document's id
     * @param cb callback
     */
    this.get = function(id, cb) {

      if(!self.exists(id)) {
        var err = new errors.RedisStoreError("Document does not exist.");
        if (cb) cb(err);
        console.log(err);
        return null;
      }

      var doc = documents.getJSON(id);
      doc.commits = {};

      doc.refs = self.getRefs(id);

      var lastSha = (doc.refs.master) ? doc.refs.master.last : null;

      if (lastSha) {
        var commits = self.commits(id, lastSha);

        _.each(commits, function(c) {
          doc.commits[c.sha] = c;
        });
      }

      if (lastSha && !doc.commits[lastSha]) {
        var err = new errors.RedisStoreError('Corrupted Document: contains empty commits');
        console.log(err, doc);
        if (cb) cb(err);
        return null;
      }

      if(cb) cb(null, doc);
      return doc;
    };

    this.clear = function(cb) {
      self.redis.removeWithPrefix("");
      if (cb) cb(null);
    };

    this.deletedDocuments = function(cb) {
      var res = deletedDocuments.getKeys();
      if (cb) cb(null, res);
      return res;
    };

    this.confirmDeletion = function(id, cb) {
      var success = deletedDocuments.remove(id);
      if (!success) {
        var err = new errors.RedisStoreError("Could not confirm deletion.");
        if (cb) cb(err);
        else console.log(err);
      }
      return success;
    };

    // TODO: consider branches
    this.import = function(data, cb) {
      // var success = true;
      var success = _.every(data['documents'], function(doc, id) {
        if (self.exists(id)) {
          self.delete(id);
          self.confirmDeletion(id);
        }

        if (self.create(id)) {
          var commits = [];

          if (doc.refs.master.last) {
            var c = doc.commits[doc.refs.master.last];
            commits.push(c);

            while (c = doc.commits[c.parent]) {
              commits.push(c);
            }
          }

          // console.log('LE COMMITS MISSIEU', commits);
          var options = {
            commits: commits.reverse(),
            meta: doc.meta,
            refs: doc.refs
          };

          if (!self.update_new(id, options, cb)) {
            var err = new errors.RedisStoreError("Update failed.");
            if (cb) cb(err); else console.log(err);
            return false; // success = false;
          }
        } else {
          var err = new errors.RedisStoreError("Import failed.");
          if (cb) cb(err); else console.log(err);
          return false; // success = false;
        }
        return true;
      });

      return success;
    };

    this.seed = function(data, cb) {
      this.clear();
      this.import(data, cb);
      return true;
    };

    this.dump = function(cb) {
      // TODO: how to dump settings?
      var settings = {};
      var deletedDocs = [];

      var docs = {};
      var docInfos = self.list();

      _.each(docInfos, function(info, idx, docInfos) {
        docs[info.id] = self.get(info.id);
      });

      _.each(deletedDocuments.getKeys(), function(id) {
        deletedDocs.push(id);
      });

      var dump = {
        'documents': docs,
        'deleted-documents': deletedDocs
      };

      if (cb) cb(null, dump);
      return dump;
    };

    // Blob Interface
    // ========

    // Create a new blob for given data
    // --------

    this.createBlob = function(id, base64data, cb) {
      if (!this.blobExists(id)) {
        var blob = {
          data: base64data,
          id: id
        };

        blobs.set(id, blob);
        if (cb) cb(null, blob);
        return blob;
      } else {
        if (cb) cb(new errors.RedisStoreError("Blob already exists."));
        return false;
      }
    };

    // Get Blob by id
    // --------

    this.getBlob = function(id, cb) {
      var blob = blobs.getJSON(id);

      if (blob) {
        if (cb) cb(null, blob.data);
        return blob.data;
      } else {
        if (cb) cb(new errors.RedisStoreError("Blob not found."));
        return null;
      }
    };

    // Checks if blob exists
    // --------

    this.blobExists = function (id, cb) {
      var result = blobs.contains(id);
      if (cb) cb(null, result);
      return result;
    };

    // Delete blob by given id
    // --------

    this.deleteBlob = function(id, cb) {
      blobs.remove(id);
      self.redis.removeWithPrefix(id);
      if (cb) cb(null);
      return true;
    };

    // Returns a list of blob ids
    // --------

    this.listBlobs = function(cb) {
      var docIds = blobs.getKeys();
      if (cb) cb(null, docIds);
      return docIds;
    };

  };

  RedisStore.prototype.name = "RedisStore";

  // Exports
  if (typeof exports !== 'undefined') {
    exports.RedisStore = RedisStore;
  } else {
    root.RedisStore = RedisStore;
    root.Substance.RedisStore = RedisStore;
  }

})(this);
<commit_msg>Make confirmDeletion more silent.<commit_after>
(function(){

  var root = this;

  // Native extension
  if (typeof exports !== 'undefined') {
    var redis = require('../lib/redis');
    var errors = require('../../util/errors');
    var _ = require('underscore');
  } else {
    var redis = root.redis;
    var errors = root.Substance.errors;
    var _ = root._;
  }

  // TODO: discuss about error codes
  errors.define('RedisStoreError', -1);

  var RedisStore = function(settings) {

    // Initialization
    // --------

    // reference to this for use within instance methods
    var self = this;

    this.redis = redis.RedisAccess.Create(0);

    var defaults = {
      host: "127.0.0.1",
      port: 6379,
      scope: "substance"
    };

    var settings = _.extend(defaults, settings);

    this.redis.setHost(settings.host);
    this.redis.setPort(settings.port);

    // the scope is useful to keep parts of the redis db separated
    // e.g. tests would use its own, or one could separate user spaces
    self.redis.setScope(settings.scope);
    self.redis.connect();

    var documents = self.redis.asHash("documents");
    var blobs = self.redis.asHash("blobs");
    var deletedDocuments = self.redis.asHash("deleted-documents");

    // Private Methods
    // ========

    function markAsDeleted(id) {
      deletedDocuments.set(id, id);
      return true;
    }

    this.snapshotKey = function(id) {
      return id + ":snapshots"
    };

    // Public Interface
    // ========

    // Checks if a document exists
    // --------

    this.exists = function (id, cb) {
      var result = documents.contains(id);
      if (cb) cb(null, result);
      return result;
    };

    // Creates a new document with the provided id
    // --------

    this.create = function (id, cb) {
      if(self.exists(id) && cb) {
        return cb(new errors.RedisStoreError("Document already exists."));
      }

      var doc = {
        "id": id,
        "meta": {
          "created_at": new Date(),
          "updated_at": new Date()
        }
      };

      // TODO: what to do if this fails?
      // TODO: create initial list for commits
      documents.set(id, doc);

      if (cb) cb(null, doc);
      return doc;
    };

    // Updates an existing document with the provided metadata object
    // --------
    //
    // meta:  object containing all metadata

    this.updateMeta = function(id, meta, cb) {
      if (!meta) {
        if (cb) cb(null);
        return true;
      }

      if (!self.exists(id) && cb) {
        cb(new errors.RedisStoreError("Document does not exist."));
        return false;
      }

      var doc = {
        "id": id,
        "meta": meta
      };

      documents.set(id, doc);
      if (cb) cb(null, doc);

      return true;
    };

    // Get document info (no contents)
    // --------

    this.getInfo = function(id, cb) {
      var doc = documents.getJSON(id);
      doc.refs = this.getRefs(id);
      if (cb) cb(null, doc);
      return doc;
    };

    /**
     * List all documents complete with metadata
     */

    this.list = function (cb) {
      var docIds = documents.getKeys();
      var docs = [];

      for (var idx = 0; idx < docIds.length; ++idx) {
        var doc = documents.getJSON(docIds[idx]);
        doc.refs = self.getRefs(docIds[idx]);
        docs.push(doc);
      }

      // sort the documents in descending order with respect to the time of the last update
      docs.sort(function(a, b) {
        return new Date(b.meta.updated_at) - new Date(a.meta.updated_at);
      });

      if(cb) cb(null, docs);
      return docs;
    };

    /**
     *  Permanently deletes a document
     *  @param cb callback
     */

    this.delete = function (id, cb) {
      documents.remove(id);
      self.redis.removeWithPrefix(id);
      markAsDeleted(id);
      if (cb) cb(null);
      return true;
    };

    /**
     *  Stores a sequence of commits for a given document id.
     *
     *  @param newCommits an array of commit objects
     *  @param cb callback
     */
    function updateCommits(id, newCommits, cb) {

      // TODO: this does not to be asynchronous anymore as it is used privately only

      // No commits supplied. Go ahead
      if (newCommits.length === 0) {
        cb(null);
        return true;
      }

      var commitsKey = id + ":commits";
      var commits = self.redis.asList(commitsKey);

      // Note: we allow to update the document with commits pointing
      //       to a commit that does not need to be the last one.
      //       E.g. this happens after undoing commits and adding new changes.
      //       Instead of deleting the undone commits we keep them as detached refs
      //       which allows to recover such versions.

      // Find the parent commit
      var lastSha = newCommits[0].parent;
      if (lastSha && !self.redis.exists(commitsKey + ":" + lastSha)) {
        var msg = "Parent commit not found.";
        cb ? cb(new errors.RedisStoreError(msg)) : console.log(msg);
        return false;
      }

      for(var idx=0; idx<newCommits.length; idx++) {

        var commit = newCommits[idx];

        // commit must be in proper order
        if (lastSha && commit.parent != lastSha) {
          var err = new errors.RedisStoreError("Invalid commit chain.");
          cb ? cb(err) : console.log(err);
          return false;
        }

        lastSha = commit.sha;
      }

      // save the commits after knowing that everything is fine
      for (var idx = 0; idx < newCommits.length; idx++) {
        var commit = newCommits[idx];
        if (!_.isObject(commit)) {
          var err = new errors.RedisStoreError("Can not store empty commit.");
          if (cb) cb(err); else console.log(err);
          return false;
        }

        commits.addAsString(commit.sha);
        // store the commit's data into an own field
        self.redis.set(commitsKey + ":" + newCommits[idx].sha, commit);
      }

      // console.log('Stored these commits in the database', newCommits);

      if (cb) cb(null);
      return true;
    };

    // TODO: obsolete now
    function updateRefs(id, refs, cb) {
      self.setRefs(id, refs, cb);
      return true;
    }

    this.update_new = function(id, options, cb) {
      // TODO: remove this legacy dispatcher as soon we are stable again
      var success = true;

      // Note: providing a special error callback to pass through detailed error messages
      // but not messages on success which will be done once for all
      function errCb(err) {
        // don't call on success
        if(err) {
          console.log(err);
          if (cb) cb(err);
        }
      }

      // update the document depending on available data and stop if an error occurs
      if(options.commits) {
        success = updateCommits(id, options.commits, errCb);
      }
      if(success && options.meta) {
        success = self.updateMeta(id, options.meta, errCb);
      }
      if(success && options.refs) {
        success = updateRefs(id, options.refs, errCb);
      }

      if(cb) cb(null, success);
      return success;

    }

    // TODO: remove this legacy dispatcher as soon as we are stable again
    this.update = function(id, newCommits, cb_or_meta, refs, cb) {
      var meta = arguments.length < 4 ? null : cb_or_meta;
      var cb = arguments.length < 4 ? cb_or_meta : cb;
      var options = {
        commits: newCommits,
        meta: meta,
        refs: refs
      };
      return this.update_new(id, options, cb);
    };

    this.setRefs = function(id, branch_or_refs, refs_or_cb, cb) {
      var references = self.redis.asHash(id + ":refs");
      if (arguments.length == 2 || _.isFunction(refs_or_cb)) {
        cb = refs_or_cb;
        var refs = branch_or_refs;
        _.each(refs, function(branchRefs, branch) {
          var tmp = references.getJSON(branch) || {};
          branchRefs = _.extend(tmp, branchRefs);
          references.set(branch, branchRefs);
        });
      } else {
        var branch = branch_or_refs;
        var refs = refs_or_cb;
        var tmp = references.getJSON(branch) || {};
        refs = _.extend(tmp, refs);
        references.set(branch, refs);
      }
      if (cb) cb(null);
      return true;
    };

    this.getRefs = function(id, branch_or_cb, cb) {
      var references = self.redis.asHash(id + ":refs");
      var res;
      // return all refs if not branch is specified
      if (arguments.length == 1 || _.isFunction(branch_or_cb)) {
        cb = branch_or_cb;
        res = {}
        var branches = references.getKeys();
        _.each(branches, function(branch) {
          res[branch] = references.getJSON(branch);
        });
      } else {
        res = references.getJSON(branch_or_cb);
      }
      if(cb) cb(null, res);
      return res;
    };

    this.setSnapshot = function (id, data, title, cb) {
      var snapshots = self.redis.asHash(self.snapshotKey(id));
      snapshots.set(title, data);
      if(cb) cb(null);
    };

    /**
     * Retrieves a range of the document's commits
     *
     * @param id the document's id
     * @param head where to start traversing the commits
     * @param stop the commit that is excluded
     */

    this.commits = function(id, head, stop, cb) {

      function getCommit(sha) {
        return self.redis.getJSON( id + ":commits:" + sha);
      }

      if (head === stop) {
        if (cb) cb(null, []);
        return [];
      }
      var commit = getCommit(head);

      if (!commit) {
        if (cb) cb(null, []);
        return [];
      }

      commit.sha = head;

      var commits = [commit];
      var prev = commit;

      while (commit = getCommit(commit.parent)) {
        if (stop && commit.sha === stop) break;
        commit.sha = prev.parent;
        commits.push(commit);
        prev = commit;
      }

      commits = commits.reverse();
      if (cb) cb(null, commits);
      return commits;
    };

    /**
     * Retrieves a document
     *
     * @param id the document's id
     * @param cb callback
     */
    this.get = function(id, cb) {

      if(!self.exists(id)) {
        var err = new errors.RedisStoreError("Document does not exist.");
        if (cb) cb(err);
        console.log(err);
        return null;
      }

      var doc = documents.getJSON(id);
      doc.commits = {};

      doc.refs = self.getRefs(id);

      var lastSha = (doc.refs.master) ? doc.refs.master.last : null;

      if (lastSha) {
        var commits = self.commits(id, lastSha);

        _.each(commits, function(c) {
          doc.commits[c.sha] = c;
        });
      }

      if (lastSha && !doc.commits[lastSha]) {
        var err = new errors.RedisStoreError('Corrupted Document: contains empty commits');
        console.log(err, doc);
        if (cb) cb(err);
        return null;
      }

      if(cb) cb(null, doc);
      return doc;
    };

    this.clear = function(cb) {
      self.redis.removeWithPrefix("");
      if (cb) cb(null);
    };

    this.deletedDocuments = function(cb) {
      var res = deletedDocuments.getKeys();
      if (cb) cb(null, res);
      return res;
    };

    // TODO: Add error handling?
    this.confirmDeletion = function(id, cb) {
      deletedDocuments.remove(id);

      cb(null);
      return true;
    };

    // TODO: consider branches
    this.import = function(data, cb) {
      // var success = true;
      var success = _.every(data['documents'], function(doc, id) {
        if (self.exists(id)) {
          self.delete(id);
          self.confirmDeletion(id);
        }

        if (self.create(id)) {
          var commits = [];

          if (doc.refs.master.last) {
            var c = doc.commits[doc.refs.master.last];
            commits.push(c);

            while (c = doc.commits[c.parent]) {
              commits.push(c);
            }
          }

          // console.log('LE COMMITS MISSIEU', commits);
          var options = {
            commits: commits.reverse(),
            meta: doc.meta,
            refs: doc.refs
          };

          if (!self.update_new(id, options, cb)) {
            var err = new errors.RedisStoreError("Update failed.");
            if (cb) cb(err); else console.log(err);
            return false; // success = false;
          }
        } else {
          var err = new errors.RedisStoreError("Import failed.");
          if (cb) cb(err); else console.log(err);
          return false; // success = false;
        }
        return true;
      });

      return success;
    };

    this.seed = function(data, cb) {
      this.clear();
      this.import(data, cb);
      return true;
    };

    this.dump = function(cb) {
      // TODO: how to dump settings?
      var settings = {};
      var deletedDocs = [];

      var docs = {};
      var docInfos = self.list();

      _.each(docInfos, function(info, idx, docInfos) {
        docs[info.id] = self.get(info.id);
      });

      _.each(deletedDocuments.getKeys(), function(id) {
        deletedDocs.push(id);
      });

      var dump = {
        'documents': docs,
        'deleted-documents': deletedDocs
      };

      if (cb) cb(null, dump);
      return dump;
    };

    // Blob Interface
    // ========

    // Create a new blob for given data
    // --------

    this.createBlob = function(id, base64data, cb) {
      if (!this.blobExists(id)) {
        var blob = {
          data: base64data,
          id: id
        };

        blobs.set(id, blob);
        if (cb) cb(null, blob);
        return blob;
      } else {
        if (cb) cb(new errors.RedisStoreError("Blob already exists."));
        return false;
      }
    };

    // Get Blob by id
    // --------

    this.getBlob = function(id, cb) {
      var blob = blobs.getJSON(id);

      if (blob) {
        if (cb) cb(null, blob.data);
        return blob.data;
      } else {
        if (cb) cb(new errors.RedisStoreError("Blob not found."));
        return null;
      }
    };

    // Checks if blob exists
    // --------

    this.blobExists = function (id, cb) {
      var result = blobs.contains(id);
      if (cb) cb(null, result);
      return result;
    };

    // Delete blob by given id
    // --------

    this.deleteBlob = function(id, cb) {
      blobs.remove(id);
      self.redis.removeWithPrefix(id);
      if (cb) cb(null);
      return true;
    };

    // Returns a list of blob ids
    // --------

    this.listBlobs = function(cb) {
      var docIds = blobs.getKeys();
      if (cb) cb(null, docIds);
      return docIds;
    };

  };

  RedisStore.prototype.name = "RedisStore";

  // Exports
  if (typeof exports !== 'undefined') {
    exports.RedisStore = RedisStore;
  } else {
    root.RedisStore = RedisStore;
    root.Substance.RedisStore = RedisStore;
  }

})(this);
<|endoftext|>


<commit_before>// @flow
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/**
 * UI reducer
 * @module reducers/ui
 */

import makeRecord from "../utils/makeRecord";
import { prefs } from "../utils/prefs";

import type { Action, panelPositionType } from "../actions/types";
import type { Record } from "../utils/makeRecord";

export type SymbolSearchType = "functions" | "variables";
export type ActiveSearchType =
  | "project"
  | "source"
  | "file"
  | "symbol"
  | "line";

export type OrientationType = "horizontal" | "vertical";

export type SelectedPrimaryPaneTabType = "sources" | "outline";

export type UIState = {
  selectedPrimaryPaneTab: selectedPrimaryPaneTabType,
  activeSearch: ?ActiveSearchType,
  contextMenu: any,
  symbolSearchType: SymbolSearchType,
  shownSource: string,
  startPanelCollapsed: boolean,
  endPanelCollapsed: boolean,
  frameworkGroupingOn: boolean,
  projectDirectoryRoot: string,
  orientation: OrientationType,
  highlightedLineRange?: {
    start?: number,
    end?: number,
    sourceId?: number
  },
  conditionalPanelLine: null | number
};

export const State = makeRecord(
  ({
    selectedPrimaryPaneTab: "sources",
    activeSearch: null,
    contextMenu: {},
    symbolSearchType: "functions",
    shownSource: "",
    projectDirectoryRoot: "",
    startPanelCollapsed: prefs.startPanelCollapsed,
    endPanelCollapsed: prefs.endPanelCollapsed,
    frameworkGroupingOn: prefs.frameworkGroupingOn,
    highlightedLineRange: undefined,
    conditionalPanelLine: null,
    orientation: "horizontal"
  }: UIState)
);

function update(
  state: Record<UIState> = State(),
  action: Action
): Record<UIState> {
  switch (action.type) {
    case "TOGGLE_ACTIVE_SEARCH": {
      return state.set("activeSearch", action.value);
    }

    case "TOGGLE_FRAMEWORK_GROUPING": {
      prefs.frameworkGroupingOn = action.value;
      return state.set("frameworkGroupingOn", action.value);
    }

    case "SET_SYMBOL_SEARCH_TYPE": {
      return state.set("symbolSearchType", action.symbolType);
    }

    case "SET_CONTEXT_MENU": {
      return state.set("contextMenu", action.contextMenu);
    }

    case "SET_ORIENTATION": {
      return state.set("orientation", action.orientation);
    }

    case "SHOW_SOURCE": {
      return state.set("shownSource", action.sourceUrl);
    }

    case "TOGGLE_PANE": {
      if (action.position == "start") {
        prefs.startPanelCollapsed = action.paneCollapsed;
        return state.set("startPanelCollapsed", action.paneCollapsed);
      }

      prefs.endPanelCollapsed = action.paneCollapsed;
      return state.set("endPanelCollapsed", action.paneCollapsed);
    }

    case "HIGHLIGHT_LINES":
      const { start, end, sourceId } = action.location;
      let lineRange = {};

      if (start && end && sourceId) {
        lineRange = { start, end, sourceId };
      }

      return state.set("highlightedLineRange", lineRange);

    case "CLEAR_HIGHLIGHT_LINES":
      return state.set("highlightedLineRange", {});

    case "OPEN_CONDITIONAL_PANEL":
      return state.set("conditionalPanelLine", action.line);

    case "CLOSE_CONDITIONAL_PANEL":
      return state.set("conditionalPanelLine", null);

    case "SET_PROJECT_DIRECTORY_ROOT":
      prefs.projectDirectoryRoot = action.url;
      return state.set("projectDirectoryRoot", action.url);

    case "SET_PRIMARY_PANE_TAB":
      return state.set("selectedPrimaryPaneTab", action.tabName);

    default: {
      return state;
    }
  }
}

// NOTE: we'd like to have the app state fully typed
// https://github.com/devtools-html/debugger.html/blob/master/src/reducers/sources.js#L179-L185
type OuterState = { ui: Record<UIState> };

export function getSelectedPrimaryPaneTab(
  state: OuterState
): selectedPrimaryPaneTabType {
  return state.ui.get("selectedPrimaryPaneTab");
}

export function getActiveSearch(state: OuterState): ActiveSearchType {
  return state.ui.get("activeSearch");
}

export function getContextMenu(state: OuterState): any {
  return state.ui.get("contextMenu");
}

export function getFrameworkGroupingState(state: OuterState): boolean {
  return state.ui.get("frameworkGroupingOn");
}

export function getSymbolSearchType(state: OuterState): SymbolSearchType {
  return state.ui.get("symbolSearchType");
}

export function getShownSource(state: OuterState): boolean {
  return state.ui.get("shownSource");
}

export function getPaneCollapse(
  state: OuterState,
  position: panelPositionType
): boolean {
  if (position == "start") {
    return state.ui.get("startPanelCollapsed");
  }

  return state.ui.get("endPanelCollapsed");
}

export function getHighlightedLineRange(state: OuterState) {
  return state.ui.get("highlightedLineRange");
}

export function getConditionalPanelLine(state: OuterState): null | number {
  return state.ui.get("conditionalPanelLine");
}

export function getProjectDirectoryRoot(state: OuterState): boolean {
  return state.ui.get("projectDirectoryRoot");
}

export function getOrientation(state: OuterState): boolean {
  return state.ui.get("orientation");
}

export default update;
<commit_msg>Fix typo<commit_after>// @flow
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/**
 * UI reducer
 * @module reducers/ui
 */

import makeRecord from "../utils/makeRecord";
import { prefs } from "../utils/prefs";

import type { Action, panelPositionType } from "../actions/types";
import type { Record } from "../utils/makeRecord";

export type SymbolSearchType = "functions" | "variables";
export type ActiveSearchType =
  | "project"
  | "source"
  | "file"
  | "symbol"
  | "line";

export type OrientationType = "horizontal" | "vertical";

export type SelectedPrimaryPaneTabType = "sources" | "outline";

export type UIState = {
  selectedPrimaryPaneTab: SelectedPrimaryPaneTabType,
  activeSearch: ?ActiveSearchType,
  contextMenu: any,
  symbolSearchType: SymbolSearchType,
  shownSource: string,
  startPanelCollapsed: boolean,
  endPanelCollapsed: boolean,
  frameworkGroupingOn: boolean,
  projectDirectoryRoot: string,
  orientation: OrientationType,
  highlightedLineRange?: {
    start?: number,
    end?: number,
    sourceId?: number
  },
  conditionalPanelLine: null | number
};

export const State = makeRecord(
  ({
    selectedPrimaryPaneTab: "sources",
    activeSearch: null,
    contextMenu: {},
    symbolSearchType: "functions",
    shownSource: "",
    projectDirectoryRoot: "",
    startPanelCollapsed: prefs.startPanelCollapsed,
    endPanelCollapsed: prefs.endPanelCollapsed,
    frameworkGroupingOn: prefs.frameworkGroupingOn,
    highlightedLineRange: undefined,
    conditionalPanelLine: null,
    orientation: "horizontal"
  }: UIState)
);

function update(
  state: Record<UIState> = State(),
  action: Action
): Record<UIState> {
  switch (action.type) {
    case "TOGGLE_ACTIVE_SEARCH": {
      return state.set("activeSearch", action.value);
    }

    case "TOGGLE_FRAMEWORK_GROUPING": {
      prefs.frameworkGroupingOn = action.value;
      return state.set("frameworkGroupingOn", action.value);
    }

    case "SET_SYMBOL_SEARCH_TYPE": {
      return state.set("symbolSearchType", action.symbolType);
    }

    case "SET_CONTEXT_MENU": {
      return state.set("contextMenu", action.contextMenu);
    }

    case "SET_ORIENTATION": {
      return state.set("orientation", action.orientation);
    }

    case "SHOW_SOURCE": {
      return state.set("shownSource", action.sourceUrl);
    }

    case "TOGGLE_PANE": {
      if (action.position == "start") {
        prefs.startPanelCollapsed = action.paneCollapsed;
        return state.set("startPanelCollapsed", action.paneCollapsed);
      }

      prefs.endPanelCollapsed = action.paneCollapsed;
      return state.set("endPanelCollapsed", action.paneCollapsed);
    }

    case "HIGHLIGHT_LINES":
      const { start, end, sourceId } = action.location;
      let lineRange = {};

      if (start && end && sourceId) {
        lineRange = { start, end, sourceId };
      }

      return state.set("highlightedLineRange", lineRange);

    case "CLEAR_HIGHLIGHT_LINES":
      return state.set("highlightedLineRange", {});

    case "OPEN_CONDITIONAL_PANEL":
      return state.set("conditionalPanelLine", action.line);

    case "CLOSE_CONDITIONAL_PANEL":
      return state.set("conditionalPanelLine", null);

    case "SET_PROJECT_DIRECTORY_ROOT":
      prefs.projectDirectoryRoot = action.url;
      return state.set("projectDirectoryRoot", action.url);

    case "SET_PRIMARY_PANE_TAB":
      return state.set("selectedPrimaryPaneTab", action.tabName);

    default: {
      return state;
    }
  }
}

// NOTE: we'd like to have the app state fully typed
// https://github.com/devtools-html/debugger.html/blob/master/src/reducers/sources.js#L179-L185
type OuterState = { ui: Record<UIState> };

export function getSelectedPrimaryPaneTab(
  state: OuterState
): selectedPrimaryPaneTabType {
  return state.ui.get("selectedPrimaryPaneTab");
}

export function getActiveSearch(state: OuterState): ActiveSearchType {
  return state.ui.get("activeSearch");
}

export function getContextMenu(state: OuterState): any {
  return state.ui.get("contextMenu");
}

export function getFrameworkGroupingState(state: OuterState): boolean {
  return state.ui.get("frameworkGroupingOn");
}

export function getSymbolSearchType(state: OuterState): SymbolSearchType {
  return state.ui.get("symbolSearchType");
}

export function getShownSource(state: OuterState): boolean {
  return state.ui.get("shownSource");
}

export function getPaneCollapse(
  state: OuterState,
  position: panelPositionType
): boolean {
  if (position == "start") {
    return state.ui.get("startPanelCollapsed");
  }

  return state.ui.get("endPanelCollapsed");
}

export function getHighlightedLineRange(state: OuterState) {
  return state.ui.get("highlightedLineRange");
}

export function getConditionalPanelLine(state: OuterState): null | number {
  return state.ui.get("conditionalPanelLine");
}

export function getProjectDirectoryRoot(state: OuterState): boolean {
  return state.ui.get("projectDirectoryRoot");
}

export function getOrientation(state: OuterState): boolean {
  return state.ui.get("orientation");
}

export default update;
<|endoftext|>


<commit_before>;(function rekapiCore (global) {

  if (!_) {
    throw 'underscore.js is required for Kapi.';
  }

  if (!Tweenable) {
    throw 'shifty.js is required for Kapi.';
  }

  var gk
      ,defaultConfig
      ,now;

  /**
   * Sorts an array numerically, from smallest to largest.
   * @param {Array} array The Array to sort.
   * @returns {Array} The sorted Array.
   */
  function sortNumerically (array) {
    return array.sort(function (a, b) {
      return a - b;
    });
  }

  function calculateCurrentMillisecond (kapi) {
    var currentMillisecond;

    //TODO: Some more work needs to be done in order to prevent auto-looping.
    currentMillisecond = (now() - kapi._loopTimestamp) % kapi._animationLength;

    return currentMillisecond;
  }

  function renderCurrentMillisecond (kapi) {
    var currentMillisecond;

    currentMillisecond = calculateCurrentMillisecond(kapi);
    kapi.render(currentMillisecond);
  }

  function tick (kapi) {
    kapi._loopId = setTimeout(function () {
      renderCurrentMillisecond(kapi);
      tick(kapi);
    }, 1000 / kapi.config.fps);
  }

  function noop () {
    // NOOP!
  }

  defaultConfig = {
    'fps': 30
  };

  now = Tweenable.util.now;

  /**
   * @param {HTMLCanvas} canvas
   * @param {Object} config
   * @param {Object} events
   */
  gk = global.Kapi || function Kapi (canvas, config, events) {
    this.canvas = canvas;
    this.canvas_context = canvas.getContext('2d');

    this.config = {};
    this._actors = {};
    this._drawOrder = [];

    // The setTimeout ID of `tick`
    this._loopId = null;

    // The UNIX time at which the animation loop started
    this._loopTimestamp = null;

    // Millisecond duration of the animation
    this._animationLength = null;

    _.extend(this.config, config);
    _.defaults(this.config, defaultConfig);

    return this;
  };

  gk.prototype.animationLength = function () {
    return this._animationLength;
  };

  /**
   * @param {number} millisecond
   */
  gk.prototype.render = function (millisecond) {
    var i, len
        ,currentActor
        ,curr
        ,canvas_context;

    len = this._drawOrder.length;
    curr = now();
    canvas_context = this.context();
    this.canvas_clear();

    for (i = 0; i < len; i++) {
      currentActor = this._actors[this._drawOrder[i]];
      currentActor
        .calculatePosition(millisecond)
        .draw(canvas_context, currentActor.get());
    }
  };

  gk.prototype.updateInternalState = function () {
    var allKeyframeLists;

    allKeyframeLists = [];

    _.each(this._drawOrder, function (i) {
      allKeyframeLists = allKeyframeLists.concat(allKeyframeLists,
          this._actors[i].keyframeList());
    }, this);

    this._animationLength = Math.max.apply(Math, allKeyframeLists);

    return this;
  };

  /**
   * @param {Kapi.Actor} actor
   * @param {Object} opt_initialState
   */
  gk.prototype.add = function (actor, opt_initialState) {
    actor.set(opt_initialState);
    this._actors[actor.id] = actor;
    this._drawOrder.push(actor.id);
  };

  gk.prototype.play = function () {
    this._loopTimestamp = now();
    tick(this);
  };

  gk.prototype.stop = function (alsoClear) {
    clearTimeout(this._loopId);

    if (alsoClear === true) {
      this.canvas_clear();
    }
  };

  gk.util = {};

  _.extend(gk.util, {
    'noop': noop
    ,'sortNumerically': sortNumerically
  });

  global.Kapi = gk;

} (this));<commit_msg>Some debug hooks.<commit_after>;(function rekapiCore (global) {

  if (!_) {
    throw 'underscore.js is required for Kapi.';
  }

  if (!Tweenable) {
    throw 'shifty.js is required for Kapi.';
  }

  var gk
      ,defaultConfig
      ,now;

  /**
   * Sorts an array numerically, from smallest to largest.
   * @param {Array} array The Array to sort.
   * @returns {Array} The sorted Array.
   */
  function sortNumerically (array) {
    return array.sort(function (a, b) {
      return a - b;
    });
  }

  function calculateCurrentMillisecond (kapi) {
    var currentMillisecond;

    //TODO: Some more work needs to be done in order to prevent auto-looping.
    currentMillisecond = (now() - kapi._loopTimestamp) % kapi._animationLength;

    return currentMillisecond;
  }

  function renderCurrentMillisecond (kapi) {
    var currentMillisecond;

    currentMillisecond = calculateCurrentMillisecond(kapi);
    kapi.render(currentMillisecond);
  }

  function tick (kapi) {
    kapi._loopId = setTimeout(function () {
      renderCurrentMillisecond(kapi);
      tick(kapi);
    }, 1000 / kapi.config.fps);
  }

  function noop () {
    // NOOP!
  }

  defaultConfig = {
    'fps': 30
  };

  if (KAPI_DEBUG === true && KAPI_DEBUG_NOW) {
    now = KAPI_DEBUG_NOW;
  } else {
    now = Tweenable.util.now;
  }

  /**
   * @param {HTMLCanvas} canvas
   * @param {Object} config
   * @param {Object} events
   */
  gk = global.Kapi || function Kapi (canvas, config, events) {
    this.canvas = canvas;
    this.canvas_context = canvas.getContext('2d');

    this.config = {};
    this._actors = {};
    this._drawOrder = [];

    // The setTimeout ID of `tick`
    this._loopId = null;

    // The UNIX time at which the animation loop started
    this._loopTimestamp = null;

    // Millisecond duration of the animation
    this._animationLength = null;

    _.extend(this.config, config);
    _.defaults(this.config, defaultConfig);

    return this;
  };

  gk.prototype.animationLength = function () {
    return this._animationLength;
  };

  /**
   * @param {number} millisecond
   */
  gk.prototype.render = function (millisecond) {
    var i, len
        ,currentActor
        ,curr
        ,canvas_context;

    len = this._drawOrder.length;
    curr = now();
    canvas_context = this.context();
    this.canvas_clear();

    // Having two loops here is horrifically inefficient, yes.  However, having
    // position updates occur in tandem with draws could cause weird
    // synchronicity issues.  For example, if the `draw` method of one actor
    // modifies the state of an actor that is drawn later in the loop.
    for (i = 0; i < len; i++) {
      currentActor = this._actors[this._drawOrder[i]];
      currentActor.calculatePosition(millisecond);
    }

    for (i = 0; i < len; i++) {
      currentActor = this._actors[this._drawOrder[i]];
      currentActor.draw(canvas_context, currentActor.get());
    }
  };

  gk.prototype.updateInternalState = function () {
    var allKeyframeLists;

    allKeyframeLists = [];

    _.each(this._drawOrder, function (i) {
      allKeyframeLists = allKeyframeLists.concat(allKeyframeLists,
          this._actors[i].keyframeList());
    }, this);

    this._animationLength = Math.max.apply(Math, allKeyframeLists);

    return this;
  };

  /**
   * @param {Kapi.Actor} actor
   * @param {Object} opt_initialState
   */
  gk.prototype.add = function (actor, opt_initialState) {
    actor.set(opt_initialState);
    this._actors[actor.id] = actor;
    this._drawOrder.push(actor.id);
  };

  gk.prototype.play = function () {
    this._loopTimestamp = now();
    tick(this);
  };

  gk.prototype.stop = function (alsoClear) {
    clearTimeout(this._loopId);

    if (alsoClear === true) {
      this.canvas_clear();
    }
  };

  gk.util = {};

  _.extend(gk.util, {
    'noop': noop
    ,'sortNumerically': sortNumerically
  });

  if (KAPI_DEBUG === true) {
    gk._private = {
      'sortNumerically': sortNumerically
      ,'calculateCurrentMillisecond': calculateCurrentMillisecond
      ,'renderCurrentMillisecond': renderCurrentMillisecond
      ,'tick': tick
    }
  }

  global.Kapi = gk;

} (this));<|endoftext|>


<commit_before>#ifndef __lang_Json__
#define __lang_Json__

////////////////////////////////////////////////////////////////////////////////
// Json

Json = function() { // Do not open scope as new function() so this == global == window
	// Support the native Json object if it is there:
	var JSON = this.JSON;
	var special = { '\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"' : '\\"', "'" : "\\'", '\\': '\\\\' };
	return {
		encode: function(obj, properties) {
#ifdef RHINO
			// Do not send native Java objects through JSON.stringify
			if (JSON && Base.type(obj) != 'java')
#else // !RHINO
			if (JSON)
#endif // !RHINO
				// Unfortunately IE does not natively support __proto__, so
				// we need to filter it out from Json
#ifdef BROWSER
				return JSON.stringify(obj, properties || Browser.TRIDENT && function(key, value) {
					return key == '__proto__' ? undefined : value;
				});
#else // !BROWSER
				return JSON.stringify(obj, properties);
#endif // !BROWSER
			if (Base.type(properties) == 'array') {
				// Convert properties to a lookup table:
				properties = properties.each(function(val) {
					this[val] = true;
				}, {});
			}
			switch (Base.type(obj)) {
				case 'string':
					return '"' + obj.replace(/[\x00-\x1f\\"]/g, function(chr) {
						return special[chr] || '\\u' + chr.charCodeAt(0).toPaddedString(4, 16);
					}) + '"';
				case 'array':
					return '[' + obj.collect(function(val) {
						return Json.encode(val, properties);
					}) + ']';
				case 'object':
				// Treat hash just like object
				case 'hash':
					return '{' + Hash.collect(obj, function(val, key) {
						if (!properties || properties[key]) {
							val = Json.encode(val, properties);
							if (val !== undefined)
								return Json.encode(key) + ':' + val;
						}
					}) + '}';
				// Filter out functions, they are not part of JSON
				case 'function':
					return undefined;
				default:
					return obj + '';
			}
			return undefined;
		},

		decode: function(str, secure) {
			try {
				// Make sure the incoming data is actual JSON
				// Logic borrowed from http://json.org/json2.js
#ifdef BROWSER
				// Make sure leading/trailing whitespace is removed (IE can't handle it)
				return Base.type(str) == 'string' && (str = str.trim()) &&
#else // !BROWSER
				return Base.type(str) == 'string' && str &&
#endif // !BROWSER
					// No need for security checks when using native JSON (?)
					(!secure || JSON || /^[\],:{}\s]*$/.test(
						str.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, "@")
							.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, "]")
							.replace(/(?:^|:|,)(?:\s*\[)+/g, "")))
								? JSON ? JSON.parse(str) : (new Function('return ' + str))() : null;
			} catch (e) {
				return null;
			}
		}
	};
}();

#endif // __lang_Json__<commit_msg>Improved Json code to better use native JSON object.<commit_after>#ifndef __lang_Json__
#define __lang_Json__

////////////////////////////////////////////////////////////////////////////////
// Json

#ifdef ECMASCRIPT_5

Json = {
#ifdef RHINO
	encode: function(obj, properties) {
		// Do not send native Java objects through JSON.stringify
		return Base.type(obj) != 'java' ? JSON.stringify(obj, properties) : null;
	},

#elif defined(BROWSER) // BROWSER
	// Unfortunately IE does not natively support __proto__, so
	// we need to filter it out from Json
	encode: function(obj, properties) {
		return JSON.stringify(obj, properties || Browser.TRIDENT && function(key, value) {
			return key == '__proto__' ? undefined : value;
		});
	},

#else // !RHINO && !BROWSER
	encode: JSON.stringify,
#endif // !RHINO && !BROWSER
	decode: JSON.parse
};

#else // !ECMASCRIPT_5

Json = function(JSON) {
	var special = { '\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"' : '\\"', "'" : "\\'", '\\': '\\\\' };
	// Support the native Json object if it is there:
	return {
		encode: JSON
#ifdef RHINO
			? function(obj, properties) {
				// Do not send native Java objects through JSON.stringify
				return Base.type(obj) != 'java' ? JSON.stringify(obj, properties) : null;
			}
#elif defined(BROWSER) // BROWSER
			? function(obj, properties) {
				// Unfortunately IE does not natively support __proto__, so
				// we need to filter it out from Json
				return JSON.stringify(obj, properties || Browser.TRIDENT && function(key, value) {
					return key == '__proto__' ? undefined : value;
				});
			}
#else // !RHINO && !BROWSER
			? JSON.stringify
#endif // !RHINO && !BROWSER
			: function(obj, properties) {
				if (Base.type(properties) == 'array') {
					// Convert properties to a lookup table:
					properties = properties.each(function(val) {
						this[val] = true;
					}, {});
				}
				switch (Base.type(obj)) {
					case 'string':
						return '"' + obj.replace(/[\x00-\x1f\\"]/g, function(chr) {
							return special[chr] || '\\u' + chr.charCodeAt(0).toPaddedString(4, 16);
						}) + '"';
					case 'array':
						return '[' + obj.collect(function(val) {
							return Json.encode(val, properties);
						}) + ']';
					case 'object':
					// Treat hash just like object
					case 'hash':
						return '{' + Hash.collect(obj, function(val, key) {
							if (!properties || properties[key]) {
								val = Json.encode(val, properties);
								if (val !== undefined)
									return Json.encode(key) + ':' + val;
							}
						}) + '}';
					// Filter out functions, they are not part of JSON
					case 'function':
						return undefined;
					default:
						return obj + '';
				}
				return null;
			},

		decode: JSON
			? JSON.parse
			: function(str, secure) {
				try {
					// Make sure the incoming data is actual JSON
					// Logic borrowed from http://json.org/json2.js
#ifdef BROWSER
					// Make sure leading/trailing whitespace is removed (IE can't handle it)
					return Base.type(str) == 'string' && (str = str.trim()) &&
#else // !BROWSER
					return Base.type(str) == 'string' && str &&
#endif // !BROWSER
						// No need for security checks when using native JSON (?)
						(!secure || /^[\],:{}\s]*$/.test(
							str.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, "@")
								.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, "]")
								.replace(/(?:^|:|,)(?:\s*\[)+/g, "")))
									? (new Function('return ' + str))() : null;
				} catch (e) {
					return null;
				}
			}
	};
}(this.JSON);

#endif // !ECMASCRIPT_5

#endif // __lang_Json__<|endoftext|>


<commit_before>const {Mark} = require("./mark")

// ::- You'll often have to '[resolve](#model.Node.resolve)' a
// position to get the context you need. Objects of this class
// represent such a resolved position, providing various pieces of
// context information and helper methods.
//
// Throughout this interface, methods that take an optional `depth`
// parameter will interpret undefined as `this.depth` and negative
// numbers as `this.depth + value`.
class ResolvedPos {
  constructor(pos, path, parentOffset) {
    // :: number The position that was resolved.
    this.pos = pos
    this.path = path
    // :: number
    // The number of levels the parent node is from the root. If this
    // position points directly into the root, it is 0. If it points
    // into a top-level paragraph, 1, and so on.
    this.depth = path.length / 3 - 1
    // :: number The offset this position has into its parent node.
    this.parentOffset = parentOffset
  }

  resolveDepth(val) {
    if (val == null) return this.depth
    if (val < 0) return this.depth + val
    return val
  }

  // :: Node
  // The parent node that the position points into. Note that even if
  // a position points into a text node, that node is not considered
  // the parent—text nodes are 'flat' in this model.
  get parent() { return this.node(this.depth) }

  // :: Node
  // The root node in which the position was resolved.
  get doc() { return this.node(0) }

  // :: (?number) → Node
  // The ancestor node at the given level. `p.node(p.depth)` is the
  // same as `p.parent`.
  node(depth) { return this.path[this.resolveDepth(depth) * 3] }

  // :: (?number) → number
  // The index into the ancestor at the given level. If this points at
  // the 3rd node in the 2nd paragraph on the top level, for example,
  // `p.index(0)` is 2 and `p.index(1)` is 3.
  index(depth) { return this.path[this.resolveDepth(depth) * 3 + 1] }

  // :: (?number) → number
  // The index pointing after this position into the ancestor at the
  // given level.
  indexAfter(depth) {
    depth = this.resolveDepth(depth)
    return this.index(depth) + (depth == this.depth && !this.textOffset ? 0 : 1)
  }

  // :: (?number) → number
  // The (absolute) position at the start of the node at the given
  // level.
  start(depth) {
    depth = this.resolveDepth(depth)
    return depth == 0 ? 0 : this.path[depth * 3 - 1] + 1
  }

  // :: (?number) → number
  // The (absolute) position at the end of the node at the given
  // level.
  end(depth) {
    depth = this.resolveDepth(depth)
    return this.start(depth) + this.node(depth).content.size
  }

  // :: (?number) → number
  // The (absolute) position directly before the node at the given
  // level, or, when `level` is `this.depth + 1`, the original
  // position.
  before(depth) {
    depth = this.resolveDepth(depth)
    if (!depth) throw new RangeError("There is no position before the top-level node")
    return depth == this.depth + 1 ? this.pos : this.path[depth * 3 - 1]
  }

  // :: (?number) → number
  // The (absolute) position directly after the node at the given
  // level, or, when `level` is `this.depth + 1`, the original
  // position.
  after(depth) {
    depth = this.resolveDepth(depth)
    if (!depth) throw new RangeError("There is no position after the top-level node")
    return depth == this.depth + 1 ? this.pos : this.path[depth * 3 - 1] + this.path[depth * 3].nodeSize
  }

  // :: number
  // When this position points into a text node, this returns the
  // distance between the position and the start of the text node.
  // Will be zero for positions that point between nodes.
  get textOffset() { return this.pos - this.path[this.path.length - 1] }

  // :: ?Node
  // Get the node directly after the position, if any. If the position
  // points into a text node, only the part of that node after the
  // position is returned.
  get nodeAfter() {
    let parent = this.parent, index = this.index(this.depth)
    if (index == parent.childCount) return null
    let dOff = this.pos - this.path[this.path.length - 1], child = parent.child(index)
    return dOff ? parent.child(index).cut(dOff) : child
  }

  // :: ?Node
  // Get the node directly before the position, if any. If the
  // position points into a text node, only the part of that node
  // before the position is returned.
  get nodeBefore() {
    let index = this.index(this.depth)
    let dOff = this.pos - this.path[this.path.length - 1]
    if (dOff) return this.parent.child(index).cut(0, dOff)
    return index == 0 ? null : this.parent.child(index - 1)
  }

  // :: (?bool) → [Mark]
  // Get the marks at this position, factoring in the surrounding
  // marks' [`inclusive`](#model.MarkSpec.inclusive) property. If the
  // position is at the start of a non-empty node, or `after` is true,
  // the marks of the node after it (if any) are returned.
  marks(after) {
    let parent = this.parent, index = this.index()

    // In an empty parent, return the empty array
    if (parent.content.size == 0) return Mark.none

    // When inside a text node, just return the text node's marks
    if (this.textOffset) return parent.child(index).marks

    let main = parent.maybeChild(index - 1), other = parent.maybeChild(index)
    // If the `after` flag is true of there is no node before, make
    // the node after this position the main reference.
    if ((after && other) || !main) { let tmp = main; main = other; other = tmp }

    // Use all marks in the main node, except those that have
    // `inclusive` set to false and are not present in the other node.
    let marks = main.marks
    for (var i = 0; i < marks.length; i++)
      if (marks[i].type.spec.inclusive === false && (!other || !marks[i].isInSet(other.marks)))
        marks = marks[i--].removeFromSet(marks)

    return marks
  }

  // :: (number) → number
  // The depth up to which this position and the given (non-resolved)
  // position share the same parent nodes.
  sharedDepth(pos) {
    for (let depth = this.depth; depth > 0; depth--)
      if (this.start(depth) <= pos && this.end(depth) >= pos) return depth
    return 0
  }

  // :: (?ResolvedPos, ?(Node) → bool) → ?NodeRange
  // Returns a range based on the place where this position and the
  // given position diverge around block content. If both point into
  // the same textblock, for example, a range around that textblock
  // will be returned. If they point into different blocks, the range
  // around those blocks or their ancestors in their common ancestor
  // is returned. You can pass in an optional predicate that will be
  // called with a parent node to see if a range into that parent is
  // acceptable.
  blockRange(other = this, pred) {
    if (other.pos < this.pos) return other.blockRange(this)
    for (let d = this.depth - (this.parent.inlineContent || this.pos == other.pos ? 1 : 0); d >= 0; d--)
      if (other.pos <= this.end(d) && (!pred || pred(this.node(d))))
        return new NodeRange(this, other, d)
  }

  // :: (ResolvedPos) → bool
  // Query whether the given position shares the same parent node.
  sameParent(other) {
    return this.pos - this.parentOffset == other.pos - other.parentOffset
  }

  // :: (ResolvedPos) → ResolvedPos
  // Return the greater of this and the given position.
  max(other) {
    return other.pos > this.pos ? other : this
  }

  // :: (ResolvedPos) → ResolvedPos
  // Return the smaller of this and the given position.
  min(other) {
    return other.pos < this.pos ? other : this
  }

  toString() {
    let str = ""
    for (let i = 1; i <= this.depth; i++)
      str += (str ? "/" : "") + this.node(i).type.name + "_" + this.index(i - 1)
    return str + ":" + this.parentOffset
  }

  static resolve(doc, pos) {
    if (!(pos >= 0 && pos <= doc.content.size)) throw new RangeError("Position " + pos + " out of range")
    let path = []
    let start = 0, parentOffset = pos
    for (let node = doc;;) {
      let {index, offset} = node.content.findIndex(parentOffset)
      let rem = parentOffset - offset
      path.push(node, index, start + offset)
      if (!rem) break
      node = node.child(index)
      if (node.isText) break
      parentOffset = rem - 1
      start += offset + 1
    }
    return new ResolvedPos(pos, path, parentOffset)
  }

  static resolveCached(doc, pos) {
    for (let i = 0; i < resolveCache.length; i++) {
      let cached = resolveCache[i]
      if (cached.pos == pos && cached.node(0) == doc) return cached
    }
    let result = resolveCache[resolveCachePos] = ResolvedPos.resolve(doc, pos)
    resolveCachePos = (resolveCachePos + 1) % resolveCacheSize
    return result
  }
}
exports.ResolvedPos = ResolvedPos

let resolveCache = [], resolveCachePos = 0, resolveCacheSize = 6

// ::- Represents a flat range of content.
class NodeRange {
  constructor($from, $to, depth) {
    // :: ResolvedPos A resolved position along the start of the
    // content. May have a `depth` greater than this object's `depth`
    // property, since these are the positions that were used to
    // compute the range, not re-resolved positions directly at its
    // boundaries.
    this.$from = $from
    // :: ResolvedPos A position along the end of the content. See
    // caveat for [`$from`](#model.NodeRange.$from).
    this.$to = $to
    // :: number The depth of the node that this range points into.
    this.depth = depth
  }

  // :: number The position at the start of the range.
  get start() { return this.$from.before(this.depth + 1) }
  // :: number The position at the end of the range.
  get end() { return this.$to.after(this.depth + 1) }

  // :: Node The parent node that the range points into.
  get parent() { return this.$from.node(this.depth) }
  // :: number The start index of the range in the parent node.
  get startIndex() { return this.$from.index(this.depth) }
  // :: number The end index of the range in the parent node.
  get endIndex() { return this.$to.indexAfter(this.depth) }
}
exports.NodeRange = NodeRange
<commit_msg>Export `NodeRange` constructor<commit_after>const {Mark} = require("./mark")

// ::- You'll often have to '[resolve](#model.Node.resolve)' a
// position to get the context you need. Objects of this class
// represent such a resolved position, providing various pieces of
// context information and helper methods.
//
// Throughout this interface, methods that take an optional `depth`
// parameter will interpret undefined as `this.depth` and negative
// numbers as `this.depth + value`.
class ResolvedPos {
  constructor(pos, path, parentOffset) {
    // :: number The position that was resolved.
    this.pos = pos
    this.path = path
    // :: number
    // The number of levels the parent node is from the root. If this
    // position points directly into the root, it is 0. If it points
    // into a top-level paragraph, 1, and so on.
    this.depth = path.length / 3 - 1
    // :: number The offset this position has into its parent node.
    this.parentOffset = parentOffset
  }

  resolveDepth(val) {
    if (val == null) return this.depth
    if (val < 0) return this.depth + val
    return val
  }

  // :: Node
  // The parent node that the position points into. Note that even if
  // a position points into a text node, that node is not considered
  // the parent—text nodes are 'flat' in this model.
  get parent() { return this.node(this.depth) }

  // :: Node
  // The root node in which the position was resolved.
  get doc() { return this.node(0) }

  // :: (?number) → Node
  // The ancestor node at the given level. `p.node(p.depth)` is the
  // same as `p.parent`.
  node(depth) { return this.path[this.resolveDepth(depth) * 3] }

  // :: (?number) → number
  // The index into the ancestor at the given level. If this points at
  // the 3rd node in the 2nd paragraph on the top level, for example,
  // `p.index(0)` is 2 and `p.index(1)` is 3.
  index(depth) { return this.path[this.resolveDepth(depth) * 3 + 1] }

  // :: (?number) → number
  // The index pointing after this position into the ancestor at the
  // given level.
  indexAfter(depth) {
    depth = this.resolveDepth(depth)
    return this.index(depth) + (depth == this.depth && !this.textOffset ? 0 : 1)
  }

  // :: (?number) → number
  // The (absolute) position at the start of the node at the given
  // level.
  start(depth) {
    depth = this.resolveDepth(depth)
    return depth == 0 ? 0 : this.path[depth * 3 - 1] + 1
  }

  // :: (?number) → number
  // The (absolute) position at the end of the node at the given
  // level.
  end(depth) {
    depth = this.resolveDepth(depth)
    return this.start(depth) + this.node(depth).content.size
  }

  // :: (?number) → number
  // The (absolute) position directly before the node at the given
  // level, or, when `level` is `this.depth + 1`, the original
  // position.
  before(depth) {
    depth = this.resolveDepth(depth)
    if (!depth) throw new RangeError("There is no position before the top-level node")
    return depth == this.depth + 1 ? this.pos : this.path[depth * 3 - 1]
  }

  // :: (?number) → number
  // The (absolute) position directly after the node at the given
  // level, or, when `level` is `this.depth + 1`, the original
  // position.
  after(depth) {
    depth = this.resolveDepth(depth)
    if (!depth) throw new RangeError("There is no position after the top-level node")
    return depth == this.depth + 1 ? this.pos : this.path[depth * 3 - 1] + this.path[depth * 3].nodeSize
  }

  // :: number
  // When this position points into a text node, this returns the
  // distance between the position and the start of the text node.
  // Will be zero for positions that point between nodes.
  get textOffset() { return this.pos - this.path[this.path.length - 1] }

  // :: ?Node
  // Get the node directly after the position, if any. If the position
  // points into a text node, only the part of that node after the
  // position is returned.
  get nodeAfter() {
    let parent = this.parent, index = this.index(this.depth)
    if (index == parent.childCount) return null
    let dOff = this.pos - this.path[this.path.length - 1], child = parent.child(index)
    return dOff ? parent.child(index).cut(dOff) : child
  }

  // :: ?Node
  // Get the node directly before the position, if any. If the
  // position points into a text node, only the part of that node
  // before the position is returned.
  get nodeBefore() {
    let index = this.index(this.depth)
    let dOff = this.pos - this.path[this.path.length - 1]
    if (dOff) return this.parent.child(index).cut(0, dOff)
    return index == 0 ? null : this.parent.child(index - 1)
  }

  // :: (?bool) → [Mark]
  // Get the marks at this position, factoring in the surrounding
  // marks' [`inclusive`](#model.MarkSpec.inclusive) property. If the
  // position is at the start of a non-empty node, or `after` is true,
  // the marks of the node after it (if any) are returned.
  marks(after) {
    let parent = this.parent, index = this.index()

    // In an empty parent, return the empty array
    if (parent.content.size == 0) return Mark.none

    // When inside a text node, just return the text node's marks
    if (this.textOffset) return parent.child(index).marks

    let main = parent.maybeChild(index - 1), other = parent.maybeChild(index)
    // If the `after` flag is true of there is no node before, make
    // the node after this position the main reference.
    if ((after && other) || !main) { let tmp = main; main = other; other = tmp }

    // Use all marks in the main node, except those that have
    // `inclusive` set to false and are not present in the other node.
    let marks = main.marks
    for (var i = 0; i < marks.length; i++)
      if (marks[i].type.spec.inclusive === false && (!other || !marks[i].isInSet(other.marks)))
        marks = marks[i--].removeFromSet(marks)

    return marks
  }

  // :: (number) → number
  // The depth up to which this position and the given (non-resolved)
  // position share the same parent nodes.
  sharedDepth(pos) {
    for (let depth = this.depth; depth > 0; depth--)
      if (this.start(depth) <= pos && this.end(depth) >= pos) return depth
    return 0
  }

  // :: (?ResolvedPos, ?(Node) → bool) → ?NodeRange
  // Returns a range based on the place where this position and the
  // given position diverge around block content. If both point into
  // the same textblock, for example, a range around that textblock
  // will be returned. If they point into different blocks, the range
  // around those blocks or their ancestors in their common ancestor
  // is returned. You can pass in an optional predicate that will be
  // called with a parent node to see if a range into that parent is
  // acceptable.
  blockRange(other = this, pred) {
    if (other.pos < this.pos) return other.blockRange(this)
    for (let d = this.depth - (this.parent.inlineContent || this.pos == other.pos ? 1 : 0); d >= 0; d--)
      if (other.pos <= this.end(d) && (!pred || pred(this.node(d))))
        return new NodeRange(this, other, d)
  }

  // :: (ResolvedPos) → bool
  // Query whether the given position shares the same parent node.
  sameParent(other) {
    return this.pos - this.parentOffset == other.pos - other.parentOffset
  }

  // :: (ResolvedPos) → ResolvedPos
  // Return the greater of this and the given position.
  max(other) {
    return other.pos > this.pos ? other : this
  }

  // :: (ResolvedPos) → ResolvedPos
  // Return the smaller of this and the given position.
  min(other) {
    return other.pos < this.pos ? other : this
  }

  toString() {
    let str = ""
    for (let i = 1; i <= this.depth; i++)
      str += (str ? "/" : "") + this.node(i).type.name + "_" + this.index(i - 1)
    return str + ":" + this.parentOffset
  }

  static resolve(doc, pos) {
    if (!(pos >= 0 && pos <= doc.content.size)) throw new RangeError("Position " + pos + " out of range")
    let path = []
    let start = 0, parentOffset = pos
    for (let node = doc;;) {
      let {index, offset} = node.content.findIndex(parentOffset)
      let rem = parentOffset - offset
      path.push(node, index, start + offset)
      if (!rem) break
      node = node.child(index)
      if (node.isText) break
      parentOffset = rem - 1
      start += offset + 1
    }
    return new ResolvedPos(pos, path, parentOffset)
  }

  static resolveCached(doc, pos) {
    for (let i = 0; i < resolveCache.length; i++) {
      let cached = resolveCache[i]
      if (cached.pos == pos && cached.node(0) == doc) return cached
    }
    let result = resolveCache[resolveCachePos] = ResolvedPos.resolve(doc, pos)
    resolveCachePos = (resolveCachePos + 1) % resolveCacheSize
    return result
  }
}
exports.ResolvedPos = ResolvedPos

let resolveCache = [], resolveCachePos = 0, resolveCacheSize = 6

// ::- Represents a flat range of content.
class NodeRange {
  // :: (ResolvedPos, ResolvedPos, number)
  // Construct a node range. `$from` and `$to` should point into the
  // same node until at least the given `depth`, since a node range
  // denotes an adjacent set of nodes in a single parent node.
  constructor($from, $to, depth) {
    // :: ResolvedPos A resolved position along the start of the
    // content. May have a `depth` greater than this object's `depth`
    // property, since these are the positions that were used to
    // compute the range, not re-resolved positions directly at its
    // boundaries.
    this.$from = $from
    // :: ResolvedPos A position along the end of the content. See
    // caveat for [`$from`](#model.NodeRange.$from).
    this.$to = $to
    // :: number The depth of the node that this range points into.
    this.depth = depth
  }

  // :: number The position at the start of the range.
  get start() { return this.$from.before(this.depth + 1) }
  // :: number The position at the end of the range.
  get end() { return this.$to.after(this.depth + 1) }

  // :: Node The parent node that the range points into.
  get parent() { return this.$from.node(this.depth) }
  // :: number The start index of the range in the parent node.
  get startIndex() { return this.$from.index(this.depth) }
  // :: number The end index of the range in the parent node.
  get endIndex() { return this.$to.indexAfter(this.depth) }
}
exports.NodeRange = NodeRange
<|endoftext|>


<commit_before>roleSpawner = {

    run: function(roomId) {
	    var store = {
	        'harvester': 2,
	        'worker': 5,
	        'reserver': 1,
	    };

	    for(role in store) {
	        var count = store[role];
	        var creeps = _.filter(Game.creeps, (creep) => creep.memory.role == role && creep.memory.home == roomId);
	        if(creeps.length < count) {

	            var build = undefined;
	            switch (role) {
                    case 'harvester':
                        build = roleSpawner.currentHarvesterBuild();
                        break;
	                case 'worker':
	                    build = roleSpawner.currentWorkerBuild();
	                    break;
	                case 'reserver':
	                    build = roleSpawner.currentReserverBuild();
	                    break;
                    case 'claimer':
                        build = roleSpawner.currentClaimerBuild();
                        break;
                    case 'settler':
                        build = roleSpawner.currentSettlerBuild();
                        break;
	            }
                var spawnName = Memory[roomId].spawns[0];

                var targetHome = roomId;
                if(role == 'settler') {
                    targetHome = Memory.expansionTarget;
                }

		        var newName = Game.spawns[spawnName].createCreep(build, undefined, {role: role, home: targetHome});
                if(newName != ERR_BUSY && newName != ERR_NOT_ENOUGH_ENERGY) {
                    console.log('spawning new ' + role + ': ' + newName);
                }
	        }
	    }
    },

    assembleBuild: function(stats) {
        var build = [];
        for(part in stats) {
            for(var i=0; i < stats[part]; i++) {
                var partCode = undefined;
                switch (part) {
                    case 'WORK':
                        partCode = WORK;
                        break;
                    case 'CARRY':
                        partCode = CARRY;
                        break;
                    case 'MOVE':
                        partCode = MOVE;
                        break;
                }

                build.push(partCode);
            }
        }
        return build;
    },

    currentWorkerBuild: function() {
        var spawnRoomCapacity = Game.rooms[Memory.home].energyCapacityAvailable;
        if(spawnRoomCapacity >= 1300) {
            return roleSpawner.assembleBuild({
                WORK: 4,
                CARRY: 7,
                MOVE: 11,
            })
        } else if(spawnRoomCapacity >= 800) {
            return roleSpawner.assembleBuild({
                WORK: 2,
                CARRY: 5,
                MOVE: 7,
            })
        } else if(spawnRoomCapacity >= 550) {
            return roleSpawner.assembleBuild({
                WORK: 2,
                CARRY: 3,
                MOVE: 4,
            })
        } else {
            return [WORK,CARRY,MOVE];
        }
    },

    currentHarvesterBuild: function() {
        var spawnRoomCapacity = Game.rooms[Memory.home].energyCapacityAvailable;
        if(spawnRoomCapacity >= 1300) {
            return roleSpawner.assembleBuild({
                WORK: 6,
                CARRY: 4,
                MOVE: 6,
            })
        } else if(spawnRoomCapacity >= 800) {
            return roleSpawner.assembleBuild({
                WORK: 4,
                CARRY: 4,
                MOVE: 4,
            })
        } else if(spawnRoomCapacity >= 550) {
            return roleSpawner.assembleBuild({
                WORK: 3,
                CARRY: 2,
                MOVE: 3,
            })
        } else {
            return [WORK,CARRY,MOVE];
        }
    },

    currentReserverBuild: function() {
        return [CLAIM, CLAIM, MOVE, MOVE];
    },

    currentClaimerBuild: function() {
        return [CLAIM, CARRY, CARRY, CARRY, CARRY, CARRY, MOVE, MOVE, MOVE, MOVE, MOVE, MOVE];
    },

    currentSettlerBuild: function() {
        return roleSpawner.assembleBuild({
            WORK: 4,
            CARRY: 4,
            MOVE: 8,
        })
    },
}

module.exports = roleSpawner;
<commit_msg>only 4 workers<commit_after>roleSpawner = {

    run: function(roomId) {
	    var store = {
	        'harvester': 2,
	        'worker': 4,
	        'reserver': 1,
	    };

	    for(role in store) {
	        var count = store[role];
	        var creeps = _.filter(Game.creeps, (creep) => creep.memory.role == role && creep.memory.home == roomId);
	        if(creeps.length < count) {

	            var build = undefined;
	            switch (role) {
                    case 'harvester':
                        build = roleSpawner.currentHarvesterBuild();
                        break;
	                case 'worker':
	                    build = roleSpawner.currentWorkerBuild();
	                    break;
	                case 'reserver':
	                    build = roleSpawner.currentReserverBuild();
	                    break;
                    case 'claimer':
                        build = roleSpawner.currentClaimerBuild();
                        break;
                    case 'settler':
                        build = roleSpawner.currentSettlerBuild();
                        break;
	            }
                var spawnName = Memory[roomId].spawns[0];

                var targetHome = roomId;
                if(role == 'settler') {
                    targetHome = Memory.expansionTarget;
                }

		        var newName = Game.spawns[spawnName].createCreep(build, undefined, {role: role, home: targetHome});
                if(newName != ERR_BUSY && newName != ERR_NOT_ENOUGH_ENERGY) {
                    console.log('spawning new ' + role + ': ' + newName);
                }
	        }
	    }
    },

    assembleBuild: function(stats) {
        var build = [];
        for(part in stats) {
            for(var i=0; i < stats[part]; i++) {
                var partCode = undefined;
                switch (part) {
                    case 'WORK':
                        partCode = WORK;
                        break;
                    case 'CARRY':
                        partCode = CARRY;
                        break;
                    case 'MOVE':
                        partCode = MOVE;
                        break;
                }

                build.push(partCode);
            }
        }
        return build;
    },

    currentWorkerBuild: function() {
        var spawnRoomCapacity = Game.rooms[Memory.home].energyCapacityAvailable;
        if(spawnRoomCapacity >= 1300) {
            return roleSpawner.assembleBuild({
                WORK: 4,
                CARRY: 7,
                MOVE: 11,
            })
        } else if(spawnRoomCapacity >= 800) {
            return roleSpawner.assembleBuild({
                WORK: 2,
                CARRY: 5,
                MOVE: 7,
            })
        } else if(spawnRoomCapacity >= 550) {
            return roleSpawner.assembleBuild({
                WORK: 2,
                CARRY: 3,
                MOVE: 4,
            })
        } else {
            return [WORK,CARRY,MOVE];
        }
    },

    currentHarvesterBuild: function() {
        var spawnRoomCapacity = Game.rooms[Memory.home].energyCapacityAvailable;
        if(spawnRoomCapacity >= 1300) {
            return roleSpawner.assembleBuild({
                WORK: 6,
                CARRY: 4,
                MOVE: 6,
            })
        } else if(spawnRoomCapacity >= 800) {
            return roleSpawner.assembleBuild({
                WORK: 4,
                CARRY: 4,
                MOVE: 4,
            })
        } else if(spawnRoomCapacity >= 550) {
            return roleSpawner.assembleBuild({
                WORK: 3,
                CARRY: 2,
                MOVE: 3,
            })
        } else {
            return [WORK,CARRY,MOVE];
        }
    },

    currentReserverBuild: function() {
        return [CLAIM, CLAIM, MOVE, MOVE];
    },

    currentClaimerBuild: function() {
        return [CLAIM, CARRY, CARRY, CARRY, CARRY, CARRY, MOVE, MOVE, MOVE, MOVE, MOVE, MOVE];
    },

    currentSettlerBuild: function() {
        return roleSpawner.assembleBuild({
            WORK: 4,
            CARRY: 4,
            MOVE: 8,
        })
    },
}

module.exports = roleSpawner;
<|endoftext|>


<commit_before>import React from "react";
import { mapper } from "../containers/container";
import { updateHistory } from "./index";

const handleClick = (ev, update) => {
  ev.preventDefault();
  update();
};

const Link = ({ update, href, children }) => (
   handleClick(ev, update)}>
    {children}

);

const actionsFromStore = {
  update: (store, { href }) => updateHistory(store, href)
};

export default mapper({}, actionsFromStore)(Link);
<commit_msg>spread props on Link<commit_after>import React from "react";
import { mapper } from "../containers/container";
import { updateHistory } from "./index";

const handleClick = (ev, update) => {
  ev.preventDefault();
  update();
};

const Link = ({ update, href, children, ...rest }) => (
   handleClick(ev, update)} {...rest}>
    {children}

);

const actionsFromStore = {
  update: (store, { href }) => updateHistory(store, href)
};

export default mapper({}, actionsFromStore)(Link);
<|endoftext|>


<commit_before>import Boot    from './app/states/Boot';
import Preload from './app/states/Preload';
import Logo    from './app/states/Logo';
import Title   from './app/states/Title';
import Levels  from './app/states/Levels';
import Credits from './app/states/Credits';
import Game    from './app/states/Game';

export function start () {
  var game = new Phaser.Game(240, 160, Phaser.AUTO);

  [
    Boot,
    Preload,
    Logo,
    Title,
    Levels,
    Credits,
    Game
  ].map(state => game.state.add(state.name, state));

  game.state.start('Boot');

  return game;
}
<commit_msg>Face palm: class identifiers get mangled by Uglify<commit_after>import Boot    from './app/states/Boot';
import Preload from './app/states/Preload';
import Logo    from './app/states/Logo';
import Title   from './app/states/Title';
import Levels  from './app/states/Levels';
import Credits from './app/states/Credits';
import Game    from './app/states/Game';

export function start () {
  var game = new Phaser.Game(240, 160, Phaser.AUTO);

  [
    [ 'Boot'   , Boot    ],
    [ 'Preload', Preload ],
    [ 'Logo'   , Logo    ],
    [ 'Title'  , Title   ],
    [ 'Levels' , Levels  ],
    [ 'Credits', Credits ],
    [ 'Game'   , Game    ],
  ].map(([name, state]) => game.state.add(name, state));

  game.state.start('Boot');

  return game;
}
<|endoftext|>