<commit_before>var EventEmitter = require('events').EventEmitter;
var debug = require('debug')('mongo-mock:cursor');
var asyncish = require('../').asyncish;
var sift = require('../sift.js');
var _ = require('lodash');
var ObjectId = require('bson-objectid');

var Cursor = module.exports = function(documents, opts) {
  debug('initializing cursor');
  var i = 0;
  var state = Cursor.INIT;
  if(!documents) documents = [];

  function getDocs(applySkipLimit) {
    state = Cursor.OPEN;
    var docs = sift(opts.query, documents);
    if (opts.sort) {
      docs = docs.sort(function(a,b) {
        retVal = 0;
        for (var field in opts.sort) {
          var aVal = _.get(a, field);
          var bVal = _.get(b, field);
          if (aVal > bVal) retVal = 1;
          else if (aVal < bVal) retVal = -1;
          retVal *= opts.sort[field];

          if (retVal !== 0) break; // no need to continue;
        }

        return retVal;
      });
    }
    if (applySkipLimit) {
      docs = docs.slice(opts.skip||0, opts.skip+(opts.limit||docs.length));
    }
    docs = _.cloneDeep(docs, cloneObjectIDs);

    if(docs.length && opts.fields) {
      var props = Object.keys(opts.fields);
      var type = opts.fields[props[0]];
      type = type === 1 ? "pick" : type === -1 ? "omit" : undefined;
      if (docs.length && type) {
        docs = docs.map(function (doc) {
          return _[type](doc, props);//only supports simple projections. PRs welcome! :)
        });
      }
    }
    return docs;
  }

  var interface = {
    cmd: opts,

    batchSize: NotImplemented,

    clone: NotImplemented,

    close: function (callback) {
      state = Cursor.CLOSED;
      docs = [];
      debug('closing cursor');
      interface.emit('close');
      if(callback) return callback(null, interface);
    },

    count: function (applySkipLimit, callback) {
      callback = arguments[arguments.length-1];
      applySkipLimit = (applySkipLimit === callback) ? false : applySkipLimit;
      if(typeof callback !== 'function')
        return Promise.resolve(getDocs(applySkipLimit).length);

      asyncish(function () {
        callback(null, getDocs(applySkipLimit).length)
      });
    },

    each: NotImplemented,

    limit: function (n) {
      if(state !== Cursor.INIT)
        throw new Error('MongoError: Cursor is closed');
      opts.limit = n;
      return this;
    },

    next: function (callback) {
      var docs = getDocs(true);
      var limit = Math.min(opts.limit || Number.MAX_VALUE, docs.length);
      var next_idx = i<limit? i++ : i;
      var doc = docs[next_idx] || null;
      if(typeof callback !== 'function')
        return Promise.resolve(doc);

      asyncish(function () {
        callback(null, doc);
      });
    },

    rewind: function () {
      i = 0;
    },

    size: function(callback) {
      return this.count(true, callback);
    },

    skip: function (n) {
      if(state !== Cursor.INIT)
        throw new Error('MongoError: Cursor is closed');
      opts.skip = n;
      return this;
    },

    sort: function(fields) {
      if(state !== Cursor.INIT)
        throw new Error('MongoError: Cursor is closed');
      opts.sort = fields;
      return this;
    },

    toArray: function (callback) {
      debug('cursor.toArray()');

      function done() {
        interface.rewind();
        return getDocs(true);
      }

      if(!callback)
        return Promise.resolve(done());

      asyncish(function () {
        callback(null, done())
      });
    }
  };
  interface.__proto__ = EventEmitter.prototype;
  return interface;
};
Cursor.INIT = 0;
Cursor.OPEN = 1;
Cursor.CLOSED = 2;

function NotImplemented(){
  throw Error('Not Implemented');
}

function cloneObjectIDs(value) {
  return value instanceof ObjectId? ObjectId(value) : undefined;
}
<commit_msg>Scope retval.<commit_after>var EventEmitter = require('events').EventEmitter;
var debug = require('debug')('mongo-mock:cursor');
var asyncish = require('../').asyncish;
var sift = require('../sift.js');
var _ = require('lodash');
var ObjectId = require('bson-objectid');

var Cursor = module.exports = function(documents, opts) {
  debug('initializing cursor');
  var i = 0;
  var state = Cursor.INIT;
  if(!documents) documents = [];

  function getDocs(applySkipLimit) {
    state = Cursor.OPEN;
    var docs = sift(opts.query, documents);
    if (opts.sort) {
      docs = docs.sort(function(a,b) {
        var retVal = 0;
        for (var field in opts.sort) {
          var aVal = _.get(a, field);
          var bVal = _.get(b, field);
          if (aVal > bVal) retVal = 1;
          else if (aVal < bVal) retVal = -1;
          retVal *= opts.sort[field];

          if (retVal !== 0) break; // no need to continue;
        }

        return retVal;
      });
    }
    if (applySkipLimit) {
      docs = docs.slice(opts.skip||0, opts.skip+(opts.limit||docs.length));
    }
    docs = _.cloneDeep(docs, cloneObjectIDs);

    if(docs.length && opts.fields) {
      var props = Object.keys(opts.fields);
      var type = opts.fields[props[0]];
      type = type === 1 ? "pick" : type === -1 ? "omit" : undefined;
      if (docs.length && type) {
        docs = docs.map(function (doc) {
          return _[type](doc, props);//only supports simple projections. PRs welcome! :)
        });
      }
    }
    return docs;
  }

  var interface = {
    cmd: opts,

    batchSize: NotImplemented,

    clone: NotImplemented,

    close: function (callback) {
      state = Cursor.CLOSED;
      docs = [];
      debug('closing cursor');
      interface.emit('close');
      if(callback) return callback(null, interface);
    },

    count: function (applySkipLimit, callback) {
      callback = arguments[arguments.length-1];
      applySkipLimit = (applySkipLimit === callback) ? false : applySkipLimit;
      if(typeof callback !== 'function')
        return Promise.resolve(getDocs(applySkipLimit).length);

      asyncish(function () {
        callback(null, getDocs(applySkipLimit).length)
      });
    },

    each: NotImplemented,

    limit: function (n) {
      if(state !== Cursor.INIT)
        throw new Error('MongoError: Cursor is closed');
      opts.limit = n;
      return this;
    },

    next: function (callback) {
      var docs = getDocs(true);
      var limit = Math.min(opts.limit || Number.MAX_VALUE, docs.length);
      var next_idx = i<limit? i++ : i;
      var doc = docs[next_idx] || null;
      if(typeof callback !== 'function')
        return Promise.resolve(doc);

      asyncish(function () {
        callback(null, doc);
      });
    },

    rewind: function () {
      i = 0;
    },

    size: function(callback) {
      return this.count(true, callback);
    },

    skip: function (n) {
      if(state !== Cursor.INIT)
        throw new Error('MongoError: Cursor is closed');
      opts.skip = n;
      return this;
    },

    sort: function(fields) {
      if(state !== Cursor.INIT)
        throw new Error('MongoError: Cursor is closed');
      opts.sort = fields;
      return this;
    },

    toArray: function (callback) {
      debug('cursor.toArray()');

      function done() {
        interface.rewind();
        return getDocs(true);
      }

      if(!callback)
        return Promise.resolve(done());

      asyncish(function () {
        callback(null, done())
      });
    }
  };
  interface.__proto__ = EventEmitter.prototype;
  return interface;
};
Cursor.INIT = 0;
Cursor.OPEN = 1;
Cursor.CLOSED = 2;

function NotImplemented(){
  throw Error('Not Implemented');
}

function cloneObjectIDs(value) {
  return value instanceof ObjectId? ObjectId(value) : undefined;
}
<|endoftext|>


<commit_before>Hummingbird.CommunityWidgetComponent = Ember.Component.extend({
  classNames: ["community-widget-panel"],
  topics: [],

  // Number of loading fake posts to show.
  loadingSeq: [1, 2, 3, 4, 5],

  loadPosts: function() {
    var self = this;

    ic.ajax({
      url: this.get('apiCall'),
      type: "GET",
      xhrFields: {
        withCredentials: true
      }
    }).then(function(data) {

      var users = {},
          topics = [];

      data.users.forEach(function(user) {
        users[user.id] = user;
      });

      for (var i=0; i<data.topic_list.topics.length; i++) {
        var topicInfo = data.topic_list.topics[i],
            topic = {};

        if (topics.length === 5) break;
        if (topicInfo.pinned) continue;

        topic.title = topicInfo.title;
        topic.url = "http://forums.hummingbird.me/t/" + topicInfo.slug + "/" + topicInfo.id;
        if (topicInfo.last_read_post_number) {
          topic.url += topicInfo.last_read_post_number;
        }
        topic.postCount = topicInfo.highest_post_number;
        topic.lastPostTime = topicInfo.last_posted_at;

        topic.users = [];
        topicInfo.posters.forEach(function(poster) {
          var user = users[poster.user_id];
          topic.users.push({
            name: user.username,
            avatar: user.avatar_template.replace(/\.[a-zA-Z]+\?/, '.jpg?').replace("{size}", "small"),
            url: "http://forums.hummingbird.me/users/" + user.username + "/activity"
          });
        });

        topics.push(topic);
      }

      self.set('topics', topics);

    }, function() {
      console.log("Could not load " + self.get('apiCall'));
    });
  }.on('init')
});
<commit_msg>Forgot a "/".<commit_after>Hummingbird.CommunityWidgetComponent = Ember.Component.extend({
  classNames: ["community-widget-panel"],
  topics: [],

  // Number of loading fake posts to show.
  loadingSeq: [1, 2, 3, 4, 5],

  loadPosts: function() {
    var self = this;

    ic.ajax({
      url: this.get('apiCall'),
      type: "GET",
      xhrFields: {
        withCredentials: true
      }
    }).then(function(data) {

      var users = {},
          topics = [];

      data.users.forEach(function(user) {
        users[user.id] = user;
      });

      for (var i=0; i<data.topic_list.topics.length; i++) {
        var topicInfo = data.topic_list.topics[i],
            topic = {};

        if (topics.length === 5) break;
        if (topicInfo.pinned) continue;

        topic.title = topicInfo.title;
        topic.url = "http://forums.hummingbird.me/t/" + topicInfo.slug + "/" + topicInfo.id;
        if (topicInfo.last_read_post_number) {
          topic.url += "/" + topicInfo.last_read_post_number;
        }
        topic.postCount = topicInfo.highest_post_number;
        topic.lastPostTime = topicInfo.last_posted_at;

        topic.users = [];
        topicInfo.posters.forEach(function(poster) {
          var user = users[poster.user_id];
          topic.users.push({
            name: user.username,
            avatar: user.avatar_template.replace(/\.[a-zA-Z]+\?/, '.jpg?').replace("{size}", "small"),
            url: "http://forums.hummingbird.me/users/" + user.username + "/activity"
          });
        });

        topics.push(topic);
      }

      self.set('topics', topics);

    }, function() {
      console.log("Could not load " + self.get('apiCall'));
    });
  }.on('init')
});
<|endoftext|>


<commit_before>/* 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/. */

load('lib/WindowManager');
load('lib/prefs');

var myPrefs = prefs.createStore('extensions.scrollbar-like-scroller@piro.sakura.ne.jp.');
myPrefs.define('debug',          false);
myPrefs.define('areaSizeRight',  64, 'areaSize.right');
myPrefs.define('areaSizeBottom', 64, 'areaSize.buttom');
myPrefs.define('startThreshold', 12);
myPrefs.define('startDelay',     150);
myPrefs.define('offsetX',        '0.05', 'offset.x');
myPrefs.define('offsetMinX',     64,    'offset.minX');
myPrefs.define('offsetY',        '0.05', 'offset.y');
myPrefs.define('offsetMinY',     64,    'offset.minY');
myPrefs.define('scrollDelay',    50);
myPrefs.define('thumbEnabled',   true, 'thumb.enabled');
myPrefs.define('thumbExpandedArea', 16, 'thumb.expandedArea');
myPrefs.define('thumbMinWidth',  80,   'thumb.minWidth');
myPrefs.define('thumbMinHeight', 80,   'thumb.minHeight');

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

const TYPE_BROWSER = 'navigator:browser';

function parseTouchEvent(aEvent) {
	var chrome = WindowManager.getWindow(TYPE_BROWSER);
	var content = aEvent.originalTarget;
	content = content.defaultView || content.ownerDocument.defaultView;
	var touch = aEvent.touches[0];
	if (!touch)
		throw new Error('there is no touch!');
	var viewport = chrome.BrowserApp.selectedTab.getViewport();
	var [pageWidth, pageHeight] = chrome.BrowserApp.selectedTab.getPageSize(content.document, viewport.width, viewport.height);
	var parsed = {
		zoom    : viewport.zoom,
		width   : viewport.width,
		height  : viewport.height,
		pageWidth  : viewport.pageRight,
		pageHeight : viewport.pageBottom,
		scrollX    : viewport.x,
		scrollY    : viewport.y,
		scrollMaxX : (viewport.pageRight - viewport.width) / viewport.zoom,
		scrollMaxY : (viewport.pageBottom - viewport.height) / viewport.zoom,
		eventX  : touch.clientX * viewport.zoom,
		eventY  : touch.clientY * viewport.zoom
	};
	var maxXArea = viewport.width * 0.5;
	parsed.rightArea = Math.min(maxXArea, myPrefs.areaSizeRight);
	var maxYArea = viewport.width * 0.5;
	parsed.bottomArea = Math.min(maxYArea, myPrefs.areaSizeBottom);
	parsed.rightEdgeTouching = parsed.width - parsed.eventX <= parsed.rightArea;
	parsed.bottomEdgeTouching = parsed.height - parsed.eventY <= parsed.bottomArea;

	if (myPrefs.thumbEnabled) {
		let expandedArea = myPrefs.thumbExpandedArea * viewport.zoom;

		let thumbStartY = parsed.scrollY - expandedArea;
		let thumbEndY = parsed.scrollY + parsed.height + expandedArea;
		let thumbHeight = (thumbEndY - thumbStartY) / parsed.pageHeight * parsed.height;
		let minHeight = myPrefs.thumbMinHeight;
		if (thumbHeight < minHeight) {
			let expand = (minHeight - thumbHeight) / parsed.height * parsed.pageHeight / 2;
			thumbStartY -= expand;
			thumbEndY += expand;
		}
		parsed.thumbStartY = thumbStartY / parsed.pageHeight * parsed.height;
		parsed.thumbEndY = thumbEndY / parsed.pageHeight * parsed.height;
		parsed.thumbHeight = parsed.thumbEndY - parsed.thumbStartY;

		let thumbStartX = parsed.scrollX - expandedArea;
		let thumbEndX = parsed.scrollX + parsed.width + expandedArea;
		let thumbWidth = (thumbEndX - thumbStartX) / parsed.pageWidth * parsed.width;
		let minWidth = myPrefs.thumbMinWidth;
		if (thumbWidth < minWidth) {
			let expand = (minWidth - thumbWidth) / parsed.width * parsed.pageWidth / 2;
			thumbStartX -= expand;
			thumbEndX += expand;
		}
		parsed.thumbStartX = thumbStartX / parsed.pageWidth * parsed.width;
		parsed.thumbEndX = thumbEndX / parsed.pageWidth * parsed.width;
		parsed.thumbWidth = parsed.thumbEndX - parsed.thumbStartX;
	}

	if (myPrefs.debug && aEvent.type != 'touchmove')
		chrome.NativeWindow.toast.show(aEvent.type+'\n'+
			JSON.stringify(parsed).replace(/,/g,',\n'), 'short');

	return [chrome, content, parsed];
}

var scrollXAxis = false;
var scrollYAxis = false;
function updateScrollPosition(aWindow, aParsedTouch) {
	var x = aWindow.scrollX;
	var y = aWindow.scrollY;
	if (scrollXAxis) {
		let maxX = aParsedTouch.scrollMaxX;
		let offset = Math.max(aParsedTouch.width * parseFloat(myPrefs.offsetX), myPrefs.offsetMinX);
		let position = calculateThumbPositionPercentage(offset, aParsedTouch.width, aParsedTouch.eventX);
		x = maxX * position;
	}
	if (scrollYAxis) {
		let maxY = aParsedTouch.scrollMaxY;
		let offset = Math.max(aParsedTouch.height * parseFloat(myPrefs.offsetY), myPrefs.offsetMinY);
		let position = calculateThumbPositionPercentage(offset, aParsedTouch.height, aParsedTouch.eventY);
		y = maxY * position;
	}
	aWindow.scrollTo(x, y);
}

function calculateThumbPositionPercentage(aOffset, aSize, aPosition) {
	let scrollbarSize = aSize - (aOffset * 2);
	let percentage = (aPosition - aOffset) / scrollbarSize;
	return Math.min(Math.max(0, percentage), 1)
}

var STATE_NONE     = 0;
var STATE_READY    = 1;
var STATE_HANDLING = 2;
var state = STATE_NONE;
var startTime = -1;
var startX = -1;
var startY = -1;

function handleTouchStart(aEvent) {
	if (aEvent.touches.length != 1)
		return;
	var [chrome, content, parsed] = parseTouchEvent(aEvent);
	if (!parsed.rightEdgeTouching && !parsed.bottomEdgeTouching)
		return;
	scrollXAxis = false;
	scrollYAxis = false;
	if (myPrefs.thumbEnabled) {
		if (parsed.rightEdgeTouching)
			scrollYAxis = parsed.eventY >= parsed.thumbStartY && parsed.eventY <= parsed.thumbEndY;
		if (parsed.bottomEdgeTouching)
			scrollXAxis = parsed.eventX >= parsed.thumbStartX && parsed.eventX <= parsed.thumbEndX;
		if (!scrollXAxis && !scrollYAxis)
			return;
	}
	state = STATE_READY;
	startX = parsed.eventX;
	startY = parsed.eventY;
	startTime = Date.now();
}

function handleTouchEnd(aEvent) {
	if (state == STATE_NONE)
		return;
	if (aEvent.touches.length != 1) {
		state = STATE_NONE;
		return;
	}
	state = STATE_NONE;
	startTime = -1;
	startX = -1;
	startY = -1;
	scrollXAxis = false;
	scrollYAxis = false;
	var [chrome, content, parsed] = parseTouchEvent(aEvent);
	let (thumb = thumbsXAxis.get(content)) {
		if (thumb) {
			thumb.parentNode.removeChild(thumb);
			thumbsXAxis.set(content, undefined);
		}
	}
	let (thumb = thumbsYAxis.get(content)) {
		if (thumb) {
			thumb.parentNode.removeChild(thumb);
			thumbsYAxis.set(content, undefined);
		}
	}
	updateScrollPosition(content, parsed);
	aEvent.stopPropagation();
	aEvent.preventDefault();
	chrome.sendMessageToJava({ gecko: { type : 'Panning:Override' } });
}

function handleTouchMove(aEvent) {
	if (state == STATE_NONE)
		return;
	if (aEvent.touches.length != 1) {
		state = STATE_NONE;
		return;
	}
	var [chrome, content, parsed] = parseTouchEvent(aEvent);
	if (state == STATE_READY) {
		if (Date.now() - startTime < myPrefs.startDelay)
			return;
		let threshold = myPrefs.startThreshold;
		scrollXAxis = scrollXAxis && parsed.bottomEdgeTouching && Math.abs(parsed.eventX - startX) >= threshold;
		scrollYAxis = scrollYAxis && parsed.rightEdgeTouching && Math.abs(parsed.eventY - startY) >= threshold;
		if (!scrollXAxis && !scrollYAxis)
			return;
		if (myPrefs.debug)
			chrome.NativeWindow.toast.show('start scrollbar like behavior\n'+
				JSON.stringify(parsed).replace(/,/g,',\n'), 'short');
		state = STATE_HANDLING;
	}
	if (scrollXAxis) {
		let thumb = thumbsXAxis.get(content);
		if (!thumb) {
			thumb = createThumb(content);
			thumbsXAxis.set(content, thumb);
		}
		let style = thumb.style;
		style.minHeight = (parsed.bottomArea / parsed.zoom) + 'px';
		style.minWidth = (parsed.thumbWidth / parsed.zoom) + 'px';
		style.bottom = 0;
		style.display = 'block';
		style.left = (parsed.thumbStartX / parsed.zoom) + 'px';
	}
	if (scrollYAxis) {
		let thumb = thumbsYAxis.get(content);
		if (!thumb) {
			thumb = createThumb(content);
			thumbsYAxis.set(content, thumb);
		}
		let style = thumb.style;
		style.minWidth = (parsed.rightArea / parsed.zoom) + 'px';
		style.minHeight = (parsed.thumbHeight / parsed.zoom) + 'px';
		style.display = 'block';
		style.right = 0;
		style.top = (parsed.thumbStartY / parsed.zoom) + 'px';
	}
	updateScrollPosition(content, parsed);
	aEvent.stopPropagation();
	aEvent.preventDefault();
	chrome.sendMessageToJava({ gecko: { type : 'Panning:Override' } });
}

var thumbsXAxis = new WeakMap();
var thumbsYAxis = new WeakMap();

function handleWindow(aWindow)
{
	var doc = aWindow.document;
	if (doc.documentElement.getAttribute('windowtype') != TYPE_BROWSER)
		return;

	aWindow.addEventListener('touchstart', handleTouchStart, true);
	aWindow.addEventListener('touchend', handleTouchEnd, true);
	aWindow.addEventListener('touchmove', handleTouchMove, true);
}

function createThumb(aWindow) {
	var thumb = aWindow.document.createElementNS('http://www.w3.org/1999/xhtml', 'div');
	aWindow.document.documentElement.appendChild(thumb);
	var style = thumb.style;
	style.display = 'none';
	style.zIndex = 65000;
	style.background = 'red';
	style.border = '2px solid red';
	style.position = 'fixed';
	style.MozTransition = 'top 0.2s linier, left 0.2s linier, right 0.2s linier, bottom 0.2s linier, min-width 0.2s ease, min-height 0.2s ease';
	style.margin = 'auto';
	return thumb;
}

WindowManager.getWindows(TYPE_BROWSER).forEach(handleWindow);
WindowManager.addHandler(handleWindow);

function shutdown()
{
	WindowManager.getWindows(TYPE_BROWSER).forEach(function(aWindow) {
		aWindow.removeEventListener('touchstart', handleTouchStart, true);
		aWindow.removeEventListener('touchend', handleTouchEnd, true);
		aWindow.removeEventListener('touchmove', handleTouchMove, true);
	});

	thumbsXAxis = undefined;
	thumbsYAxis = undefined;
	WindowManager = undefined;
	myPrefs.destroy();
	myPrefs = undefined;
	prefs = undefined;
	config = undefined;
}
<commit_msg>Handle touchend event correctly<commit_after>/* 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/. */

load('lib/WindowManager');
load('lib/prefs');

var myPrefs = prefs.createStore('extensions.scrollbar-like-scroller@piro.sakura.ne.jp.');
myPrefs.define('debug',          false);
myPrefs.define('areaSizeRight',  64, 'areaSize.right');
myPrefs.define('areaSizeBottom', 64, 'areaSize.buttom');
myPrefs.define('startThreshold', 12);
myPrefs.define('startDelay',     150);
myPrefs.define('offsetX',        '0.05', 'offset.x');
myPrefs.define('offsetMinX',     64,    'offset.minX');
myPrefs.define('offsetY',        '0.05', 'offset.y');
myPrefs.define('offsetMinY',     64,    'offset.minY');
myPrefs.define('scrollDelay',    50);
myPrefs.define('thumbEnabled',   true, 'thumb.enabled');
myPrefs.define('thumbExpandedArea', 16, 'thumb.expandedArea');
myPrefs.define('thumbMinWidth',  80,   'thumb.minWidth');
myPrefs.define('thumbMinHeight', 80,   'thumb.minHeight');

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

const TYPE_BROWSER = 'navigator:browser';

function parseTouchEvent(aEvent) {
	var chrome = WindowManager.getWindow(TYPE_BROWSER);
	var content = aEvent.originalTarget;
	content = content.defaultView || content.ownerDocument.defaultView;
	var touch = aEvent.touches[0];
	if (!touch)
		throw new Error('there is no touch!');
	var viewport = chrome.BrowserApp.selectedTab.getViewport();
	var [pageWidth, pageHeight] = chrome.BrowserApp.selectedTab.getPageSize(content.document, viewport.width, viewport.height);
	var parsed = {
		zoom    : viewport.zoom,
		width   : viewport.width,
		height  : viewport.height,
		pageWidth  : viewport.pageRight,
		pageHeight : viewport.pageBottom,
		scrollX    : viewport.x,
		scrollY    : viewport.y,
		scrollMaxX : (viewport.pageRight - viewport.width) / viewport.zoom,
		scrollMaxY : (viewport.pageBottom - viewport.height) / viewport.zoom,
		eventX  : touch.clientX * viewport.zoom,
		eventY  : touch.clientY * viewport.zoom
	};
	var maxXArea = viewport.width * 0.5;
	parsed.rightArea = Math.min(maxXArea, myPrefs.areaSizeRight);
	var maxYArea = viewport.width * 0.5;
	parsed.bottomArea = Math.min(maxYArea, myPrefs.areaSizeBottom);
	parsed.rightEdgeTouching = parsed.width - parsed.eventX <= parsed.rightArea;
	parsed.bottomEdgeTouching = parsed.height - parsed.eventY <= parsed.bottomArea;

	if (myPrefs.thumbEnabled) {
		let expandedArea = myPrefs.thumbExpandedArea * viewport.zoom;

		let thumbStartY = parsed.scrollY - expandedArea;
		let thumbEndY = parsed.scrollY + parsed.height + expandedArea;
		let thumbHeight = (thumbEndY - thumbStartY) / parsed.pageHeight * parsed.height;
		let minHeight = myPrefs.thumbMinHeight;
		if (thumbHeight < minHeight) {
			let expand = (minHeight - thumbHeight) / parsed.height * parsed.pageHeight / 2;
			thumbStartY -= expand;
			thumbEndY += expand;
		}
		parsed.thumbStartY = thumbStartY / parsed.pageHeight * parsed.height;
		parsed.thumbEndY = thumbEndY / parsed.pageHeight * parsed.height;
		parsed.thumbHeight = parsed.thumbEndY - parsed.thumbStartY;

		let thumbStartX = parsed.scrollX - expandedArea;
		let thumbEndX = parsed.scrollX + parsed.width + expandedArea;
		let thumbWidth = (thumbEndX - thumbStartX) / parsed.pageWidth * parsed.width;
		let minWidth = myPrefs.thumbMinWidth;
		if (thumbWidth < minWidth) {
			let expand = (minWidth - thumbWidth) / parsed.width * parsed.pageWidth / 2;
			thumbStartX -= expand;
			thumbEndX += expand;
		}
		parsed.thumbStartX = thumbStartX / parsed.pageWidth * parsed.width;
		parsed.thumbEndX = thumbEndX / parsed.pageWidth * parsed.width;
		parsed.thumbWidth = parsed.thumbEndX - parsed.thumbStartX;
	}

	if (myPrefs.debug && aEvent.type != 'touchmove')
		chrome.NativeWindow.toast.show(aEvent.type+'\n'+
			JSON.stringify(parsed).replace(/,/g,',\n'), 'short');

	return [chrome, content, parsed];
}

var scrollXAxis = false;
var scrollYAxis = false;
function updateScrollPosition(aWindow, aParsedTouch) {
	var x = aWindow.scrollX;
	var y = aWindow.scrollY;
	if (scrollXAxis) {
		let maxX = aParsedTouch.scrollMaxX;
		let offset = Math.max(aParsedTouch.width * parseFloat(myPrefs.offsetX), myPrefs.offsetMinX);
		let position = calculateThumbPositionPercentage(offset, aParsedTouch.width, aParsedTouch.eventX);
		x = maxX * position;
	}
	if (scrollYAxis) {
		let maxY = aParsedTouch.scrollMaxY;
		let offset = Math.max(aParsedTouch.height * parseFloat(myPrefs.offsetY), myPrefs.offsetMinY);
		let position = calculateThumbPositionPercentage(offset, aParsedTouch.height, aParsedTouch.eventY);
		y = maxY * position;
	}
	aWindow.scrollTo(x, y);
}

function calculateThumbPositionPercentage(aOffset, aSize, aPosition) {
	let scrollbarSize = aSize - (aOffset * 2);
	let percentage = (aPosition - aOffset) / scrollbarSize;
	return Math.min(Math.max(0, percentage), 1)
}

var STATE_NONE     = 0;
var STATE_READY    = 1;
var STATE_HANDLING = 2;
var state = STATE_NONE;
var startTime = -1;
var startX = -1;
var startY = -1;

function handleTouchStart(aEvent) {
	if (aEvent.touches.length != 1)
		return;
	var [chrome, content, parsed] = parseTouchEvent(aEvent);
	if (!parsed.rightEdgeTouching && !parsed.bottomEdgeTouching)
		return;
	scrollXAxis = false;
	scrollYAxis = false;
	if (myPrefs.thumbEnabled) {
		if (parsed.rightEdgeTouching)
			scrollYAxis = parsed.eventY >= parsed.thumbStartY && parsed.eventY <= parsed.thumbEndY;
		if (parsed.bottomEdgeTouching)
			scrollXAxis = parsed.eventX >= parsed.thumbStartX && parsed.eventX <= parsed.thumbEndX;
		if (!scrollXAxis && !scrollYAxis)
			return;
	}
	state = STATE_READY;
	startX = parsed.eventX;
	startY = parsed.eventY;
	startTime = Date.now();
}

function handleTouchEnd(aEvent) {
	if (state == STATE_NONE)
		return;
	state = STATE_NONE;
	startTime = -1;
	startX = -1;
	startY = -1;
	scrollXAxis = false;
	scrollYAxis = false;
	var content = aEvent.originalTarget;
	content = content.defaultView || content.ownerDocument.defaultView;
	let (thumb = thumbsXAxis.get(content)) {
		if (thumb) {
			thumb.parentNode.removeChild(thumb);
			thumbsXAxis.set(content, undefined);
		}
	}
	let (thumb = thumbsYAxis.get(content)) {
		if (thumb) {
			thumb.parentNode.removeChild(thumb);
			thumbsYAxis.set(content, undefined);
		}
	}
	aEvent.stopPropagation();
	aEvent.preventDefault();
	chrome.sendMessageToJava({ gecko: { type : 'Panning:Override' } });
}

function handleTouchMove(aEvent) {
	if (state == STATE_NONE)
		return;
	if (aEvent.touches.length != 1) {
		state = STATE_NONE;
		return;
	}
	var [chrome, content, parsed] = parseTouchEvent(aEvent);
	if (state == STATE_READY) {
		if (Date.now() - startTime < myPrefs.startDelay)
			return;
		let threshold = myPrefs.startThreshold;
		scrollXAxis = scrollXAxis && parsed.bottomEdgeTouching && Math.abs(parsed.eventX - startX) >= threshold;
		scrollYAxis = scrollYAxis && parsed.rightEdgeTouching && Math.abs(parsed.eventY - startY) >= threshold;
		if (!scrollXAxis && !scrollYAxis)
			return;
		if (myPrefs.debug)
			chrome.NativeWindow.toast.show('start scrollbar like behavior\n'+
				JSON.stringify(parsed).replace(/,/g,',\n'), 'short');
		state = STATE_HANDLING;
	}
	if (scrollXAxis) {
		let thumb = thumbsXAxis.get(content);
		if (!thumb) {
			thumb = createThumb(content);
			thumbsXAxis.set(content, thumb);
		}
		let style = thumb.style;
		style.minHeight = (parsed.bottomArea / parsed.zoom) + 'px';
		style.minWidth = (parsed.thumbWidth / parsed.zoom) + 'px';
		style.bottom = 0;
		style.display = 'block';
		style.left = (parsed.thumbStartX / parsed.zoom) + 'px';
	}
	if (scrollYAxis) {
		let thumb = thumbsYAxis.get(content);
		if (!thumb) {
			thumb = createThumb(content);
			thumbsYAxis.set(content, thumb);
		}
		let style = thumb.style;
		style.minWidth = (parsed.rightArea / parsed.zoom) + 'px';
		style.minHeight = (parsed.thumbHeight / parsed.zoom) + 'px';
		style.display = 'block';
		style.right = 0;
		style.top = (parsed.thumbStartY / parsed.zoom) + 'px';
	}
	updateScrollPosition(content, parsed);
	aEvent.stopPropagation();
	aEvent.preventDefault();
	chrome.sendMessageToJava({ gecko: { type : 'Panning:Override' } });
}

var thumbsXAxis = new WeakMap();
var thumbsYAxis = new WeakMap();

function handleWindow(aWindow)
{
	var doc = aWindow.document;
	if (doc.documentElement.getAttribute('windowtype') != TYPE_BROWSER)
		return;

	aWindow.addEventListener('touchstart', handleTouchStart, true);
	aWindow.addEventListener('touchend', handleTouchEnd, true);
	aWindow.addEventListener('touchmove', handleTouchMove, true);
}

function createThumb(aWindow) {
	var thumb = aWindow.document.createElementNS('http://www.w3.org/1999/xhtml', 'div');
	aWindow.document.documentElement.appendChild(thumb);
	var style = thumb.style;
	style.display = 'none';
	style.zIndex = 65000;
	style.background = 'red';
	style.border = '2px solid red';
	style.position = 'fixed';
	style.MozTransition = 'top 0.2s linier, left 0.2s linier, right 0.2s linier, bottom 0.2s linier, min-width 0.2s ease, min-height 0.2s ease';
	style.margin = 'auto';
	return thumb;
}

WindowManager.getWindows(TYPE_BROWSER).forEach(handleWindow);
WindowManager.addHandler(handleWindow);

function shutdown()
{
	WindowManager.getWindows(TYPE_BROWSER).forEach(function(aWindow) {
		aWindow.removeEventListener('touchstart', handleTouchStart, true);
		aWindow.removeEventListener('touchend', handleTouchEnd, true);
		aWindow.removeEventListener('touchmove', handleTouchMove, true);
	});

	thumbsXAxis = undefined;
	thumbsYAxis = undefined;
	WindowManager = undefined;
	myPrefs.destroy();
	myPrefs = undefined;
	prefs = undefined;
	config = undefined;
}
<|endoftext|>


<commit_before>var request = require('request'),
    _ = require('underscore'),
    passError = require('passerror'),
    httpErrors = require('httperrors');

require('bufferjs');

module.exports = function () {
    return function http(assetConfig, fromUrl, cb) {
        var url = assetConfig.url;
        assetConfig.rawSrcProxy = function (cb) {
            var matchUrl = url.match(/^([^#]*)((?:#.*)?)$/),
                urlWithoutFragmentIdentifier = matchUrl[1],
                fragmentIdentifier = matchUrl[2],
                requestOptions = http.requestOptions || {},
                numRetries = requestOptions.numRetries || 0;

            (function doRequest() {
                // Hack: Allows you to control the request options by setting the 'requestOptions' of the returned function to
                // an object.
                var req = request(_.defaults({
                    uri: urlWithoutFragmentIdentifier,
                    onResponse: true
                }, requestOptions), function (err, response) {
                    if (err) {
                        if (typeof numRetries === 'number' && numRetries > 0) {
                            numRetries -= 1;
                            return doRequest();
                        } else {
                            return cb(err);
                        }
                    }
                    if (response.statusCode >= 400) {
                        return cb(new (httpErrors[response.statusCode] || httpErrors.BadGateway)({
                            message: 'Got ' + response.statusCode + ' error retrieving ' + urlWithoutFragmentIdentifier,
                            url: urlWithoutFragmentIdentifier
                        }));
                    }
                    var metadata = {
                            url: req.uri.href + fragmentIdentifier // Might be different from the originally requested url if redirects were followed
                        },
                        buffers = [];
                    if ('content-type' in response.headers) {
                        var matchContentType = response.headers['content-type'].match(/^\s*([\w\-\+\.]+\/[\w\-\+\.]+).*(?:;\s*charset\s*=\s*([\'\"]|)\s*([\w\-]+)\s*\2\s*)?/i);
                        if (matchContentType) {
                            metadata.contentType = matchContentType[1].toLowerCase();
                            if (matchContentType[3]) {
                                metadata.encoding = matchContentType[3].toLowerCase();
                            }
                        } else {
                            console.warn("resolvers.http: Couldn't parse Content-Type " + response.headers['content-type'] + " while fetching " + url);
                        }
                    }
                    if ('etag' in response.headers) {
                        metadata.etag = response.headers.etag;
                    }
                    if ('cache-control' in response.headers) {
                        metadata.cacheControl = response.headers['cache-control'];
                    }
                    ['date', 'last-modified'].forEach(function (headerName) {
                        if (headerName in response.headers) {
                            metadata[headerName.replace(/-([a-z])/, function ($0, ch) {
                                return ch.toUpperCase();
                            })] = new Date(response.headers[headerName]);
                        }
                    });
                    response.on('data', function (buffer) {
                        buffers.push(buffer);
                    }).on('end', function () {
                        cb(null, Buffer.concat(buffers), metadata);
                    }).on('error', cb);
                });
            }());
        };
        assetConfig.isResolved = true;
        process.nextTick(function () {
            cb(null, assetConfig);
        });
    };
};
<commit_msg>resolvers.http: Fixed parsing of charset=... in the Content-Type header.<commit_after>var request = require('request'),
    _ = require('underscore'),
    passError = require('passerror'),
    httpErrors = require('httperrors');

require('bufferjs');

module.exports = function () {
    return function http(assetConfig, fromUrl, cb) {
        var url = assetConfig.url;
        assetConfig.rawSrcProxy = function (cb) {
            var matchUrl = url.match(/^([^#]*)((?:#.*)?)$/),
                urlWithoutFragmentIdentifier = matchUrl[1],
                fragmentIdentifier = matchUrl[2],
                requestOptions = http.requestOptions || {},
                numRetries = requestOptions.numRetries || 0;

            (function doRequest() {
                // Hack: Allows you to control the request options by setting the 'requestOptions' of the returned function to
                // an object.
                var req = request(_.defaults({
                    uri: urlWithoutFragmentIdentifier,
                    onResponse: true
                }, requestOptions), function (err, response) {
                    if (err) {
                        if (typeof numRetries === 'number' && numRetries > 0) {
                            numRetries -= 1;
                            return doRequest();
                        } else {
                            return cb(err);
                        }
                    }
                    if (response.statusCode >= 400) {
                        return cb(new (httpErrors[response.statusCode] || httpErrors.BadGateway)({
                            message: 'Got ' + response.statusCode + ' error retrieving ' + urlWithoutFragmentIdentifier,
                            url: urlWithoutFragmentIdentifier
                        }));
                    }
                    var metadata = {
                            url: req.uri.href + fragmentIdentifier // Might be different from the originally requested url if redirects were followed
                        },
                        buffers = [],
                        contentType = response.headers['content-type'];
                    if (contentType) {
                        var matchContentType = contentType.match(/^\s*([\w\-\+\.]+\/[\w\-\+\.]+)(?:\s|;|$)/i);
                        if (matchContentType) {
                            metadata.contentType = matchContentType[1].toLowerCase();
                            var matchCharset = contentType.match(/;\s*charset\s*=\s*(['"]|)\s*([\w\-]+)\s*\1(?:\s|;|$)/i);
                            if (matchCharset) {
                                metadata.encoding = matchCharset[2].toLowerCase();
                            }
                        } else {
                            console.warn("resolvers.http: Couldn't parse Content-Type " + response.headers['content-type'] + " while fetching " + url);
                        }
                    }
                    if ('etag' in response.headers) {
                        metadata.etag = response.headers.etag;
                    }
                    if ('cache-control' in response.headers) {
                        metadata.cacheControl = response.headers['cache-control'];
                    }
                    ['date', 'last-modified'].forEach(function (headerName) {
                        if (headerName in response.headers) {
                            metadata[headerName.replace(/-([a-z])/, function ($0, ch) {
                                return ch.toUpperCase();
                            })] = new Date(response.headers[headerName]);
                        }
                    });
                    response.on('data', function (buffer) {
                        buffers.push(buffer);
                    }).on('end', function () {
                        cb(null, Buffer.concat(buffers), metadata);
                    }).on('error', cb);
                });
            }());
        };
        assetConfig.isResolved = true;
        process.nextTick(function () {
            cb(null, assetConfig);
        });
    };
};
<|endoftext|>


<commit_before>"use strict";

/*
Controller - Observer
*/

function TransactionsController(model, undefined){

	var transactionsModel = model;

	var transactionsController = {
		update: function(viewEvent) {
			if(viewEvent.name === 'c.transactions.export') {

			}
		}
	};

	return transactionsController;

}
<commit_msg>[ci skip] transactions controller refactored<commit_after>"use strict";

(function(aScope, undefined) {
	var model = aScope.transactionsModel;
	var view = aScope.transactionsView;

	model.subscribe(view);
	aScope.primaryModel = model;
	model.updateData();

})($EX);<|endoftext|>


<commit_before>'use strict'
var Operator = require('./')
var objectCache = require('./cache/object')
exports.inject = [
  require('./val')
]

exports.properties = {
  $: {
    val: new Operator({
      key: '$',
      define: {
        set: function (pattern) {
          if (typeof pattern === 'string') {
            var keys = pattern.split('.')
            var value = true
            for (var i = keys.length - 1; i >= 0; i--) {
              pattern = {}
              pattern[keys[i]] = value
              value = pattern
            }
          }
          console.log('HAHAHA',this)
          this._parent.subscribe(pattern, function (data, event) {
            if(!data){
              console.warn('something fishy going on, why am I fired? Do data.')
              return
            }

            // if (this._$._output !== data.origin) {
              // console.log(data.origin.path,'------>',this.path)
              // console.log('!!!!',this._$._context)
              // // if (!this.hasOwnProperty('_$') && this._$._context) {
              // if (this._$._context) {
              //   this._$.resolveContext({}, event)
              // }

              // var path = []
              // var parent = this
              // while(parent){
              //   path.unshift(parent.key)
              //   parent = parent.parent
              // }
            if (!this.hasOwnProperty('_$') && this._$._context) {
              console.error('!')
              this._$.resolveContext({}, event)
            }

              console.log(this.path,'<--',data.origin.path)
            this._$.output = data.origin
            this.emit('data')
            // }
          }).run()
        }
      },
      operator: function (val, operator, origin) {
        var parsed = operator.parseValue(val, origin)
        var cached = objectCache.call(this, parsed)
        if (cached) {
          // events etc to these sets!
          cached.clear()
          cached.set(parsed)
          return cached
        } else {
          return parsed
        }
      }
    }),
    override: '_$'
  }
}
<commit_msg>removed logs<commit_after>'use strict'
var Operator = require('./')
var objectCache = require('./cache/object')
exports.inject = [
  require('./val')
]

exports.properties = {
  $: {
    val: new Operator({
      key: '$',
      define: {
        set: function (pattern) {
          if (typeof pattern === 'string') {
            var keys = pattern.split('.')
            var value = true
            for (var i = keys.length - 1; i >= 0; i--) {
              pattern = {}
              pattern[keys[i]] = value
              value = pattern
            }
          }
          console.log('HAHAHA',this)
          this._parent.subscribe(pattern, function (data, event) {
            if(!data){
              console.warn('something fishy going on, why am I fired? Do data.')
              return
            }

            // if (this._$._output !== data.origin) {
              // console.log(data.origin.path,'------>',this.path)
              // console.log('!!!!',this._$._context)
              // // if (!this.hasOwnProperty('_$') && this._$._context) {
              // if (this._$._context) {
              //   this._$.resolveContext({}, event)
              // }

              // var path = []
              // var parent = this
              // while(parent){
              //   path.unshift(parent.key)
              //   parent = parent.parent
              // }
            if (!this.hasOwnProperty('_$') && this._$._context) {
              // console.error('!')
              this._$.resolveContext({}, event)
            }

            console.log(this.path,'<--',data.origin.path)

            this._$.output = data.origin
            this._$.emit('data',event)
            // }
          }).run()
        }
      },
      operator: function (val, operator, origin) {
        var parsed = operator.parseValue(val, origin)
        var cached = objectCache.call(this, parsed)
        if (cached) {
          // events etc to these sets!
          cached.clear()
          cached.set(parsed)
          return cached
        } else {
          return parsed
        }
      }
    }),
    override: '_$'
  }
}
<|endoftext|>


<commit_before>// @flow
/**
 * Part of GDL gdl-frontend.
 * Copyright (C) 2017 GDL
 *
 * See LICENSE
 */

import * as React from 'react';
import { I18nProvider, withI18n } from '@lingui/react';
import Head from 'next/head';
import serializeJS from 'serialize-javascript';
import Url from 'domurl';
import type { Context } from '../types';

type Props = {
  language: string,
  catalog: string,
  href: string
};

// Currently next.js doesn't support variables with dynamic imports,
// So for now we have to add each translation specifically
const translations = {
  en: import('../locale/en/messages')
};

/**
 * A HoC that that faciliates our i18n layer
 */
export default (Page: React.ComponentType<any>) => {
  const I18nPage = withI18n()(Page);

  return class PageWithI18n extends React.Component<Props> {
    static async getInitialProps(context: Context) {
      // Evaluate the composed component's getInitialProps()
      let composedInitialProps;
      // Check if it actually is a next page
      if (typeof Page.getInitialProps === 'function') {
        composedInitialProps = await Page.getInitialProps(context);
      }

      // et the 'language' from the request object on the server.
      // In the browser, use the same values that the server serialized.
      const { req } = context;
      // $FlowFixMe: How to handle that we get 'language' on the request object?
      let { language } = req || window.__NEXT_DATA__.props;

      // Fallback to english if the language isn't found in the translations object
      if (!(language in translations)) {
        language = 'en';
      }

      // Load the translation
      // It contains functions, so we use this lib to serialize it
      const catalog = serializeJS(await translations[language]);

      let href;
      if (req != null) {
        // On the server, we build it up based on the request object
        href = `${req.protocol}://${req.headers.host}${req.originalUrl}`;
      } else {
        // In the browser, we simly read the window location
        href = window.location.href;
      }

      return {
        ...composedInitialProps,
        language,
        catalog,
        href
      };
    }

    render() {
      const languages = Object.keys(translations);
      const { language, href, catalog, ...props } = this.props;
      const url = new Url(href);
      delete url.query.hl;
      // Wrap our page with the i18n provider and add alternate links to the other supported languages in the head

      // The translation contains serialized functions. So we need to use eval here :/
      return (
        <I18nProvider
          language={language}
          catalogs={{
            [language]: eval(`(${catalog})`) // eslint-disable-line no-eval
          }}
        >
          <Head>
            <link href={url.toString()} rel="canonical" />
            <link href={url.toString()} hrefLang="x-default" rel="alternate" />
            {languages.map(lang => {
              url.query.hl = lang;
              return (
                <link
                  key={lang}
                  href={url.toString()}
                  hrefLang={lang}
                  rel="alternate"
                />
              );
            })}
          </Head>
          <I18nPage {...props} />
        </I18nProvider>
      );
    }
  };
};
<commit_msg>Remove canonical URL<commit_after>// @flow
/**
 * Part of GDL gdl-frontend.
 * Copyright (C) 2017 GDL
 *
 * See LICENSE
 */

import * as React from 'react';
import { I18nProvider, withI18n } from '@lingui/react';
import Head from 'next/head';
import serializeJS from 'serialize-javascript';
import Url from 'domurl';
import type { Context } from '../types';

type Props = {
  language: string,
  catalog: string,
  href: string
};

// Currently next.js doesn't support variables with dynamic imports,
// So for now we have to add each translation specifically
const translations = {
  en: import('../locale/en/messages')
};

/**
 * A HoC that that faciliates our i18n layer
 */
export default (Page: React.ComponentType<any>) => {
  const I18nPage = withI18n()(Page);

  return class PageWithI18n extends React.Component<Props> {
    static async getInitialProps(context: Context) {
      // Evaluate the composed component's getInitialProps()
      let composedInitialProps;
      // Check if it actually is a next page
      if (typeof Page.getInitialProps === 'function') {
        composedInitialProps = await Page.getInitialProps(context);
      }

      // et the 'language' from the request object on the server.
      // In the browser, use the same values that the server serialized.
      const { req } = context;
      // $FlowFixMe: How to handle that we get 'language' on the request object?
      let { language } = req || window.__NEXT_DATA__.props;

      // Fallback to english if the language isn't found in the translations object
      if (!(language in translations)) {
        language = 'en';
      }

      // Load the translation
      // It contains functions, so we use this lib to serialize it
      const catalog = serializeJS(await translations[language]);

      let href;
      if (req != null) {
        // On the server, we build it up based on the request object
        href = `${req.protocol}://${req.headers.host}${req.originalUrl}`;
      } else {
        // In the browser, we simly read the window location
        href = window.location.href;
      }

      return {
        ...composedInitialProps,
        language,
        catalog,
        href
      };
    }

    render() {
      const languages = Object.keys(translations);
      const { language, href, catalog, ...props } = this.props;
      const url = new Url(href);
      delete url.query.hl;
      // Wrap our page with the i18n provider and add alternate links to the other supported languages in the head

      // The translation contains serialized functions. So we need to use eval here :/
      return (
        <I18nProvider
          language={language}
          catalogs={{
            [language]: eval(`(${catalog})`) // eslint-disable-line no-eval
          }}
        >
          <Head>
            <link href={url.toString()} hrefLang="x-default" rel="alternate" />
            {languages.map(lang => {
              url.query.hl = lang;
              return (
                <link
                  key={lang}
                  href={url.toString()}
                  hrefLang={lang}
                  rel="alternate"
                />
              );
            })}
          </Head>
          <I18nPage {...props} />
        </I18nProvider>
      );
    }
  };
};
<|endoftext|>


<commit_before>var EXPORTED_SYMBOLS = ["sl3uf"];

const Cc = Components.classes, Ci = Components.interfaces;

// Compatibility with before Thunderbird 17. Ugh.
if (!String.prototype.endsWith) {
  String.prototype.endsWith = function(searchString, position) {
      var subjectString = this.toString();
      if (typeof position !== 'number' || !isFinite(position) || Math.floor(position) !== position || position > subjectString.length) {
        position = subjectString.length;
      }
      position -= searchString.length;
      var lastIndex = subjectString.indexOf(searchString, position);
      return lastIndex !== -1 && lastIndex === position;
  };
}

function octal(num) {
    // Takes an integer and returns the value of that integer as if its base-10
    // digits were actually base-8 digits.
    // For example, an input of 10 will return the base-10 value 8.
    // This is a helper function to cope with the fact that versions of
    // Thunderbird prior to 25 didn't support the 0o syntax, and I want to
    // support back to Thunderbird 20. We can just get rid of this and Use 0o
    // syntax directly once we're only supporting Thunderbird 25 or newer.
    return parseInt(String(num), 8);
}

var sl3uf = {
    list: function() {
        // Return sorted array of function names
        var entries = this.directory().directoryEntries;
        var array = [];
        while (entries.hasMoreElements()) {
            var entry = entries.getNext();
            entry.QueryInterface(Ci.nsIFile);
            if (entry.leafName.endsWith(".slj")) {
                array.push(entry.leafName.slice(0, -4));
            }
        }
        return array.sort();
    },

    load: function(name) {
        // Returns [name, help, body]
        var file = this.directory();
        file.append(name + ".slj");
        return this.import(file);
    },

    save: function(name, help, body) {
        // Returns nothing
        var file = this.file(name);
        this.export(name, help, body, file);
    },

    remove: function(name) {
        // Returns nothing
        var file = this.file(name);
        file.remove(false);
    },

    exists: function(name) {
        // Returns true or false
        return this.file(name).exists();
    },

    import: function(file) {
        // "file" is an nsIFile object
        // Returns [name, help, body]
        var data = "";
        var fstream = Cc["@mozilla.org/network/file-input-stream;1"].
            createInstance(Ci.nsIFileInputStream);
        var cstream = Cc["@mozilla.org/intl/converter-input-stream;1"].
            createInstance(Ci.nsIConverterInputStream);
        fstream.init(file, -1, 0, 0);
        cstream.init(fstream, "UTF-8", 0, 0);

        var read = 1;
        while (read != 0) {
            var str = {};
            read = cstream.readString(0xffffffff, str);
            data += str.value;
        }
        cstream.close();
        obj = JSON.parse(data);
        return [obj.name, obj.help, obj.body];
    },

    export: function(name, help, body, file) {
        // "file" is an nsIFile object
        // Returns nothing
        var obj = {
            version: 1,
            name: name,
            help: help,
            body: body
        };
        var data = JSON.stringify(obj);
        var foStream = Cc["@mozilla.org/network/file-output-stream;1"].
            createInstance(Ci.nsIFileOutputStream);
        foStream.init(file, 0x02 | 0x08 | 0x20, octal(666), 0);
        var converter = Cc["@mozilla.org/intl/converter-output-stream;1"].
            createInstance(Ci.nsIConverterOutputStream);
        converter.init(foStream, "UTF-8", 0, 0);
        converter.writeString(data);
        converter.close();
    },

    call: function(name, body, time, args) {
        body = "var next, nextspec, nextargs;" + body +
            "; return([next, nextspec, nextargs]);"
        var f = Function.apply(null, ["specname", "prev", "args", body]);
        var next, nextspec, nextargs;
        [next, nextspec, nextargs] = f("ufunc:" + name, time, args);
        if (! nextspec)
            return next;
        if (! nextargs)
            return [next, nextspec];
        nextargs.splice(0, 0, next, nextspec);
        return nextargs;
    },

    callByName: function(name, time, args) {
        // Returns whatever the function returns
        var help, body;
        [name, help, body] = this.load(name);
        return this.call(name, body, time, args);
    },

    nameOk: function(name) {
        // Returns true or false
        return /^\w+$/.test(name);
    },

    bodyOk: function(body) {
        // Returns true or false
        try {
            return !!Function.apply(null, ["specname", "prev", "args", body]);
        } catch (ex) {
            return false;
        }
    },

    parseArgs: function(argstring) {
        if (argstring === "")
            return [];
        try {
            var args = JSON.parse("[" + argstring + "]");
            this.unparseArgs(args); // throws exception on bad args
            return args;
        }
        catch (ex) {
            return false;
        }
    },

    unparseArgs: function(args) {
        // Convert a list into its string representation, WITHOUT the square
        // braces around the entire list.
        //
        // We stringify the individual elements of the list and then join them
        // because we want spaces after the commas for readability, and JSON.
        // stringify won't do that, and when you try to make it do that by
        // specifying its "space" argument, it inserts newlines as well, which
        // we obviously don't want.
        if (! args.length)
            return "";
        var arglist = [];
        for (var i in args) {
            val = args[i];
            if (val && val.splice)
                arglist.push('[' + this.unparseArgs(val) + ']');
            else if (! (/^(?:number|boolean|string)$/.test(typeof(val)) ||
                        val === null))
                throw new Error("Function arguments can only contain arrays " +
                                "numbers, booleans, strings, and null.");
            arglist.push(JSON.stringify(val));
        }
        return arglist.join(', ');
    },

    directory: function() {
        // Returns an nsIFile for the ufuncs storage directory
        var directoryService =
            Cc["@mozilla.org/file/directory_service;1"].
            getService(Ci.nsIProperties);
        // this is a reference to the profile dir (ProfD) now.
        var localDir = directoryService.get("ProfD", Ci.nsIFile);

        localDir.append("sendlater3");
        if (!localDir.exists() || !localDir.isDirectory()) {
            localDir.create(Ci.nsIFile.DIRECTORY_TYPE, octal(774));
        }

        localDir.append("ufuncs");
        if (!localDir.exists() || !localDir.isDirectory()) {
            localDir.create(Ci.nsIFile.DIRECTORY_TYPE, octal(774));
        }

        return localDir;
    },

    file: function(name) {
        // Returns an nsIFile for a ufunc file within the storage directory
        var file = this.directory();
        file.append(name + ".slj");
        return file;
    }
};
<commit_msg>Fix undeclared variable to make newest Thunderbird happy<commit_after>var EXPORTED_SYMBOLS = ["sl3uf"];

const Cc = Components.classes, Ci = Components.interfaces;

// Compatibility with before Thunderbird 17. Ugh.
if (!String.prototype.endsWith) {
  String.prototype.endsWith = function(searchString, position) {
      var subjectString = this.toString();
      if (typeof position !== 'number' || !isFinite(position) || Math.floor(position) !== position || position > subjectString.length) {
        position = subjectString.length;
      }
      position -= searchString.length;
      var lastIndex = subjectString.indexOf(searchString, position);
      return lastIndex !== -1 && lastIndex === position;
  };
}

function octal(num) {
    // Takes an integer and returns the value of that integer as if its base-10
    // digits were actually base-8 digits.
    // For example, an input of 10 will return the base-10 value 8.
    // This is a helper function to cope with the fact that versions of
    // Thunderbird prior to 25 didn't support the 0o syntax, and I want to
    // support back to Thunderbird 20. We can just get rid of this and Use 0o
    // syntax directly once we're only supporting Thunderbird 25 or newer.
    return parseInt(String(num), 8);
}

var sl3uf = {
    list: function() {
        // Return sorted array of function names
        var entries = this.directory().directoryEntries;
        var array = [];
        while (entries.hasMoreElements()) {
            var entry = entries.getNext();
            entry.QueryInterface(Ci.nsIFile);
            if (entry.leafName.endsWith(".slj")) {
                array.push(entry.leafName.slice(0, -4));
            }
        }
        return array.sort();
    },

    load: function(name) {
        // Returns [name, help, body]
        var file = this.directory();
        file.append(name + ".slj");
        return this.import(file);
    },

    save: function(name, help, body) {
        // Returns nothing
        var file = this.file(name);
        this.export(name, help, body, file);
    },

    remove: function(name) {
        // Returns nothing
        var file = this.file(name);
        file.remove(false);
    },

    exists: function(name) {
        // Returns true or false
        return this.file(name).exists();
    },

    import: function(file) {
        // "file" is an nsIFile object
        // Returns [name, help, body]
        var data = "";
        var fstream = Cc["@mozilla.org/network/file-input-stream;1"].
            createInstance(Ci.nsIFileInputStream);
        var cstream = Cc["@mozilla.org/intl/converter-input-stream;1"].
            createInstance(Ci.nsIConverterInputStream);
        fstream.init(file, -1, 0, 0);
        cstream.init(fstream, "UTF-8", 0, 0);

        var read = 1;
        while (read != 0) {
            var str = {};
            read = cstream.readString(0xffffffff, str);
            data += str.value;
        }
        cstream.close();
        var obj = JSON.parse(data);
        return [obj.name, obj.help, obj.body];
    },

    export: function(name, help, body, file) {
        // "file" is an nsIFile object
        // Returns nothing
        var obj = {
            version: 1,
            name: name,
            help: help,
            body: body
        };
        var data = JSON.stringify(obj);
        var foStream = Cc["@mozilla.org/network/file-output-stream;1"].
            createInstance(Ci.nsIFileOutputStream);
        foStream.init(file, 0x02 | 0x08 | 0x20, octal(666), 0);
        var converter = Cc["@mozilla.org/intl/converter-output-stream;1"].
            createInstance(Ci.nsIConverterOutputStream);
        converter.init(foStream, "UTF-8", 0, 0);
        converter.writeString(data);
        converter.close();
    },

    call: function(name, body, time, args) {
        body = "var next, nextspec, nextargs;" + body +
            "; return([next, nextspec, nextargs]);"
        var f = Function.apply(null, ["specname", "prev", "args", body]);
        var next, nextspec, nextargs;
        [next, nextspec, nextargs] = f("ufunc:" + name, time, args);
        if (! nextspec)
            return next;
        if (! nextargs)
            return [next, nextspec];
        nextargs.splice(0, 0, next, nextspec);
        return nextargs;
    },

    callByName: function(name, time, args) {
        // Returns whatever the function returns
        var help, body;
        [name, help, body] = this.load(name);
        return this.call(name, body, time, args);
    },

    nameOk: function(name) {
        // Returns true or false
        return /^\w+$/.test(name);
    },

    bodyOk: function(body) {
        // Returns true or false
        try {
            return !!Function.apply(null, ["specname", "prev", "args", body]);
        } catch (ex) {
            return false;
        }
    },

    parseArgs: function(argstring) {
        if (argstring === "")
            return [];
        try {
            var args = JSON.parse("[" + argstring + "]");
            this.unparseArgs(args); // throws exception on bad args
            return args;
        }
        catch (ex) {
            return false;
        }
    },

    unparseArgs: function(args) {
        // Convert a list into its string representation, WITHOUT the square
        // braces around the entire list.
        //
        // We stringify the individual elements of the list and then join them
        // because we want spaces after the commas for readability, and JSON.
        // stringify won't do that, and when you try to make it do that by
        // specifying its "space" argument, it inserts newlines as well, which
        // we obviously don't want.
        if (! args.length)
            return "";
        var arglist = [];
        for (var i in args) {
            val = args[i];
            if (val && val.splice)
                arglist.push('[' + this.unparseArgs(val) + ']');
            else if (! (/^(?:number|boolean|string)$/.test(typeof(val)) ||
                        val === null))
                throw new Error("Function arguments can only contain arrays " +
                                "numbers, booleans, strings, and null.");
            arglist.push(JSON.stringify(val));
        }
        return arglist.join(', ');
    },

    directory: function() {
        // Returns an nsIFile for the ufuncs storage directory
        var directoryService =
            Cc["@mozilla.org/file/directory_service;1"].
            getService(Ci.nsIProperties);
        // this is a reference to the profile dir (ProfD) now.
        var localDir = directoryService.get("ProfD", Ci.nsIFile);

        localDir.append("sendlater3");
        if (!localDir.exists() || !localDir.isDirectory()) {
            localDir.create(Ci.nsIFile.DIRECTORY_TYPE, octal(774));
        }

        localDir.append("ufuncs");
        if (!localDir.exists() || !localDir.isDirectory()) {
            localDir.create(Ci.nsIFile.DIRECTORY_TYPE, octal(774));
        }

        return localDir;
    },

    file: function(name) {
        // Returns an nsIFile for a ufunc file within the storage directory
        var file = this.directory();
        file.append(name + ".slj");
        return file;
    }
};
<|endoftext|>


<commit_before>var router = require('express').Router();

var db = require("../models"),
    User = db.User
    Tessel = db.Tessel;

// extracts an API key (if present) from a request
var getAPIKey = function(req) {
  var headerRegex = /^Bearer (\w+)/;

  // check in Authorization header
  if (headerRegex.test(req.headers.authorization)) {
    return req.headers.authorization.match(headerRegex)[1];
  }

  // check URL query string (only with GET requests)
  if (req.method === "GET") {
    if (req.query.api_key !== undefined && /\w+/.test(req.query.api_key)) {
      return req.query.api_key;
    }
  }

  // check POST body
  if (req.method === "POST") {
    if (req.body.api_key !== undefined && /\w+/.test(req.body.api_key)) {
      return req.body.api_key;
    }
  }

  return false;
};

// intercept and return error for all requests that don't contain an API key
router.all("/*", function(req, res, next) {
  var json = {
    code: 400,
    error: "invalid_request",
    error_description: "The API key was not found"
  };

  var key = getAPIKey(req);

  if (key) {
    req.api_key = key;
    next();
  } else {
    res.status = 400;
    res.json(json);
  }
});

// GET /v1/tessels
//
// Lists all tessels belonging to the currently authenticated user
router.get("/tessels", function(req, res) {
  User
    .find({ where: { apiKey: req.api_key }, include: [ Tessel ] })
    .success(function(user) {
      var json = [];

      user.tessels.forEach(function(tessel) {
        json.push({ id: tessel.device_id });
      });

      res.json(json);
    });
});

// GET /v1/tessels/:device_id
//
// Returns data on a specific tessel, if the user has access
router.get("/tessels/:device_id", function(req, res) {
  Tessel.find({
    include: [ User ],
    where: { device_id: req.params.device_id, }
  }).success(function(tessel) {
    var users = tessel.users.filter(function(user) {
      return user.apiKey === req.api_key;
    });

    if (users.length === 0) {
      res.status = 403;
      res.json({
        error: "Permission Denied",
        info: "I didn't recognize that core name or ID"
      });
    } else {
      res.json({ id: tessel.device_id });
    }
  });
});

module.exports = router;
<commit_msg>Add PUT /tessels/:device_id route with file upload<commit_after>var router = require('express').Router();

var fs = require('fs');

var db = require("../models"),
    User = db.User
    Tessel = db.Tessel;

var formidable = require('formidable');

// extracts an API key (if present) from a request
var getAPIKey = function(req) {
  var headerRegex = /^Bearer (\w+)/;

  // check in Authorization header
  if (headerRegex.test(req.headers.authorization)) {
    return req.headers.authorization.match(headerRegex)[1];
  }

  // check URL query string (only with GET requests)
  if (req.method === "GET") {
    if (req.query.api_key !== undefined && /\w+/.test(req.query.api_key)) {
      return req.query.api_key;
    }
  }

  // check POST body
  if (req.method === "POST") {
    if (req.body.api_key !== undefined && /\w+/.test(req.body.api_key)) {
      return req.body.api_key;
    }
  }

  return false;
};

// intercept and return error for all requests that don't contain an API key
router.all("/*", function(req, res, next) {
  var json = {
    code: 400,
    error: "invalid_request",
    error_description: "The API key was not found"
  };

  var key = getAPIKey(req);

  if (key) {
    req.api_key = key;
    next();
  } else {
    res.status = 400;
    res.json(json);
  }
});

// GET /v1/tessels
//
// Lists all tessels belonging to the currently authenticated user
router.get("/tessels", function(req, res) {
  User
    .find({ where: { apiKey: req.api_key }, include: [ Tessel ] })
    .success(function(user) {
      var json = [];

      user.tessels.forEach(function(tessel) {
        json.push({ id: tessel.device_id });
      });

      res.json(json);
    });
});

// GET /v1/tessels/:device_id
//
// Returns data on a specific tessel, if the user has access
router.get("/tessels/:device_id", function(req, res) {
  Tessel.find({
    include: [ User ],
    where: { device_id: req.params.device_id, }
  }).success(function(tessel) {
    var users = tessel.users.filter(function(user) {
      return user.apiKey === req.api_key;
    });

    if (users.length === 0) {
      res.status = 403;
      return res.json({
        error: "Permission Denied",
        info: "I didn't recognize that core name or ID"
      });
    }

    res.json({ id: tessel.device_id });
  });
});

// PUT /v1/tessels/:device_id
//
// Pushes source to the Tessel
router.put("/tessels/:device_id", function(req, res) {
  Tessel.find({
    include: [ User ],
    where: { device_id: req.params.device_id, }
  }).success(function(tessel) {
    var users = tessel.users.filter(function(user) {
      return user.apiKey === req.api_key;
    });

    if (users.length === 0) {
      res.status = 403;
      return res.json({
        error: "Permission Denied",
        info: "I didn't recognize that core name or ID"
      });
    }

    var form = new formidable.IncomingForm();

    form.parse(req, function(err, fields, files) {
      // uploaded file available under files['null'] for pushing to tessel
      res.send(fs.readFileSync(files['null'].path))
    });
  });
});

module.exports = router;
<|endoftext|>


<commit_before>(function(angular, $, MutationObserver) {

    'use strict';

    var tables = angular.module('table.highlightColumn', []);

    tables.directive('stgHighlightColumn', function($timeout, $log, stgTableService) {

        /**
         * Track new insertions of TRs into the TBODY and specify for the
         * column TDs the listeners to bind.
         *
         * @param {type} tableId
         * @param {type} index
         * @returns {undefined}
         */
        function trackNewInsertions(tableId, index) {
            var target = $("#" + tableId + " tbody")[0];
            var observer = new MutationObserver(function(mutations) {
                mutations.forEach(function(mutation) {
                    if (mutation.type === "childList" && mutation.addedNodes.length > 0) {
                        angular.forEach(mutation.addedNodes, function(node) {
                            if (node.tagName === "TR") {
                                var tds = $(node).parent().find("td:nth-child(" + (index+1) + ")");
                                addListeners(tds);
                            }
                        });
                    }
                });
            });
            var config = {childList: true};
            observer.observe(target, config);
            return observer;
        };

        function addListeners(tds) {
            tds.off("mouseenter mouseleave"); // remove all previously added ones
            tds.hover(function() {
                tds.addClass("column-highlight");
            }, function() {
                tds.removeClass("column-highlight");
            });
            //TODO: possible memory leak from not clearing on $destroy?
        };

        return {
            restrict: 'A',
            link: function(scope, element, attrs) {
                var tagName = element[0].tagName;
                if (tagName !== "COL" && tagName !== "TH") {
                    $log.error("Using stg-highlight-column on an element " + tagName + " is not supported.");
                    return;
                }
                var tableId = stgTableService.getIdOfContainingTable(element);
                if (tableId) {
                    var observer = null;
                    var index = element.parent().children().toArray().indexOf(element[0]);
                    if (index !== undefined) {
                        observer = trackNewInsertions(tableId, index);
                    }
                    scope.$on('$destroy', function() {
                        if (observer !== null) {
                            observer.disconnect(); // remove observer from binding
                        }
                    });
                }
            }
        };
    });

})(angular, jQuery, MutationObserver);<commit_msg>Performance fix for initial tables<commit_after>(function(angular, $, MutationObserver) {

    'use strict';

    var tables = angular.module('table.highlightColumn', []);

    tables.directive('stgHighlightColumn', function($timeout, $log, stgTableService) {

        /**
         * Track new insertions of TRs into the TBODY and specify for the
         * column TDs the listeners to bind.
         *
         * @param {type} tableId
         * @param {type} index
         * @returns {undefined}
         */
        function trackNewInsertions(tableId, index) {
            var target = $("#" + tableId + " tbody")[0];
            var observer = new MutationObserver(function(mutations) {
                mutations.forEach(function(mutation) {
                    if (mutation.type === "childList" && mutation.addedNodes.length > 0) {
                        angular.forEach(mutation.addedNodes, function(node) {
                            if (node.tagName === "TR") {
                                var tds = $(node).parent().find("td:nth-child(" + (index+1) + ")");
                                addListeners(tds);
                            }
                        });
                    }
                });
            });
            var config = {childList: true};
            observer.observe(target, config);
            return observer;
        };

        function addListeners(tds) {
            tds.off("mouseenter mouseleave"); // remove all previously added ones
            tds.hover(function() {
                tds.addClass("column-highlight");
            }, function() {
                tds.removeClass("column-highlight");
            });
            //TODO: possible memory leak from not clearing on $destroy?
        };

        return {
            restrict: 'A',
            link: function(scope, element, attrs) {
                var tagName = element[0].tagName;
                if (tagName !== "COL" && tagName !== "TH") {
                    $log.error("Using stg-highlight-column on an element " + tagName + " is not supported.");
                    return;
                }
                var tableId = stgTableService.getIdOfContainingTable(element);
                if (tableId) {
                    var observer = null;
                    var index = element.parent().children().toArray().indexOf(element[0]);
                    if (index !== undefined) {
                        $timeout(function() {
                            var tds = $("#" + tableId + " td:nth-child(" + (index+1) + ")");
                            addListeners(tds);
                            observer = trackNewInsertions(tableId, index);
                        }, 0, false);

                    }
                    scope.$on('$destroy', function() {
                        if (observer !== null) {
                            observer.disconnect(); // remove observer from binding
                        }
                    });
                }
            }
        };
    });

})(angular, jQuery, MutationObserver);<|endoftext|>


<commit_before>import MongoCollection                          from './MongoCollection';
import MongoSchemaCollection                    from './MongoSchemaCollection';
import {parse as parseUrl, format as formatUrl} from '../../../vendor/mongodbUrl';
import * as transform                           from './MongoTransform';
import _                                        from 'lodash';

let mongodb = require('mongodb');
let MongoClient = mongodb.MongoClient;

const MongoSchemaCollectionName = '_SCHEMA';
const DefaultMongoURI = 'mongodb://localhost:27017/parse';

const storageAdapterAllCollections = mongoAdapter => {
  return mongoAdapter.connect()
  .then(() => mongoAdapter.database.collections())
  .then(collections => {
    return collections.filter(collection => {
      if (collection.namespace.match(/\.system\./)) {
        return false;
      }
      // TODO: If you have one app with a collection prefix that happens to be a prefix of another
      // apps prefix, this will go very very badly. We should fix that somehow.
      return (collection.collectionName.indexOf(mongoAdapter._collectionPrefix) == 0);
    });
  });
}

export class MongoStorageAdapter {
  // Private
  _uri: string;
  _collectionPrefix: string;
  _mongoOptions: Object;
  // Public
  connectionPromise;
  database;

  constructor({
    uri = DefaultMongoURI,
    collectionPrefix = '',
    mongoOptions = {},
  }) {
    this._uri = uri;
    this._collectionPrefix = collectionPrefix;
    this._mongoOptions = mongoOptions;
  }

  connect() {
    if (this.connectionPromise) {
      return this.connectionPromise;
    }

    // parsing and re-formatting causes the auth value (if there) to get URI
    // encoded
    const encodedUri = formatUrl(parseUrl(this._uri));

    this.connectionPromise = MongoClient.connect(encodedUri, this._mongoOptions).then(database => {
      this.database = database;
    });
    return this.connectionPromise;
  }

  collection(name: string) {
    return this.connect().then(() => {
      return this.database.collection(name);
    });
  }

  adaptiveCollection(name: string) {
    return this.connect()
      .then(() => this.database.collection(this._collectionPrefix + name))
      .then(rawCollection => new MongoCollection(rawCollection));
  }

  schemaCollection() {
    return this.connect()
      .then(() => this.adaptiveCollection(MongoSchemaCollectionName))
      .then(collection => new MongoSchemaCollection(collection));
  }

  collectionExists(name: string) {
    return this.connect().then(() => {
      return this.database.listCollections({ name: this._collectionPrefix + name }).toArray();
    }).then(collections => {
      return collections.length > 0;
    });
  }

  // Deletes a schema. Resolve if successful. If the schema doesn't
  // exist, resolve with undefined. If schema exists, but can't be deleted for some other reason,
  // reject with INTERNAL_SERVER_ERROR.
  deleteOneSchema(className: string) {
    return this.collection(this._collectionPrefix + className).then(collection => collection.drop())
    .catch(error => {
      // 'ns not found' means collection was already gone. Ignore deletion attempt.
      if (error.message == 'ns not found') {
        return Promise.resolve();
      }
      return Promise.reject(error);
    });
  }

  // Delete all data known to this adatper. Used for testing.
  deleteAllSchemas() {
    return storageAdapterAllCollections(this)
    .then(collections => Promise.all(collections.map(collection => collection.drop())));
  }

  // Remove the column and all the data. For Relations, the _Join collection is handled
  // specially, this function does not delete _Join columns. It should, however, indicate
  // that the relation fields does not exist anymore. In mongo, this means removing it from
  // the _SCHEMA collection.  There should be no actual data in the collection under the same name
  // as the relation column, so it's fine to attempt to delete it. If the fields listed to be
  // deleted do not exist, this function should return successfully anyways. Checking for
  // attempts to delete non-existent fields is the responsibility of Parse Server.

  // Pointer field names are passed for legacy reasons: the original mongo
  // format stored pointer field names differently in the database, and therefore
  // needed to know the type of the field before it could delete it. Future database
  // adatpers should ignore the pointerFieldNames argument. All the field names are in
  // fieldNames, they show up additionally in the pointerFieldNames database for use
  // by the mongo adapter, which deals with the legacy mongo format.

  // This function is not obligated to delete fields atomically. It is given the field
  // names in a list so that databases that are capable of deleting fields atomically
  // may do so.

  // Returns a Promise.
  deleteFields(className: string, fieldNames, pointerFieldNames) {
    const nonPointerFieldNames = _.difference(fieldNames, pointerFieldNames);
    const mongoFormatNames = nonPointerFieldNames.concat(pointerFieldNames.map(name => `_p_${name}`));
    const collectionUpdate = { '$unset' : {} };
    mongoFormatNames.forEach(name => {
      collectionUpdate['$unset'][name] = null;
    });

    const schemaUpdate = { '$unset' : {} };
    fieldNames.forEach(name => {
      schemaUpdate['$unset'][name] = null;
    });

    return this.adaptiveCollection(className)
    .then(collection => collection.updateMany({}, collectionUpdate))
    .then(updateResult => this.schemaCollection())
    .then(schemaCollection => schemaCollection.updateSchema(className, schemaUpdate));
  }

  // Return a promise for all schemas known to this adapter, in Parse format. In case the
  // schemas cannot be retrieved, returns a promise that rejects. Requirements for the
  // rejection reason are TBD.
  getAllSchemas() {
    return this.schemaCollection().then(schemasCollection => schemasCollection._fetchAllSchemasFrom_SCHEMA());
  }

  // Return a promise for the schema with the given name, in Parse format. If
  // this adapter doesn't know about the schema, return a promise that rejects with
  // undefined as the reason.
  getOneSchema(className) {
    return this.schemaCollection()
    .then(schemasCollection => schemasCollection._fechOneSchemaFrom_SCHEMA(className));
  }

  // TODO: As yet not particularly well specified. Creates an object. Shouldn't need the
  // schemaController, but MongoTransform still needs it :( maybe shouldn't even need the schema,
  // and should infer from the type. Or maybe does need the schema for validations. Or maybe needs
  // the schem only for the legacy mongo format. We'll figure that out later.
  createObject(className, object, schemaController, parseFormatSchema) {
    const mongoObject = transform.parseObjectToMongoObjectForCreate(schemaController, className, object, parseFormatSchema);
    return this.adaptiveCollection(className)
    .then(collection => collection.insertOne(mongoObject))
    .catch(error => {
      if (error.code === 11000) { // Duplicate value
        throw new Parse.Error(Parse.Error.DUPLICATE_VALUE,
            'A duplicate value for a field with unique values was provided');
      }
      return Promise.reject(error);
    });
  }

  // Remove all objects that match the given parse query. Parse Query should be in Parse Format.
  // If no objects match, reject with OBJECT_NOT_FOUND. If objects are found and deleted, resolve with undefined.
  // If there is some other error, reject with INTERNAL_SERVER_ERROR.

  // Currently accepts the schema, that may not actually be necessary.
  deleteObjectsByQuery(className, query, schema) {
    return this.adaptiveCollection(className)
    .then(collection => {
      let mongoWhere = transform.transformWhere(className, query, schema);
      return collection.deleteMany(mongoWhere)
    })
    .then(({ result }) => {
      if (result.n === 0) {
        throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Object not found.');
      }
      return Promise.resolve();
    }, error => {
      throw new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'Database adapter error');
    });
  }

  // Executes a find. Accepts: className, query in Parse format, and { skip, limit, sort }.
  find(className, query, { skip, limit, sort }, schema) {
    let mongoWhere = this.transform.transformWhere(className, query, schema);
    return this.adaptiveCollection(className)
    .then(collection => collection.find(mongoWhere, { skip, limit, sort }))
    .then(objects => objects.map(object => transform.mongoObjectToParseObject(className, object, schema)));
  }

  // Executs a count.
  count(className, query, { limit, skip, sort }) {
    return this.adaptiveCollection(className)
    .then(collection => collection.count(query, { limit, skip, sort }));
  }

  get transform() {
    return transform;
  }
}

export default MongoStorageAdapter;
module.exports = MongoStorageAdapter; // Required for tests
<commit_msg>Remove limit from count<commit_after>import MongoCollection                          from './MongoCollection';
import MongoSchemaCollection                    from './MongoSchemaCollection';
import {parse as parseUrl, format as formatUrl} from '../../../vendor/mongodbUrl';
import * as transform                           from './MongoTransform';
import _                                        from 'lodash';

let mongodb = require('mongodb');
let MongoClient = mongodb.MongoClient;

const MongoSchemaCollectionName = '_SCHEMA';
const DefaultMongoURI = 'mongodb://localhost:27017/parse';

const storageAdapterAllCollections = mongoAdapter => {
  return mongoAdapter.connect()
  .then(() => mongoAdapter.database.collections())
  .then(collections => {
    return collections.filter(collection => {
      if (collection.namespace.match(/\.system\./)) {
        return false;
      }
      // TODO: If you have one app with a collection prefix that happens to be a prefix of another
      // apps prefix, this will go very very badly. We should fix that somehow.
      return (collection.collectionName.indexOf(mongoAdapter._collectionPrefix) == 0);
    });
  });
}

export class MongoStorageAdapter {
  // Private
  _uri: string;
  _collectionPrefix: string;
  _mongoOptions: Object;
  // Public
  connectionPromise;
  database;

  constructor({
    uri = DefaultMongoURI,
    collectionPrefix = '',
    mongoOptions = {},
  }) {
    this._uri = uri;
    this._collectionPrefix = collectionPrefix;
    this._mongoOptions = mongoOptions;
  }

  connect() {
    if (this.connectionPromise) {
      return this.connectionPromise;
    }

    // parsing and re-formatting causes the auth value (if there) to get URI
    // encoded
    const encodedUri = formatUrl(parseUrl(this._uri));

    this.connectionPromise = MongoClient.connect(encodedUri, this._mongoOptions).then(database => {
      this.database = database;
    });
    return this.connectionPromise;
  }

  collection(name: string) {
    return this.connect().then(() => {
      return this.database.collection(name);
    });
  }

  adaptiveCollection(name: string) {
    return this.connect()
      .then(() => this.database.collection(this._collectionPrefix + name))
      .then(rawCollection => new MongoCollection(rawCollection));
  }

  schemaCollection() {
    return this.connect()
      .then(() => this.adaptiveCollection(MongoSchemaCollectionName))
      .then(collection => new MongoSchemaCollection(collection));
  }

  collectionExists(name: string) {
    return this.connect().then(() => {
      return this.database.listCollections({ name: this._collectionPrefix + name }).toArray();
    }).then(collections => {
      return collections.length > 0;
    });
  }

  // Deletes a schema. Resolve if successful. If the schema doesn't
  // exist, resolve with undefined. If schema exists, but can't be deleted for some other reason,
  // reject with INTERNAL_SERVER_ERROR.
  deleteOneSchema(className: string) {
    return this.collection(this._collectionPrefix + className).then(collection => collection.drop())
    .catch(error => {
      // 'ns not found' means collection was already gone. Ignore deletion attempt.
      if (error.message == 'ns not found') {
        return Promise.resolve();
      }
      return Promise.reject(error);
    });
  }

  // Delete all data known to this adatper. Used for testing.
  deleteAllSchemas() {
    return storageAdapterAllCollections(this)
    .then(collections => Promise.all(collections.map(collection => collection.drop())));
  }

  // Remove the column and all the data. For Relations, the _Join collection is handled
  // specially, this function does not delete _Join columns. It should, however, indicate
  // that the relation fields does not exist anymore. In mongo, this means removing it from
  // the _SCHEMA collection.  There should be no actual data in the collection under the same name
  // as the relation column, so it's fine to attempt to delete it. If the fields listed to be
  // deleted do not exist, this function should return successfully anyways. Checking for
  // attempts to delete non-existent fields is the responsibility of Parse Server.

  // Pointer field names are passed for legacy reasons: the original mongo
  // format stored pointer field names differently in the database, and therefore
  // needed to know the type of the field before it could delete it. Future database
  // adatpers should ignore the pointerFieldNames argument. All the field names are in
  // fieldNames, they show up additionally in the pointerFieldNames database for use
  // by the mongo adapter, which deals with the legacy mongo format.

  // This function is not obligated to delete fields atomically. It is given the field
  // names in a list so that databases that are capable of deleting fields atomically
  // may do so.

  // Returns a Promise.
  deleteFields(className: string, fieldNames, pointerFieldNames) {
    const nonPointerFieldNames = _.difference(fieldNames, pointerFieldNames);
    const mongoFormatNames = nonPointerFieldNames.concat(pointerFieldNames.map(name => `_p_${name}`));
    const collectionUpdate = { '$unset' : {} };
    mongoFormatNames.forEach(name => {
      collectionUpdate['$unset'][name] = null;
    });

    const schemaUpdate = { '$unset' : {} };
    fieldNames.forEach(name => {
      schemaUpdate['$unset'][name] = null;
    });

    return this.adaptiveCollection(className)
    .then(collection => collection.updateMany({}, collectionUpdate))
    .then(updateResult => this.schemaCollection())
    .then(schemaCollection => schemaCollection.updateSchema(className, schemaUpdate));
  }

  // Return a promise for all schemas known to this adapter, in Parse format. In case the
  // schemas cannot be retrieved, returns a promise that rejects. Requirements for the
  // rejection reason are TBD.
  getAllSchemas() {
    return this.schemaCollection().then(schemasCollection => schemasCollection._fetchAllSchemasFrom_SCHEMA());
  }

  // Return a promise for the schema with the given name, in Parse format. If
  // this adapter doesn't know about the schema, return a promise that rejects with
  // undefined as the reason.
  getOneSchema(className) {
    return this.schemaCollection()
    .then(schemasCollection => schemasCollection._fechOneSchemaFrom_SCHEMA(className));
  }

  // TODO: As yet not particularly well specified. Creates an object. Shouldn't need the
  // schemaController, but MongoTransform still needs it :( maybe shouldn't even need the schema,
  // and should infer from the type. Or maybe does need the schema for validations. Or maybe needs
  // the schem only for the legacy mongo format. We'll figure that out later.
  createObject(className, object, schemaController, parseFormatSchema) {
    const mongoObject = transform.parseObjectToMongoObjectForCreate(schemaController, className, object, parseFormatSchema);
    return this.adaptiveCollection(className)
    .then(collection => collection.insertOne(mongoObject))
    .catch(error => {
      if (error.code === 11000) { // Duplicate value
        throw new Parse.Error(Parse.Error.DUPLICATE_VALUE,
            'A duplicate value for a field with unique values was provided');
      }
      return Promise.reject(error);
    });
  }

  // Remove all objects that match the given parse query. Parse Query should be in Parse Format.
  // If no objects match, reject with OBJECT_NOT_FOUND. If objects are found and deleted, resolve with undefined.
  // If there is some other error, reject with INTERNAL_SERVER_ERROR.

  // Currently accepts the schema, that may not actually be necessary.
  deleteObjectsByQuery(className, query, schema) {
    return this.adaptiveCollection(className)
    .then(collection => {
      let mongoWhere = transform.transformWhere(className, query, schema);
      return collection.deleteMany(mongoWhere)
    })
    .then(({ result }) => {
      if (result.n === 0) {
        throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Object not found.');
      }
      return Promise.resolve();
    }, error => {
      throw new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'Database adapter error');
    });
  }

  // Executes a find. Accepts: className, query in Parse format, and { skip, limit, sort }.
  find(className, query, { skip, limit, sort }, schema) {
    let mongoWhere = this.transform.transformWhere(className, query, schema);
    return this.adaptiveCollection(className)
    .then(collection => collection.find(mongoWhere, { skip, limit, sort }))
    .then(objects => objects.map(object => transform.mongoObjectToParseObject(className, object, schema)));
  }

  // Executs a count.
  count(className, query, { skip, sort }) {
    return this.adaptiveCollection(className)
    .then(collection => collection.count(query, { skip, sort }));
  }

  get transform() {
    return transform;
  }
}

export default MongoStorageAdapter;
module.exports = MongoStorageAdapter; // Required for tests
<|endoftext|>