From 7f01c5355090a53e9f192d7ecc4f003f932a5022 Mon Sep 17 00:00:00 2001
From: Eduard Bosch Bertran
+ * Some functions are only available from Cloud Code.
+ * Open Event - When you call query.subscribe(), we send a subscribe request to
+ * the LiveQuery server, when we get the confirmation from the LiveQuery server,
+ * this event will be emitted. When the client loses WebSocket connection to the
+ * LiveQuery server, we will try to auto reconnect the LiveQuery server. If we
+ * reconnect the LiveQuery server and successfully resubscribe the ParseQuery,
+ * you'll also get this event.
+ *
+ *
+ *
+ * FB.init()
. Parse.FacebookUtils will invoke FB.init() for you
+ * with these arguments.
+ *
+ * @method init
+ * @param {Object} options Facebook options argument as described here:
+ *
+ * FB.init(). The status flag will be coerced to 'false' because it
+ * interferes with Parse Facebook integration. Call FB.getLoginStatus()
+ * explicitly if this behavior is required by your application.
+ */
+ init: function (options) {
+ if (typeof FB === 'undefined') {
+ throw new Error('The Facebook JavaScript SDK must be loaded before calling init.');
+ }
+ initOptions = {};
+ if (options) {
+ for (var key in options) {
+ initOptions[key] = options[key];
+ }
+ }
+ if (initOptions.status && typeof console !== 'undefined') {
+ var warn = console.warn || console.log || function () {};
+ warn.call(console, 'The "status" flag passed into' + ' FB.init, when set to true, can interfere with Parse Facebook' + ' integration, so it has been suppressed. Please call' + ' FB.getLoginStatus() explicitly if you require this behavior.');
+ }
+ initOptions.status = false;
+ FB.init(initOptions);
+ _ParseUser2.default._registerAuthenticationProvider(provider);
+ initialized = true;
+ },
+
+ /**
+ * Gets whether the user has their account linked to Facebook.
+ *
+ * @method isLinked
+ * @param {Parse.User} user User to check for a facebook link.
+ * The user must be logged in on this device.
+ * @return {Boolean} true
if the user has their account
+ * linked to Facebook.
+ */
+ isLinked: function (user) {
+ return user._isLinked('facebook');
+ },
+
+ /**
+ * Logs in a user using Facebook. This method delegates to the Facebook
+ * SDK to authenticate the user, and then automatically logs in (or
+ * creates, in the case where it is a new user) a Parse.User.
+ *
+ * @method logIn
+ * @param {String, Object} permissions The permissions required for Facebook
+ * log in. This is a comma-separated string of permissions.
+ * Alternatively, supply a Facebook authData object as described in our
+ * REST API docs if you want to handle getting facebook auth tokens
+ * yourself.
+ * @param {Object} options Standard options object with success and error
+ * callbacks.
+ */
+ logIn: function (permissions, options) {
+ if (!permissions || typeof permissions === 'string') {
+ if (!initialized) {
+ throw new Error('You must initialize FacebookUtils before calling logIn.');
+ }
+ requestedPermissions = permissions;
+ return _ParseUser2.default._logInWith('facebook', options);
+ } else {
+ var newOptions = {};
+ if (options) {
+ for (var key in options) {
+ newOptions[key] = options[key];
+ }
+ }
+ newOptions.authData = permissions;
+ return _ParseUser2.default._logInWith('facebook', newOptions);
+ }
+ },
+
+ /**
+ * Links Facebook to an existing PFUser. This method delegates to the
+ * Facebook SDK to authenticate the user, and then automatically links
+ * the account to the Parse.User.
+ *
+ * @method link
+ * @param {Parse.User} user User to link to Facebook. This must be the
+ * current user.
+ * @param {String, Object} permissions The permissions required for Facebook
+ * log in. This is a comma-separated string of permissions.
+ * Alternatively, supply a Facebook authData object as described in our
+ * REST API docs if you want to handle getting facebook auth tokens
+ * yourself.
+ * @param {Object} options Standard options object with success and error
+ * callbacks.
+ */
+ link: function (user, permissions, options) {
+ if (!permissions || typeof permissions === 'string') {
+ if (!initialized) {
+ throw new Error('You must initialize FacebookUtils before calling link.');
+ }
+ requestedPermissions = permissions;
+ return user._linkWith('facebook', options);
+ } else {
+ var newOptions = {};
+ if (options) {
+ for (var key in options) {
+ newOptions[key] = options[key];
+ }
+ }
+ newOptions.authData = permissions;
+ return user._linkWith('facebook', newOptions);
+ }
+ },
+
+ /**
+ * Unlinks the Parse.User from a Facebook account.
+ *
+ * @method unlink
+ * @param {Parse.User} user User to unlink from Facebook. This must be the
+ * current user.
+ * @param {Object} options Standard options object with success and error
+ * callbacks.
+ */
+ unlink: function (user, options) {
+ if (!initialized) {
+ throw new Error('You must initialize FacebookUtils before calling unlink.');
+ }
+ return user._unlinkFrom('facebook', options);
+ }
+};
+
+exports.default = FacebookUtils;
+},{"./ParseUser":25,"./parseDate":40}],6:[function(_dereq_,module,exports){
+'use strict';
+
+var _CoreManager = _dereq_('./CoreManager');
+
+var _CoreManager2 = _interopRequireDefault(_CoreManager);
+
+var _ParsePromise = _dereq_('./ParsePromise');
+
+var _ParsePromise2 = _interopRequireDefault(_ParsePromise);
+
+var _Storage = _dereq_('./Storage');
+
+var _Storage2 = _interopRequireDefault(_Storage);
+
+function _interopRequireDefault(obj) {
+ return obj && obj.__esModule ? obj : { default: obj };
+}
+
+var iidCache = null; /**
+ * Copyright (c) 2015-present, Parse, LLC.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ *
+ *
+ */
+
+function hexOctet() {
+ return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
+}
+
+function generateId() {
+ return hexOctet() + hexOctet() + '-' + hexOctet() + '-' + hexOctet() + '-' + hexOctet() + '-' + hexOctet() + hexOctet() + hexOctet();
+}
+
+var InstallationController = {
+ currentInstallationId: function () {
+ if (typeof iidCache === 'string') {
+ return _ParsePromise2.default.as(iidCache);
+ }
+ var path = _Storage2.default.generatePath('installationId');
+ return _Storage2.default.getItemAsync(path).then(function (iid) {
+ if (!iid) {
+ iid = generateId();
+ return _Storage2.default.setItemAsync(path, iid).then(function () {
+ iidCache = iid;
+ return iid;
+ });
+ }
+ iidCache = iid;
+ return iid;
+ });
+ },
+ _clearCache: function () {
+ iidCache = null;
+ },
+ _setInstallationIdCache: function (iid) {
+ iidCache = iid;
+ }
+};
+
+module.exports = InstallationController;
+},{"./CoreManager":3,"./ParsePromise":20,"./Storage":29}],7:[function(_dereq_,module,exports){
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+
+var _typeof2 = _dereq_('babel-runtime/helpers/typeof');
+
+var _typeof3 = _interopRequireDefault(_typeof2);
+
+var _getIterator2 = _dereq_('babel-runtime/core-js/get-iterator');
+
+var _getIterator3 = _interopRequireDefault(_getIterator2);
+
+var _stringify = _dereq_('babel-runtime/core-js/json/stringify');
+
+var _stringify2 = _interopRequireDefault(_stringify);
+
+var _map = _dereq_('babel-runtime/core-js/map');
+
+var _map2 = _interopRequireDefault(_map);
+
+var _getPrototypeOf = _dereq_('babel-runtime/core-js/object/get-prototype-of');
+
+var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
+
+var _classCallCheck2 = _dereq_('babel-runtime/helpers/classCallCheck');
+
+var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
+
+var _createClass2 = _dereq_('babel-runtime/helpers/createClass');
+
+var _createClass3 = _interopRequireDefault(_createClass2);
+
+var _possibleConstructorReturn2 = _dereq_('babel-runtime/helpers/possibleConstructorReturn');
+
+var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
+
+var _inherits2 = _dereq_('babel-runtime/helpers/inherits');
+
+var _inherits3 = _interopRequireDefault(_inherits2);
+
+var _EventEmitter2 = _dereq_('./EventEmitter');
+
+var _EventEmitter3 = _interopRequireDefault(_EventEmitter2);
+
+var _ParsePromise = _dereq_('./ParsePromise');
+
+var _ParsePromise2 = _interopRequireDefault(_ParsePromise);
+
+var _ParseObject = _dereq_('./ParseObject');
+
+var _ParseObject2 = _interopRequireDefault(_ParseObject);
+
+var _LiveQuerySubscription = _dereq_('./LiveQuerySubscription');
+
+var _LiveQuerySubscription2 = _interopRequireDefault(_LiveQuerySubscription);
+
+function _interopRequireDefault(obj) {
+ return obj && obj.__esModule ? obj : { default: obj };
+}
+
+// The LiveQuery client inner state
+/**
+ * Copyright (c) 2015-present, Parse, LLC.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+var CLIENT_STATE = {
+ INITIALIZED: 'initialized',
+ CONNECTING: 'connecting',
+ CONNECTED: 'connected',
+ CLOSED: 'closed',
+ RECONNECTING: 'reconnecting',
+ DISCONNECTED: 'disconnected'
+};
+
+// The event type the LiveQuery client should sent to server
+var OP_TYPES = {
+ CONNECT: 'connect',
+ SUBSCRIBE: 'subscribe',
+ UNSUBSCRIBE: 'unsubscribe',
+ ERROR: 'error'
+};
+
+// The event we get back from LiveQuery server
+var OP_EVENTS = {
+ CONNECTED: 'connected',
+ SUBSCRIBED: 'subscribed',
+ UNSUBSCRIBED: 'unsubscribed',
+ ERROR: 'error',
+ CREATE: 'create',
+ UPDATE: 'update',
+ ENTER: 'enter',
+ LEAVE: 'leave',
+ DELETE: 'delete'
+};
+
+// The event the LiveQuery client should emit
+var CLIENT_EMMITER_TYPES = {
+ CLOSE: 'close',
+ ERROR: 'error',
+ OPEN: 'open'
+};
+
+// The event the LiveQuery subscription should emit
+var SUBSCRIPTION_EMMITER_TYPES = {
+ OPEN: 'open',
+ CLOSE: 'close',
+ ERROR: 'error',
+ CREATE: 'create',
+ UPDATE: 'update',
+ ENTER: 'enter',
+ LEAVE: 'leave',
+ DELETE: 'delete'
+};
+
+var generateInterval = function (k) {
+ return Math.random() * Math.min(30, Math.pow(2, k) - 1) * 1000;
+};
+
+/**
+ * Creates a new LiveQueryClient.
+ * Extends events.EventEmitter
+ * cloud functions.
+ *
+ * A wrapper of a standard WebSocket client. We add several useful methods to
+ * help you connect/disconnect to LiveQueryServer, subscribe/unsubscribe a ParseQuery easily.
+ *
+ * javascriptKey and masterKey are used for verifying the LiveQueryClient when it tries
+ * to connect to the LiveQuery server
+ *
+ * @class Parse.LiveQueryClient
+ * @constructor
+ * @param {Object} options
+ * @param {string} options.applicationId - applicationId of your Parse app
+ * @param {string} options.serverURL - the URL of your LiveQuery server
+ * @param {string} options.javascriptKey (optional)
+ * @param {string} options.masterKey (optional) Your Parse Master Key. (Node.js only!)
+ * @param {string} options.sessionToken (optional)
+ *
+ *
+ * We expose three events to help you monitor the status of the LiveQueryClient.
+ *
+ *
+ * let Parse = require('parse/node');
+ * let LiveQueryClient = Parse.LiveQueryClient;
+ * let client = new LiveQueryClient({
+ * applicationId: '',
+ * serverURL: '',
+ * javascriptKey: '',
+ * masterKey: ''
+ * });
+ *
+ *
+ * Open - When we establish the WebSocket connection to the LiveQuery server, you'll get this event.
+ *
+ * client.on('open', () => {
+ *
+ * });
+ *
+ * Close - When we lose the WebSocket connection to the LiveQuery server, you'll get this event.
+ *
+ * client.on('close', () => {
+ *
+ * });
+ *
+ * Error - When some network error or LiveQuery server error happens, you'll get this event.
+ *
+ * client.on('error', (error) => {
+ *
+ * });
+ *
+ *
+ */
+
+var LiveQueryClient = function (_EventEmitter) {
+ (0, _inherits3.default)(LiveQueryClient, _EventEmitter);
+
+ function LiveQueryClient(_ref) {
+ var applicationId = _ref.applicationId,
+ serverURL = _ref.serverURL,
+ javascriptKey = _ref.javascriptKey,
+ masterKey = _ref.masterKey,
+ sessionToken = _ref.sessionToken;
+ (0, _classCallCheck3.default)(this, LiveQueryClient);
+
+ var _this = (0, _possibleConstructorReturn3.default)(this, (LiveQueryClient.__proto__ || (0, _getPrototypeOf2.default)(LiveQueryClient)).call(this));
+
+ if (!serverURL || serverURL.indexOf('ws') !== 0) {
+ throw new Error('You need to set a proper Parse LiveQuery server url before using LiveQueryClient');
+ }
+
+ _this.reconnectHandle = null;
+ _this.attempts = 1;;
+ _this.id = 0;
+ _this.requestId = 1;
+ _this.serverURL = serverURL;
+ _this.applicationId = applicationId;
+ _this.javascriptKey = javascriptKey;
+ _this.masterKey = masterKey;
+ _this.sessionToken = sessionToken;
+ _this.connectPromise = new _ParsePromise2.default();
+ _this.subscriptions = new _map2.default();
+ _this.state = CLIENT_STATE.INITIALIZED;
+ return _this;
+ }
+
+ (0, _createClass3.default)(LiveQueryClient, [{
+ key: 'shouldOpen',
+ value: function () {
+ return this.state === CLIENT_STATE.INITIALIZED || this.state === CLIENT_STATE.DISCONNECTED;
+ }
+
+ /**
+ * Subscribes to a ParseQuery
+ *
+ * If you provide the sessionToken, when the LiveQuery server gets ParseObject's
+ * updates from parse server, it'll try to check whether the sessionToken fulfills
+ * the ParseObject's ACL. The LiveQuery server will only send updates to clients whose
+ * sessionToken is fit for the ParseObject's ACL. You can check the LiveQuery protocol
+ * here for more details. The subscription you get is the same subscription you get
+ * from our Standard API.
+ *
+ * @method subscribe
+ * @param {Object} query - the ParseQuery you want to subscribe to
+ * @param {string} sessionToken (optional)
+ * @return {Object} subscription
+ */
+
+ }, {
+ key: 'subscribe',
+ value: function (query, sessionToken) {
+ var _this2 = this;
+
+ if (!query) {
+ return;
+ }
+ var where = query.toJSON().where;
+ var className = query.className;
+ var subscribeRequest = {
+ op: OP_TYPES.SUBSCRIBE,
+ requestId: this.requestId,
+ query: {
+ className: className,
+ where: where
+ }
+ };
+
+ if (sessionToken) {
+ subscribeRequest.sessionToken = sessionToken;
+ }
+
+ var subscription = new _LiveQuerySubscription2.default(this.requestId, query, sessionToken);
+ this.subscriptions.set(this.requestId, subscription);
+ this.requestId += 1;
+ this.connectPromise.then(function () {
+ _this2.socket.send((0, _stringify2.default)(subscribeRequest));
+ });
+
+ // adding listener so process does not crash
+ // best practice is for developer to register their own listener
+ subscription.on('error', function () {});
+
+ return subscription;
+ }
+
+ /**
+ * After calling unsubscribe you'll stop receiving events from the subscription object.
+ *
+ * @method unsubscribe
+ * @param {Object} subscription - subscription you would like to unsubscribe from.
+ */
+
+ }, {
+ key: 'unsubscribe',
+ value: function (subscription) {
+ var _this3 = this;
+
+ if (!subscription) {
+ return;
+ }
+
+ this.subscriptions.delete(subscription.id);
+ var unsubscribeRequest = {
+ op: OP_TYPES.UNSUBSCRIBE,
+ requestId: subscription.id
+ };
+ this.connectPromise.then(function () {
+ _this3.socket.send((0, _stringify2.default)(unsubscribeRequest));
+ });
+ }
+
+ /**
+ * After open is called, the LiveQueryClient will try to send a connect request
+ * to the LiveQuery server.
+ *
+ * @method open
+ */
+
+ }, {
+ key: 'open',
+ value: function () {
+ var _this4 = this;
+
+ var WebSocketImplementation = this._getWebSocketImplementation();
+ if (!WebSocketImplementation) {
+ this.emit(CLIENT_EMMITER_TYPES.ERROR, 'Can not find WebSocket implementation');
+ return;
+ }
+
+ if (this.state !== CLIENT_STATE.RECONNECTING) {
+ this.state = CLIENT_STATE.CONNECTING;
+ }
+
+ // Get WebSocket implementation
+ this.socket = new WebSocketImplementation(this.serverURL);
+
+ // Bind WebSocket callbacks
+ this.socket.onopen = function () {
+ _this4._handleWebSocketOpen();
+ };
+
+ this.socket.onmessage = function (event) {
+ _this4._handleWebSocketMessage(event);
+ };
+
+ this.socket.onclose = function () {
+ _this4._handleWebSocketClose();
+ };
+
+ this.socket.onerror = function (error) {
+ _this4._handleWebSocketError(error);
+ };
+ }
+ }, {
+ key: 'resubscribe',
+ value: function () {
+ var _this5 = this;
+
+ this.subscriptions.forEach(function (subscription, requestId) {
+ var query = subscription.query;
+ var where = query.toJSON().where;
+ var className = query.className;
+ var sessionToken = subscription.sessionToken;
+ var subscribeRequest = {
+ op: OP_TYPES.SUBSCRIBE,
+ requestId: requestId,
+ query: {
+ className: className,
+ where: where
+ }
+ };
+
+ if (sessionToken) {
+ subscribeRequest.sessionToken = sessionToken;
+ }
+
+ _this5.connectPromise.then(function () {
+ _this5.socket.send((0, _stringify2.default)(subscribeRequest));
+ });
+ });
+ }
+
+ /**
+ * This method will close the WebSocket connection to this LiveQueryClient,
+ * cancel the auto reconnect and unsubscribe all subscriptions based on it.
+ *
+ * @method close
+ */
+
+ }, {
+ key: 'close',
+ value: function () {
+ if (this.state === CLIENT_STATE.INITIALIZED || this.state === CLIENT_STATE.DISCONNECTED) {
+ return;
+ }
+ this.state = CLIENT_STATE.DISCONNECTED;
+ this.socket.close();
+ // Notify each subscription about the close
+ var _iteratorNormalCompletion = true;
+ var _didIteratorError = false;
+ var _iteratorError = undefined;
+
+ try {
+ for (var _iterator = (0, _getIterator3.default)(this.subscriptions.values()), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
+ var subscription = _step.value;
+
+ subscription.emit(SUBSCRIPTION_EMMITER_TYPES.CLOSE);
+ }
+ } catch (err) {
+ _didIteratorError = true;
+ _iteratorError = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion && _iterator.return) {
+ _iterator.return();
+ }
+ } finally {
+ if (_didIteratorError) {
+ throw _iteratorError;
+ }
+ }
+ }
+
+ this._handleReset();
+ this.emit(CLIENT_EMMITER_TYPES.CLOSE);
+ }
+ }, {
+ key: '_getWebSocketImplementation',
+ value: function () {
+ return typeof WebSocket === 'function' || (typeof WebSocket === 'undefined' ? 'undefined' : (0, _typeof3.default)(WebSocket)) === 'object' ? WebSocket : null;
+ }
+
+ // ensure we start with valid state if connect is called again after close
+
+ }, {
+ key: '_handleReset',
+ value: function () {
+ this.attempts = 1;;
+ this.id = 0;
+ this.requestId = 1;
+ this.connectPromise = new _ParsePromise2.default();
+ this.subscriptions = new _map2.default();
+ }
+ }, {
+ key: '_handleWebSocketOpen',
+ value: function () {
+ this.attempts = 1;
+ var connectRequest = {
+ op: OP_TYPES.CONNECT,
+ applicationId: this.applicationId,
+ javascriptKey: this.javascriptKey,
+ masterKey: this.masterKey,
+ sessionToken: this.sessionToken
+ };
+ this.socket.send((0, _stringify2.default)(connectRequest));
+ }
+ }, {
+ key: '_handleWebSocketMessage',
+ value: function (event) {
+ var data = event.data;
+ if (typeof data === 'string') {
+ data = JSON.parse(data);
+ }
+ var subscription = null;
+ if (data.requestId) {
+ subscription = this.subscriptions.get(data.requestId);
+ }
+ switch (data.op) {
+ case OP_EVENTS.CONNECTED:
+ if (this.state === CLIENT_STATE.RECONNECTING) {
+ this.resubscribe();
+ }
+ this.emit(CLIENT_EMMITER_TYPES.OPEN);
+ this.id = data.clientId;
+ this.connectPromise.resolve();
+ this.state = CLIENT_STATE.CONNECTED;
+ break;
+ case OP_EVENTS.SUBSCRIBED:
+ if (subscription) {
+ subscription.emit(SUBSCRIPTION_EMMITER_TYPES.OPEN);
+ }
+ break;
+ case OP_EVENTS.ERROR:
+ if (data.requestId) {
+ if (subscription) {
+ subscription.emit(SUBSCRIPTION_EMMITER_TYPES.ERROR, data.error);
+ }
+ } else {
+ this.emit(CLIENT_EMMITER_TYPES.ERROR, data.error);
+ }
+ break;
+ case OP_EVENTS.UNSUBSCRIBED:
+ // We have already deleted subscription in unsubscribe(), do nothing here
+ break;
+ default:
+ // create, update, enter, leave, delete cases
+ var className = data.object.className;
+ // Delete the extrea __type and className fields during transfer to full JSON
+ delete data.object.__type;
+ delete data.object.className;
+ var parseObject = new _ParseObject2.default(className);
+ parseObject._finishFetch(data.object);
+ if (!subscription) {
+ break;
+ }
+ subscription.emit(data.op, parseObject);
+ }
+ }
+ }, {
+ key: '_handleWebSocketClose',
+ value: function () {
+ if (this.state === CLIENT_STATE.DISCONNECTED) {
+ return;
+ }
+ this.state = CLIENT_STATE.CLOSED;
+ this.emit(CLIENT_EMMITER_TYPES.CLOSE);
+ // Notify each subscription about the close
+ var _iteratorNormalCompletion2 = true;
+ var _didIteratorError2 = false;
+ var _iteratorError2 = undefined;
+
+ try {
+ for (var _iterator2 = (0, _getIterator3.default)(this.subscriptions.values()), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
+ var subscription = _step2.value;
+
+ subscription.emit(SUBSCRIPTION_EMMITER_TYPES.CLOSE);
+ }
+ } catch (err) {
+ _didIteratorError2 = true;
+ _iteratorError2 = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion2 && _iterator2.return) {
+ _iterator2.return();
+ }
+ } finally {
+ if (_didIteratorError2) {
+ throw _iteratorError2;
+ }
+ }
+ }
+
+ this._handleReconnect();
+ }
+ }, {
+ key: '_handleWebSocketError',
+ value: function (error) {
+ this.emit(CLIENT_EMMITER_TYPES.ERROR, error);
+ var _iteratorNormalCompletion3 = true;
+ var _didIteratorError3 = false;
+ var _iteratorError3 = undefined;
+
+ try {
+ for (var _iterator3 = (0, _getIterator3.default)(this.subscriptions.values()), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
+ var subscription = _step3.value;
+
+ subscription.emit(SUBSCRIPTION_EMMITER_TYPES.ERROR);
+ }
+ } catch (err) {
+ _didIteratorError3 = true;
+ _iteratorError3 = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion3 && _iterator3.return) {
+ _iterator3.return();
+ }
+ } finally {
+ if (_didIteratorError3) {
+ throw _iteratorError3;
+ }
+ }
+ }
+
+ this._handleReconnect();
+ }
+ }, {
+ key: '_handleReconnect',
+ value: function () {
+ var _this6 = this;
+
+ // if closed or currently reconnecting we stop attempting to reconnect
+ if (this.state === CLIENT_STATE.DISCONNECTED) {
+ return;
+ }
+
+ this.state = CLIENT_STATE.RECONNECTING;
+ var time = generateInterval(this.attempts);
+
+ // handle case when both close/error occur at frequent rates we ensure we do not reconnect unnecessarily.
+ // we're unable to distinguish different between close/error when we're unable to reconnect therefore
+ // we try to reonnect in both cases
+ // server side ws and browser WebSocket behave differently in when close/error get triggered
+
+ if (this.reconnectHandle) {
+ clearTimeout(this.reconnectHandle);
+ }
+
+ this.reconnectHandle = setTimeout(function () {
+ _this6.attempts++;
+ _this6.connectPromise = new _ParsePromise2.default();
+ _this6.open();
+ }.bind(this), time);
+ }
+ }]);
+ return LiveQueryClient;
+}(_EventEmitter3.default);
+
+exports.default = LiveQueryClient;
+},{"./EventEmitter":4,"./LiveQuerySubscription":8,"./ParseObject":18,"./ParsePromise":20,"babel-runtime/core-js/get-iterator":43,"babel-runtime/core-js/json/stringify":44,"babel-runtime/core-js/map":45,"babel-runtime/core-js/object/get-prototype-of":50,"babel-runtime/helpers/classCallCheck":56,"babel-runtime/helpers/createClass":57,"babel-runtime/helpers/inherits":59,"babel-runtime/helpers/possibleConstructorReturn":60,"babel-runtime/helpers/typeof":61}],8:[function(_dereq_,module,exports){
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+
+var _getPrototypeOf = _dereq_('babel-runtime/core-js/object/get-prototype-of');
+
+var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
+
+var _classCallCheck2 = _dereq_('babel-runtime/helpers/classCallCheck');
+
+var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
+
+var _createClass2 = _dereq_('babel-runtime/helpers/createClass');
+
+var _createClass3 = _interopRequireDefault(_createClass2);
+
+var _possibleConstructorReturn2 = _dereq_('babel-runtime/helpers/possibleConstructorReturn');
+
+var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
+
+var _inherits2 = _dereq_('babel-runtime/helpers/inherits');
+
+var _inherits3 = _interopRequireDefault(_inherits2);
+
+var _EventEmitter2 = _dereq_('./EventEmitter');
+
+var _EventEmitter3 = _interopRequireDefault(_EventEmitter2);
+
+var _CoreManager = _dereq_('./CoreManager');
+
+var _CoreManager2 = _interopRequireDefault(_CoreManager);
+
+function _interopRequireDefault(obj) {
+ return obj && obj.__esModule ? obj : { default: obj };
+}
+
+/**
+ * Creates a new LiveQuery Subscription.
+ * Extends events.EventEmitter
+ * cloud functions.
+ *
+ * @constructor
+ * @param {string} id - subscription id
+ * @param {string} query - query to subscribe to
+ * @param {string} sessionToken - optional session token
+ *
+ *
+ * subscription.on('open', () => {
+ *
+ * });
Create Event - When a new ParseObject is created and it fulfills the ParseQuery you subscribe, + * you'll get this event. The object is the ParseObject which is created. + * + *
+ * subscription.on('create', (object) => { + * + * });+ * + *
Update Event - When an existing ParseObject which fulfills the ParseQuery you subscribe + * is updated (The ParseObject fulfills the ParseQuery before and after changes), + * you'll get this event. The object is the ParseObject which is updated. + * Its content is the latest value of the ParseObject. + * + *
+ * subscription.on('update', (object) => { + * + * });+ * + *
Enter Event - When an existing ParseObject's old value doesn't fulfill the ParseQuery + * but its new value fulfills the ParseQuery, you'll get this event. The object is the + * ParseObject which enters the ParseQuery. Its content is the latest value of the ParseObject. + * + *
+ * subscription.on('enter', (object) => { + * + * });+ * + * + *
Update Event - When an existing ParseObject's old value fulfills the ParseQuery but its new value + * doesn't fulfill the ParseQuery, you'll get this event. The object is the ParseObject + * which leaves the ParseQuery. Its content is the latest value of the ParseObject. + * + *
+ * subscription.on('leave', (object) => { + * + * });+ * + * + *
Delete Event - When an existing ParseObject which fulfills the ParseQuery is deleted, you'll + * get this event. The object is the ParseObject which is deleted. + * + *
+ * subscription.on('delete', (object) => { + * + * });+ * + * + *
Close Event - When the client loses the WebSocket connection to the LiveQuery + * server and we stop receiving events, you'll get this event. + * + *
+ * subscription.on('close', () => { + * + * });+ * + * + */ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ + +var Subscription = function (_EventEmitter) { + (0, _inherits3.default)(Subscription, _EventEmitter); + + function Subscription(id, query, sessionToken) { + (0, _classCallCheck3.default)(this, Subscription); + + var _this2 = (0, _possibleConstructorReturn3.default)(this, (Subscription.__proto__ || (0, _getPrototypeOf2.default)(Subscription)).call(this)); + + _this2.id = id; + _this2.query = query; + _this2.sessionToken = sessionToken; + return _this2; + } + + /** + * @method unsubscribe + */ + + (0, _createClass3.default)(Subscription, [{ + key: 'unsubscribe', + value: function () { + var _this3 = this; + + var _this = this; + _CoreManager2.default.getLiveQueryController().getDefaultLiveQueryClient().then(function (liveQueryClient) { + liveQueryClient.unsubscribe(_this); + _this.emit('close'); + _this3.resolve(); + }); + } + }]); + return Subscription; +}(_EventEmitter3.default); + +exports.default = Subscription; +},{"./CoreManager":3,"./EventEmitter":4,"babel-runtime/core-js/object/get-prototype-of":50,"babel-runtime/helpers/classCallCheck":56,"babel-runtime/helpers/createClass":57,"babel-runtime/helpers/inherits":59,"babel-runtime/helpers/possibleConstructorReturn":60}],9:[function(_dereq_,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _stringify = _dereq_('babel-runtime/core-js/json/stringify'); + +var _stringify2 = _interopRequireDefault(_stringify); + +var _typeof2 = _dereq_('babel-runtime/helpers/typeof'); + +var _typeof3 = _interopRequireDefault(_typeof2); + +exports.defaultState = defaultState; +exports.setServerData = setServerData; +exports.setPendingOp = setPendingOp; +exports.pushPendingState = pushPendingState; +exports.popPendingState = popPendingState; +exports.mergeFirstPendingState = mergeFirstPendingState; +exports.estimateAttribute = estimateAttribute; +exports.estimateAttributes = estimateAttributes; +exports.commitServerChanges = commitServerChanges; + +var _encode = _dereq_('./encode'); + +var _encode2 = _interopRequireDefault(_encode); + +var _ParseFile = _dereq_('./ParseFile'); + +var _ParseFile2 = _interopRequireDefault(_ParseFile); + +var _ParseObject = _dereq_('./ParseObject'); + +var _ParseObject2 = _interopRequireDefault(_ParseObject); + +var _ParsePromise = _dereq_('./ParsePromise'); + +var _ParsePromise2 = _interopRequireDefault(_ParsePromise); + +var _ParseRelation = _dereq_('./ParseRelation'); + +var _ParseRelation2 = _interopRequireDefault(_ParseRelation); + +var _TaskQueue = _dereq_('./TaskQueue'); + +var _TaskQueue2 = _interopRequireDefault(_TaskQueue); + +var _ParseOp = _dereq_('./ParseOp'); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { default: obj }; +} + +function defaultState() { + return { + serverData: {}, + pendingOps: [{}], + objectCache: {}, + tasks: new _TaskQueue2.default(), + existed: false + }; +} /** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + */ + +function setServerData(serverData, attributes) { + for (var _attr in attributes) { + if (typeof attributes[_attr] !== 'undefined') { + serverData[_attr] = attributes[_attr]; + } else { + delete serverData[_attr]; + } + } +} + +function setPendingOp(pendingOps, attr, op) { + var last = pendingOps.length - 1; + if (op) { + pendingOps[last][attr] = op; + } else { + delete pendingOps[last][attr]; + } +} + +function pushPendingState(pendingOps) { + pendingOps.push({}); +} + +function popPendingState(pendingOps) { + var first = pendingOps.shift(); + if (!pendingOps.length) { + pendingOps[0] = {}; + } + return first; +} + +function mergeFirstPendingState(pendingOps) { + var first = popPendingState(pendingOps); + var next = pendingOps[0]; + for (var _attr2 in first) { + if (next[_attr2] && first[_attr2]) { + var merged = next[_attr2].mergeWith(first[_attr2]); + if (merged) { + next[_attr2] = merged; + } + } else { + next[_attr2] = first[_attr2]; + } + } +} + +function estimateAttribute(serverData, pendingOps, className, id, attr) { + var value = serverData[attr]; + for (var i = 0; i < pendingOps.length; i++) { + if (pendingOps[i][attr]) { + if (pendingOps[i][attr] instanceof _ParseOp.RelationOp) { + if (id) { + value = pendingOps[i][attr].applyTo(value, { className: className, id: id }, attr); + } + } else { + value = pendingOps[i][attr].applyTo(value); + } + } + } + return value; +} + +function estimateAttributes(serverData, pendingOps, className, id) { + var data = {}; + var attr = void 0; + for (attr in serverData) { + data[attr] = serverData[attr]; + } + for (var i = 0; i < pendingOps.length; i++) { + for (attr in pendingOps[i]) { + if (pendingOps[i][attr] instanceof _ParseOp.RelationOp) { + if (id) { + data[attr] = pendingOps[i][attr].applyTo(data[attr], { className: className, id: id }, attr); + } + } else { + data[attr] = pendingOps[i][attr].applyTo(data[attr]); + } + } + } + return data; +} + +function commitServerChanges(serverData, objectCache, changes) { + for (var _attr3 in changes) { + var val = changes[_attr3]; + serverData[_attr3] = val; + if (val && (typeof val === 'undefined' ? 'undefined' : (0, _typeof3.default)(val)) === 'object' && !(val instanceof _ParseObject2.default) && !(val instanceof _ParseFile2.default) && !(val instanceof _ParseRelation2.default)) { + var json = (0, _encode2.default)(val, false, true); + objectCache[_attr3] = (0, _stringify2.default)(json); + } + } +} +},{"./ParseFile":14,"./ParseObject":18,"./ParseOp":19,"./ParsePromise":20,"./ParseRelation":22,"./TaskQueue":31,"./encode":36,"babel-runtime/core-js/json/stringify":44,"babel-runtime/helpers/typeof":61}],10:[function(_dereq_,module,exports){ +'use strict'; + +var _decode = _dereq_('./decode'); + +var _decode2 = _interopRequireDefault(_decode); + +var _encode = _dereq_('./encode'); + +var _encode2 = _interopRequireDefault(_encode); + +var _CoreManager = _dereq_('./CoreManager'); + +var _CoreManager2 = _interopRequireDefault(_CoreManager); + +var _InstallationController = _dereq_('./InstallationController'); + +var _InstallationController2 = _interopRequireDefault(_InstallationController); + +var _ParseOp = _dereq_('./ParseOp'); + +var ParseOp = _interopRequireWildcard(_ParseOp); + +var _RESTController = _dereq_('./RESTController'); + +var _RESTController2 = _interopRequireDefault(_RESTController); + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } else { + var newObj = {};if (obj != null) { + for (var key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; + } + }newObj.default = obj;return newObj; + } +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { default: obj }; +} + +/** + * Contains all Parse API classes and functions. + * @class Parse + * @static + */ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +var Parse = { + /** + * Call this method first to set up your authentication tokens for Parse. + * You can get your keys from the Data Browser on parse.com. + * @method initialize + * @param {String} applicationId Your Parse Application ID. + * @param {String} javaScriptKey (optional) Your Parse JavaScript Key (Not needed for parse-server) + * @param {String} masterKey (optional) Your Parse Master Key. (Node.js only!) + * @static + */ + initialize: function (applicationId, javaScriptKey) { + if ('browser' === 'browser' && _CoreManager2.default.get('IS_NODE')) { + console.log('It looks like you\'re using the browser version of the SDK in a ' + 'node.js environment. You should require(\'parse/node\') instead.'); + } + Parse._initialize(applicationId, javaScriptKey); + }, + _initialize: function (applicationId, javaScriptKey, masterKey) { + _CoreManager2.default.set('APPLICATION_ID', applicationId); + _CoreManager2.default.set('JAVASCRIPT_KEY', javaScriptKey); + _CoreManager2.default.set('MASTER_KEY', masterKey); + _CoreManager2.default.set('USE_MASTER_KEY', false); + } +}; + +/** These legacy setters may eventually be deprecated **/ +Object.defineProperty(Parse, 'applicationId', { + get: function () { + return _CoreManager2.default.get('APPLICATION_ID'); + }, + set: function (value) { + _CoreManager2.default.set('APPLICATION_ID', value); + } +}); +Object.defineProperty(Parse, 'javaScriptKey', { + get: function () { + return _CoreManager2.default.get('JAVASCRIPT_KEY'); + }, + set: function (value) { + _CoreManager2.default.set('JAVASCRIPT_KEY', value); + } +}); +Object.defineProperty(Parse, 'masterKey', { + get: function () { + return _CoreManager2.default.get('MASTER_KEY'); + }, + set: function (value) { + _CoreManager2.default.set('MASTER_KEY', value); + } +}); +Object.defineProperty(Parse, 'serverURL', { + get: function () { + return _CoreManager2.default.get('SERVER_URL'); + }, + set: function (value) { + _CoreManager2.default.set('SERVER_URL', value); + } +}); +Object.defineProperty(Parse, 'liveQueryServerURL', { + get: function () { + return _CoreManager2.default.get('LIVEQUERY_SERVER_URL'); + }, + set: function (value) { + _CoreManager2.default.set('LIVEQUERY_SERVER_URL', value); + } +}); +/** End setters **/ + +Parse.ACL = _dereq_('./ParseACL').default; +Parse.Analytics = _dereq_('./Analytics'); +Parse.Cloud = _dereq_('./Cloud'); +Parse.CoreManager = _dereq_('./CoreManager'); +Parse.Config = _dereq_('./ParseConfig').default; +Parse.Error = _dereq_('./ParseError').default; +Parse.FacebookUtils = _dereq_('./FacebookUtils').default; +Parse.File = _dereq_('./ParseFile').default; +Parse.GeoPoint = _dereq_('./ParseGeoPoint').default; +Parse.Installation = _dereq_('./ParseInstallation').default; +Parse.Object = _dereq_('./ParseObject').default; +Parse.Op = { + Set: ParseOp.SetOp, + Unset: ParseOp.UnsetOp, + Increment: ParseOp.IncrementOp, + Add: ParseOp.AddOp, + Remove: ParseOp.RemoveOp, + AddUnique: ParseOp.AddUniqueOp, + Relation: ParseOp.RelationOp +}; +Parse.Promise = _dereq_('./ParsePromise').default; +Parse.Push = _dereq_('./Push'); +Parse.Query = _dereq_('./ParseQuery').default; +Parse.Relation = _dereq_('./ParseRelation').default; +Parse.Role = _dereq_('./ParseRole').default; +Parse.Session = _dereq_('./ParseSession').default; +Parse.Storage = _dereq_('./Storage'); +Parse.User = _dereq_('./ParseUser').default; +Parse.LiveQuery = _dereq_('./ParseLiveQuery').default; +Parse.LiveQueryClient = _dereq_('./LiveQueryClient').default; + +Parse._request = function () { + for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + return _CoreManager2.default.getRESTController().request.apply(null, args); +}; +Parse._ajax = function () { + for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { + args[_key2] = arguments[_key2]; + } + + return _CoreManager2.default.getRESTController().ajax.apply(null, args); +}; +// We attempt to match the signatures of the legacy versions of these methods +Parse._decode = function (_, value) { + return (0, _decode2.default)(value); +}; +Parse._encode = function (value, _, disallowObjects) { + return (0, _encode2.default)(value, disallowObjects); +}; +Parse._getInstallationId = function () { + return _CoreManager2.default.getInstallationController().currentInstallationId(); +}; + +_CoreManager2.default.setInstallationController(_InstallationController2.default); +_CoreManager2.default.setRESTController(_RESTController2.default); + +// For legacy requires, of the form `var Parse = require('parse').Parse` +Parse.Parse = Parse; + +module.exports = Parse; +},{"./Analytics":1,"./Cloud":2,"./CoreManager":3,"./FacebookUtils":5,"./InstallationController":6,"./LiveQueryClient":7,"./ParseACL":11,"./ParseConfig":12,"./ParseError":13,"./ParseFile":14,"./ParseGeoPoint":15,"./ParseInstallation":16,"./ParseLiveQuery":17,"./ParseObject":18,"./ParseOp":19,"./ParsePromise":20,"./ParseQuery":21,"./ParseRelation":22,"./ParseRole":23,"./ParseSession":24,"./ParseUser":25,"./Push":26,"./RESTController":27,"./Storage":29,"./decode":35,"./encode":36}],11:[function(_dereq_,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _keys = _dereq_('babel-runtime/core-js/object/keys'); + +var _keys2 = _interopRequireDefault(_keys); + +var _typeof2 = _dereq_('babel-runtime/helpers/typeof'); + +var _typeof3 = _interopRequireDefault(_typeof2); + +var _classCallCheck2 = _dereq_('babel-runtime/helpers/classCallCheck'); + +var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); + +var _createClass2 = _dereq_('babel-runtime/helpers/createClass'); + +var _createClass3 = _interopRequireDefault(_createClass2); + +var _ParseRole = _dereq_('./ParseRole'); + +var _ParseRole2 = _interopRequireDefault(_ParseRole); + +var _ParseUser = _dereq_('./ParseUser'); + +var _ParseUser2 = _interopRequireDefault(_ParseUser); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { default: obj }; +} + +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + */ + +var PUBLIC_KEY = '*'; + +/** + * Creates a new ACL. + * If no argument is given, the ACL has no permissions for anyone. + * If the argument is a Parse.User, the ACL will have read and write + * permission for only that user. + * If the argument is any other JSON object, that object will be interpretted + * as a serialized ACL created with toJSON(). + * @class Parse.ACL + * @constructor + * + *
An ACL, or Access Control List can be added to any
+ * Parse.Object
to restrict access to only a subset of users
+ * of your application.
Parse.Error
.
+ * @param {String} message A detailed description of the error.
+ */
+var ParseError = function ParseError(code, message) {
+ (0, _classCallCheck3.default)(this, ParseError);
+
+ this.code = code;
+ this.message = message;
+};
+
+/**
+ * Error code indicating some error other than those enumerated here.
+ * @property OTHER_CAUSE
+ * @static
+ * @final
+ */
+
+exports.default = ParseError;
+ParseError.OTHER_CAUSE = -1;
+
+/**
+ * Error code indicating that something has gone wrong with the server.
+ * If you get this error code, it is Parse's fault. Contact us at
+ * https://parse.com/help
+ * @property INTERNAL_SERVER_ERROR
+ * @static
+ * @final
+ */
+ParseError.INTERNAL_SERVER_ERROR = 1;
+
+/**
+ * Error code indicating the connection to the Parse servers failed.
+ * @property CONNECTION_FAILED
+ * @static
+ * @final
+ */
+ParseError.CONNECTION_FAILED = 100;
+
+/**
+ * Error code indicating the specified object doesn't exist.
+ * @property OBJECT_NOT_FOUND
+ * @static
+ * @final
+ */
+ParseError.OBJECT_NOT_FOUND = 101;
+
+/**
+ * Error code indicating you tried to query with a datatype that doesn't
+ * support it, like exact matching an array or object.
+ * @property INVALID_QUERY
+ * @static
+ * @final
+ */
+ParseError.INVALID_QUERY = 102;
+
+/**
+ * Error code indicating a missing or invalid classname. Classnames are
+ * case-sensitive. They must start with a letter, and a-zA-Z0-9_ are the
+ * only valid characters.
+ * @property INVALID_CLASS_NAME
+ * @static
+ * @final
+ */
+ParseError.INVALID_CLASS_NAME = 103;
+
+/**
+ * Error code indicating an unspecified object id.
+ * @property MISSING_OBJECT_ID
+ * @static
+ * @final
+ */
+ParseError.MISSING_OBJECT_ID = 104;
+
+/**
+ * Error code indicating an invalid key name. Keys are case-sensitive. They
+ * must start with a letter, and a-zA-Z0-9_ are the only valid characters.
+ * @property INVALID_KEY_NAME
+ * @static
+ * @final
+ */
+ParseError.INVALID_KEY_NAME = 105;
+
+/**
+ * Error code indicating a malformed pointer. You should not see this unless
+ * you have been mucking about changing internal Parse code.
+ * @property INVALID_POINTER
+ * @static
+ * @final
+ */
+ParseError.INVALID_POINTER = 106;
+
+/**
+ * Error code indicating that badly formed JSON was received upstream. This
+ * either indicates you have done something unusual with modifying how
+ * things encode to JSON, or the network is failing badly.
+ * @property INVALID_JSON
+ * @static
+ * @final
+ */
+ParseError.INVALID_JSON = 107;
+
+/**
+ * Error code indicating that the feature you tried to access is only
+ * available internally for testing purposes.
+ * @property COMMAND_UNAVAILABLE
+ * @static
+ * @final
+ */
+ParseError.COMMAND_UNAVAILABLE = 108;
+
+/**
+ * You must call Parse.initialize before using the Parse library.
+ * @property NOT_INITIALIZED
+ * @static
+ * @final
+ */
+ParseError.NOT_INITIALIZED = 109;
+
+/**
+ * Error code indicating that a field was set to an inconsistent type.
+ * @property INCORRECT_TYPE
+ * @static
+ * @final
+ */
+ParseError.INCORRECT_TYPE = 111;
+
+/**
+ * Error code indicating an invalid channel name. A channel name is either
+ * an empty string (the broadcast channel) or contains only a-zA-Z0-9_
+ * characters and starts with a letter.
+ * @property INVALID_CHANNEL_NAME
+ * @static
+ * @final
+ */
+ParseError.INVALID_CHANNEL_NAME = 112;
+
+/**
+ * Error code indicating that push is misconfigured.
+ * @property PUSH_MISCONFIGURED
+ * @static
+ * @final
+ */
+ParseError.PUSH_MISCONFIGURED = 115;
+
+/**
+ * Error code indicating that the object is too large.
+ * @property OBJECT_TOO_LARGE
+ * @static
+ * @final
+ */
+ParseError.OBJECT_TOO_LARGE = 116;
+
+/**
+ * Error code indicating that the operation isn't allowed for clients.
+ * @property OPERATION_FORBIDDEN
+ * @static
+ * @final
+ */
+ParseError.OPERATION_FORBIDDEN = 119;
+
+/**
+ * Error code indicating the result was not found in the cache.
+ * @property CACHE_MISS
+ * @static
+ * @final
+ */
+ParseError.CACHE_MISS = 120;
+
+/**
+ * Error code indicating that an invalid key was used in a nested
+ * JSONObject.
+ * @property INVALID_NESTED_KEY
+ * @static
+ * @final
+ */
+ParseError.INVALID_NESTED_KEY = 121;
+
+/**
+ * Error code indicating that an invalid filename was used for ParseFile.
+ * A valid file name contains only a-zA-Z0-9_. characters and is between 1
+ * and 128 characters.
+ * @property INVALID_FILE_NAME
+ * @static
+ * @final
+ */
+ParseError.INVALID_FILE_NAME = 122;
+
+/**
+ * Error code indicating an invalid ACL was provided.
+ * @property INVALID_ACL
+ * @static
+ * @final
+ */
+ParseError.INVALID_ACL = 123;
+
+/**
+ * Error code indicating that the request timed out on the server. Typically
+ * this indicates that the request is too expensive to run.
+ * @property TIMEOUT
+ * @static
+ * @final
+ */
+ParseError.TIMEOUT = 124;
+
+/**
+ * Error code indicating that the email address was invalid.
+ * @property INVALID_EMAIL_ADDRESS
+ * @static
+ * @final
+ */
+ParseError.INVALID_EMAIL_ADDRESS = 125;
+
+/**
+ * Error code indicating a missing content type.
+ * @property MISSING_CONTENT_TYPE
+ * @static
+ * @final
+ */
+ParseError.MISSING_CONTENT_TYPE = 126;
+
+/**
+ * Error code indicating a missing content length.
+ * @property MISSING_CONTENT_LENGTH
+ * @static
+ * @final
+ */
+ParseError.MISSING_CONTENT_LENGTH = 127;
+
+/**
+ * Error code indicating an invalid content length.
+ * @property INVALID_CONTENT_LENGTH
+ * @static
+ * @final
+ */
+ParseError.INVALID_CONTENT_LENGTH = 128;
+
+/**
+ * Error code indicating a file that was too large.
+ * @property FILE_TOO_LARGE
+ * @static
+ * @final
+ */
+ParseError.FILE_TOO_LARGE = 129;
+
+/**
+ * Error code indicating an error saving a file.
+ * @property FILE_SAVE_ERROR
+ * @static
+ * @final
+ */
+ParseError.FILE_SAVE_ERROR = 130;
+
+/**
+ * Error code indicating that a unique field was given a value that is
+ * already taken.
+ * @property DUPLICATE_VALUE
+ * @static
+ * @final
+ */
+ParseError.DUPLICATE_VALUE = 137;
+
+/**
+ * Error code indicating that a role's name is invalid.
+ * @property INVALID_ROLE_NAME
+ * @static
+ * @final
+ */
+ParseError.INVALID_ROLE_NAME = 139;
+
+/**
+ * Error code indicating that an application quota was exceeded. Upgrade to
+ * resolve.
+ * @property EXCEEDED_QUOTA
+ * @static
+ * @final
+ */
+ParseError.EXCEEDED_QUOTA = 140;
+
+/**
+ * Error code indicating that a Cloud Code script failed.
+ * @property SCRIPT_FAILED
+ * @static
+ * @final
+ */
+ParseError.SCRIPT_FAILED = 141;
+
+/**
+ * Error code indicating that a Cloud Code validation failed.
+ * @property VALIDATION_ERROR
+ * @static
+ * @final
+ */
+ParseError.VALIDATION_ERROR = 142;
+
+/**
+ * Error code indicating that invalid image data was provided.
+ * @property INVALID_IMAGE_DATA
+ * @static
+ * @final
+ */
+ParseError.INVALID_IMAGE_DATA = 143;
+
+/**
+ * Error code indicating an unsaved file.
+ * @property UNSAVED_FILE_ERROR
+ * @static
+ * @final
+ */
+ParseError.UNSAVED_FILE_ERROR = 151;
+
+/**
+ * Error code indicating an invalid push time.
+ * @property INVALID_PUSH_TIME_ERROR
+ * @static
+ * @final
+ */
+ParseError.INVALID_PUSH_TIME_ERROR = 152;
+
+/**
+ * Error code indicating an error deleting a file.
+ * @property FILE_DELETE_ERROR
+ * @static
+ * @final
+ */
+ParseError.FILE_DELETE_ERROR = 153;
+
+/**
+ * Error code indicating that the application has exceeded its request
+ * limit.
+ * @property REQUEST_LIMIT_EXCEEDED
+ * @static
+ * @final
+ */
+ParseError.REQUEST_LIMIT_EXCEEDED = 155;
+
+/**
+ * Error code indicating an invalid event name.
+ * @property INVALID_EVENT_NAME
+ * @static
+ * @final
+ */
+ParseError.INVALID_EVENT_NAME = 160;
+
+/**
+ * Error code indicating that the username is missing or empty.
+ * @property USERNAME_MISSING
+ * @static
+ * @final
+ */
+ParseError.USERNAME_MISSING = 200;
+
+/**
+ * Error code indicating that the password is missing or empty.
+ * @property PASSWORD_MISSING
+ * @static
+ * @final
+ */
+ParseError.PASSWORD_MISSING = 201;
+
+/**
+ * Error code indicating that the username has already been taken.
+ * @property USERNAME_TAKEN
+ * @static
+ * @final
+ */
+ParseError.USERNAME_TAKEN = 202;
+
+/**
+ * Error code indicating that the email has already been taken.
+ * @property EMAIL_TAKEN
+ * @static
+ * @final
+ */
+ParseError.EMAIL_TAKEN = 203;
+
+/**
+ * Error code indicating that the email is missing, but must be specified.
+ * @property EMAIL_MISSING
+ * @static
+ * @final
+ */
+ParseError.EMAIL_MISSING = 204;
+
+/**
+ * Error code indicating that a user with the specified email was not found.
+ * @property EMAIL_NOT_FOUND
+ * @static
+ * @final
+ */
+ParseError.EMAIL_NOT_FOUND = 205;
+
+/**
+ * Error code indicating that a user object without a valid session could
+ * not be altered.
+ * @property SESSION_MISSING
+ * @static
+ * @final
+ */
+ParseError.SESSION_MISSING = 206;
+
+/**
+ * Error code indicating that a user can only be created through signup.
+ * @property MUST_CREATE_USER_THROUGH_SIGNUP
+ * @static
+ * @final
+ */
+ParseError.MUST_CREATE_USER_THROUGH_SIGNUP = 207;
+
+/**
+ * Error code indicating that an an account being linked is already linked
+ * to another user.
+ * @property ACCOUNT_ALREADY_LINKED
+ * @static
+ * @final
+ */
+ParseError.ACCOUNT_ALREADY_LINKED = 208;
+
+/**
+ * Error code indicating that the current session token is invalid.
+ * @property INVALID_SESSION_TOKEN
+ * @static
+ * @final
+ */
+ParseError.INVALID_SESSION_TOKEN = 209;
+
+/**
+ * Error code indicating that a user cannot be linked to an account because
+ * that account's id could not be found.
+ * @property LINKED_ID_MISSING
+ * @static
+ * @final
+ */
+ParseError.LINKED_ID_MISSING = 250;
+
+/**
+ * Error code indicating that a user with a linked (e.g. Facebook) account
+ * has an invalid session.
+ * @property INVALID_LINKED_SESSION
+ * @static
+ * @final
+ */
+ParseError.INVALID_LINKED_SESSION = 251;
+
+/**
+ * Error code indicating that a service being linked (e.g. Facebook or
+ * Twitter) is unsupported.
+ * @property UNSUPPORTED_SERVICE
+ * @static
+ * @final
+ */
+ParseError.UNSUPPORTED_SERVICE = 252;
+
+/**
+ * Error code indicating that there were multiple errors. Aggregate errors
+ * have an "errors" property, which is an array of error objects with more
+ * detail about each error that occurred.
+ * @property AGGREGATE_ERROR
+ * @static
+ * @final
+ */
+ParseError.AGGREGATE_ERROR = 600;
+
+/**
+ * Error code indicating the client was unable to read an input file.
+ * @property FILE_READ_ERROR
+ * @static
+ * @final
+ */
+ParseError.FILE_READ_ERROR = 601;
+
+/**
+ * Error code indicating a real error code is unavailable because
+ * we had to use an XDomainRequest object to allow CORS requests in
+ * Internet Explorer, which strips the body from HTTP responses that have
+ * a non-2XX status code.
+ * @property X_DOMAIN_REQUEST
+ * @static
+ * @final
+ */
+ParseError.X_DOMAIN_REQUEST = 602;
+},{"babel-runtime/helpers/classCallCheck":56}],14:[function(_dereq_,module,exports){
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+
+var _classCallCheck2 = _dereq_('babel-runtime/helpers/classCallCheck');
+
+var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
+
+var _createClass2 = _dereq_('babel-runtime/helpers/createClass');
+
+var _createClass3 = _interopRequireDefault(_createClass2);
+
+var _CoreManager = _dereq_('./CoreManager');
+
+var _CoreManager2 = _interopRequireDefault(_CoreManager);
+
+var _ParsePromise = _dereq_('./ParsePromise');
+
+var _ParsePromise2 = _interopRequireDefault(_ParsePromise);
+
+function _interopRequireDefault(obj) {
+ return obj && obj.__esModule ? obj : { default: obj };
+}
+
+/**
+ * Copyright (c) 2015-present, Parse, LLC.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ *
+ *
+ */
+
+var dataUriRegexp = /^data:([a-zA-Z]*\/[a-zA-Z+.-]*);(charset=[a-zA-Z0-9\-\/\s]*,)?base64,/;
+
+function b64Digit(number) {
+ if (number < 26) {
+ return String.fromCharCode(65 + number);
+ }
+ if (number < 52) {
+ return String.fromCharCode(97 + (number - 26));
+ }
+ if (number < 62) {
+ return String.fromCharCode(48 + (number - 52));
+ }
+ if (number === 62) {
+ return '+';
+ }
+ if (number === 63) {
+ return '/';
+ }
+ throw new TypeError('Tried to encode large digit ' + number + ' in base64.');
+}
+
+/**
+ * A Parse.File is a local representation of a file that is saved to the Parse
+ * cloud.
+ * @class Parse.File
+ * @constructor
+ * @param name {String} The file's name. This will be prefixed by a unique
+ * value once the file has finished saving. The file name must begin with
+ * an alphanumeric character, and consist of alphanumeric characters,
+ * periods, spaces, underscores, or dashes.
+ * @param data {Array} The data for the file, as either:
+ * 1. an Array of byte value Numbers, or
+ * 2. an Object like { base64: "..." } with a base64-encoded String.
+ * 3. a File object selected with a file upload control. (3) only works
+ * in Firefox 3.6+, Safari 6.0.2+, Chrome 7+, and IE 10+.
+ * For example:+ * var fileUploadControl = $("#profilePhotoFileUpload")[0]; + * if (fileUploadControl.files.length > 0) { + * var file = fileUploadControl.files[0]; + * var name = "photo.jpg"; + * var parseFile = new Parse.File(name, file); + * parseFile.save().then(function() { + * // The file has been saved to Parse. + * }, function(error) { + * // The file either could not be read, or could not be saved to Parse. + * }); + * }+ * @param type {String} Optional Content-Type header to use for the file. If + * this is omitted, the content type will be inferred from the name's + * extension. + */ + +var ParseFile = function () { + function ParseFile(name, data, type) { + (0, _classCallCheck3.default)(this, ParseFile); + + var specifiedType = type || ''; + + this._name = name; + + if (data !== undefined) { + if (Array.isArray(data)) { + this._source = { + format: 'base64', + base64: ParseFile.encodeBase64(data), + type: specifiedType + }; + } else if (typeof File !== 'undefined' && data instanceof File) { + this._source = { + format: 'file', + file: data, + type: specifiedType + }; + } else if (data && typeof data.base64 === 'string') { + var _base = data.base64; + var commaIndex = _base.indexOf(','); + + if (commaIndex !== -1) { + var matches = dataUriRegexp.exec(_base.slice(0, commaIndex + 1)); + // if data URI with type and charset, there will be 4 matches. + this._source = { + format: 'base64', + base64: _base.slice(commaIndex + 1), + type: matches[1] + }; + } else { + this._source = { + format: 'base64', + base64: _base, + type: specifiedType + }; + } + } else { + throw new TypeError('Cannot create a Parse.File with that data.'); + } + } + } + + /** + * Gets the name of the file. Before save is called, this is the filename + * given by the user. After save is called, that name gets prefixed with a + * unique identifier. + * @method name + * @return {String} + */ + + (0, _createClass3.default)(ParseFile, [{ + key: 'name', + value: function () { + return this._name; + } + + /** + * Gets the url of the file. It is only available after you save the file or + * after you get the file from a Parse.Object. + * @method url + * @param {Object} options An object to specify url options + * @return {String} + */ + + }, { + key: 'url', + value: function (options) { + options = options || {}; + if (!this._url) { + return; + } + if (options.forceSecure) { + return this._url.replace(/^http:\/\//i, 'https://'); + } else { + return this._url; + } + } + + /** + * Saves the file to the Parse cloud. + * @method save + * @param {Object} options A Backbone-style options object. + * @return {Parse.Promise} Promise that is resolved when the save finishes. + */ + + }, { + key: 'save', + value: function (options) { + var _this = this; + + options = options || {}; + var controller = _CoreManager2.default.getFileController(); + if (!this._previousSave) { + if (this._source.format === 'file') { + this._previousSave = controller.saveFile(this._name, this._source).then(function (res) { + _this._name = res.name; + _this._url = res.url; + return _this; + }); + } else { + this._previousSave = controller.saveBase64(this._name, this._source).then(function (res) { + _this._name = res.name; + _this._url = res.url; + return _this; + }); + } + } + if (this._previousSave) { + return this._previousSave._thenRunCallbacks(options); + } + } + }, { + key: 'toJSON', + value: function () { + return { + __type: 'File', + name: this._name, + url: this._url + }; + } + }, { + key: 'equals', + value: function (other) { + if (this === other) { + return true; + } + // Unsaved Files are never equal, since they will be saved to different URLs + return other instanceof ParseFile && this.name() === other.name() && this.url() === other.url() && typeof this.url() !== 'undefined'; + } + }], [{ + key: 'fromJSON', + value: function (obj) { + if (obj.__type !== 'File') { + throw new TypeError('JSON object does not represent a ParseFile'); + } + var file = new ParseFile(obj.name); + file._url = obj.url; + return file; + } + }, { + key: 'encodeBase64', + value: function (bytes) { + var chunks = []; + chunks.length = Math.ceil(bytes.length / 3); + for (var i = 0; i < chunks.length; i++) { + var b1 = bytes[i * 3]; + var b2 = bytes[i * 3 + 1] || 0; + var b3 = bytes[i * 3 + 2] || 0; + + var has2 = i * 3 + 1 < bytes.length; + var has3 = i * 3 + 2 < bytes.length; + + chunks[i] = [b64Digit(b1 >> 2 & 0x3F), b64Digit(b1 << 4 & 0x30 | b2 >> 4 & 0x0F), has2 ? b64Digit(b2 << 2 & 0x3C | b3 >> 6 & 0x03) : '=', has3 ? b64Digit(b3 & 0x3F) : '='].join(''); + } + + return chunks.join(''); + } + }]); + return ParseFile; +}(); + +exports.default = ParseFile; + +var DefaultController = { + saveFile: function (name, source) { + if (source.format !== 'file') { + throw new Error('saveFile can only be used with File-type sources.'); + } + // To directly upload a File, we use a REST-style AJAX request + var headers = { + 'X-Parse-Application-ID': _CoreManager2.default.get('APPLICATION_ID'), + 'X-Parse-JavaScript-Key': _CoreManager2.default.get('JAVASCRIPT_KEY'), + 'Content-Type': source.type || (source.file ? source.file.type : null) + }; + var url = _CoreManager2.default.get('SERVER_URL'); + if (url[url.length - 1] !== '/') { + url += '/'; + } + url += 'files/' + name; + return _CoreManager2.default.getRESTController().ajax('POST', url, source.file, headers); + }, + + saveBase64: function (name, source) { + if (source.format !== 'base64') { + throw new Error('saveBase64 can only be used with Base64-type sources.'); + } + var data = { + base64: source.base64 + }; + if (source.type) { + data._ContentType = source.type; + } + + return _CoreManager2.default.getRESTController().request('POST', 'files/' + name, data); + } +}; + +_CoreManager2.default.setFileController(DefaultController); +},{"./CoreManager":3,"./ParsePromise":20,"babel-runtime/helpers/classCallCheck":56,"babel-runtime/helpers/createClass":57}],15:[function(_dereq_,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _typeof2 = _dereq_('babel-runtime/helpers/typeof'); + +var _typeof3 = _interopRequireDefault(_typeof2); + +var _classCallCheck2 = _dereq_('babel-runtime/helpers/classCallCheck'); + +var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); + +var _createClass2 = _dereq_('babel-runtime/helpers/createClass'); + +var _createClass3 = _interopRequireDefault(_createClass2); + +var _ParsePromise = _dereq_('./ParsePromise'); + +var _ParsePromise2 = _interopRequireDefault(_ParsePromise); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { default: obj }; +} + +/** + * Creates a new GeoPoint with any of the following forms:
+ * new GeoPoint(otherGeoPoint) + * new GeoPoint(30, 30) + * new GeoPoint([30, 30]) + * new GeoPoint({latitude: 30, longitude: 30}) + * new GeoPoint() // defaults to (0, 0) + *+ * @class Parse.GeoPoint + * @constructor + * + *
Represents a latitude / longitude point that may be associated + * with a key in a ParseObject or used as a reference point for geo queries. + * This allows proximity-based queries on the key.
+ * + *Only one key in a class may contain a GeoPoint.
+ * + *Example:
+ * var point = new Parse.GeoPoint(30.0, -20.0); + * var object = new Parse.Object("PlaceObject"); + * object.set("location", point); + * object.save();+ */ +var ParseGeoPoint = function () { + function ParseGeoPoint(arg1, arg2) { + (0, _classCallCheck3.default)(this, ParseGeoPoint); + + if (Array.isArray(arg1)) { + ParseGeoPoint._validate(arg1[0], arg1[1]); + this._latitude = arg1[0]; + this._longitude = arg1[1]; + } else if ((typeof arg1 === 'undefined' ? 'undefined' : (0, _typeof3.default)(arg1)) === 'object') { + ParseGeoPoint._validate(arg1.latitude, arg1.longitude); + this._latitude = arg1.latitude; + this._longitude = arg1.longitude; + } else if (typeof arg1 === 'number' && typeof arg2 === 'number') { + ParseGeoPoint._validate(arg1, arg2); + this._latitude = arg1; + this._longitude = arg2; + } else { + this._latitude = 0; + this._longitude = 0; + } + } + + /** + * North-south portion of the coordinate, in range [-90, 90]. + * Throws an exception if set out of range in a modern browser. + * @property latitude + * @type Number + */ + + (0, _createClass3.default)(ParseGeoPoint, [{ + key: 'toJSON', + + /** + * Returns a JSON representation of the GeoPoint, suitable for Parse. + * @method toJSON + * @return {Object} + */ + value: function () { + ParseGeoPoint._validate(this._latitude, this._longitude); + return { + __type: 'GeoPoint', + latitude: this._latitude, + longitude: this._longitude + }; + } + }, { + key: 'equals', + value: function (other) { + return other instanceof ParseGeoPoint && this.latitude === other.latitude && this.longitude === other.longitude; + } + + /** + * Returns the distance from this GeoPoint to another in radians. + * @method radiansTo + * @param {Parse.GeoPoint} point the other Parse.GeoPoint. + * @return {Number} + */ + + }, { + key: 'radiansTo', + value: function (point) { + var d2r = Math.PI / 180.0; + var lat1rad = this.latitude * d2r; + var long1rad = this.longitude * d2r; + var lat2rad = point.latitude * d2r; + var long2rad = point.longitude * d2r; + + var sinDeltaLatDiv2 = Math.sin((lat1rad - lat2rad) / 2); + var sinDeltaLongDiv2 = Math.sin((long1rad - long2rad) / 2); + // Square of half the straight line chord distance between both points. + var a = sinDeltaLatDiv2 * sinDeltaLatDiv2 + Math.cos(lat1rad) * Math.cos(lat2rad) * sinDeltaLongDiv2 * sinDeltaLongDiv2; + a = Math.min(1.0, a); + return 2 * Math.asin(Math.sqrt(a)); + } + + /** + * Returns the distance from this GeoPoint to another in kilometers. + * @method kilometersTo + * @param {Parse.GeoPoint} point the other Parse.GeoPoint. + * @return {Number} + */ + + }, { + key: 'kilometersTo', + value: function (point) { + return this.radiansTo(point) * 6371.0; + } + + /** + * Returns the distance from this GeoPoint to another in miles. + * @method milesTo + * @param {Parse.GeoPoint} point the other Parse.GeoPoint. + * @return {Number} + */ + + }, { + key: 'milesTo', + value: function (point) { + return this.radiansTo(point) * 3958.8; + } + + /** + * Throws an exception if the given lat-long is out of bounds. + */ + + }, { + key: 'latitude', + get: function () { + return this._latitude; + }, + set: function (val) { + ParseGeoPoint._validate(val, this.longitude); + this._latitude = val; + } + + /** + * East-west portion of the coordinate, in range [-180, 180]. + * Throws if set out of range in a modern browser. + * @property longitude + * @type Number + */ + + }, { + key: 'longitude', + get: function () { + return this._longitude; + }, + set: function (val) { + ParseGeoPoint._validate(this.latitude, val); + this._longitude = val; + } + }], [{ + key: '_validate', + value: function (latitude, longitude) { + if (latitude !== latitude || longitude !== longitude) { + throw new TypeError('GeoPoint latitude and longitude must be valid numbers'); + } + if (latitude < -90.0) { + throw new TypeError('GeoPoint latitude out of bounds: ' + latitude + ' < -90.0.'); + } + if (latitude > 90.0) { + throw new TypeError('GeoPoint latitude out of bounds: ' + latitude + ' > 90.0.'); + } + if (longitude < -180.0) { + throw new TypeError('GeoPoint longitude out of bounds: ' + longitude + ' < -180.0.'); + } + if (longitude > 180.0) { + throw new TypeError('GeoPoint longitude out of bounds: ' + longitude + ' > 180.0.'); + } + } + + /** + * Creates a GeoPoint with the user's current location, if available. + * Calls options.success with a new GeoPoint instance or calls options.error. + * @method current + * @param {Object} options An object with success and error callbacks. + * @static + */ + + }, { + key: 'current', + value: function (options) { + var promise = new _ParsePromise2.default(); + navigator.geolocation.getCurrentPosition(function (location) { + promise.resolve(new ParseGeoPoint(location.coords.latitude, location.coords.longitude)); + }, function (error) { + promise.reject(error); + }); + + return promise._thenRunCallbacks(options); + } + }]); + return ParseGeoPoint; +}(); /** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + */ + +exports.default = ParseGeoPoint; +},{"./ParsePromise":20,"babel-runtime/helpers/classCallCheck":56,"babel-runtime/helpers/createClass":57,"babel-runtime/helpers/typeof":61}],16:[function(_dereq_,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _typeof2 = _dereq_('babel-runtime/helpers/typeof'); + +var _typeof3 = _interopRequireDefault(_typeof2); + +var _getPrototypeOf = _dereq_('babel-runtime/core-js/object/get-prototype-of'); + +var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); + +var _classCallCheck2 = _dereq_('babel-runtime/helpers/classCallCheck'); + +var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); + +var _possibleConstructorReturn2 = _dereq_('babel-runtime/helpers/possibleConstructorReturn'); + +var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); + +var _inherits2 = _dereq_('babel-runtime/helpers/inherits'); + +var _inherits3 = _interopRequireDefault(_inherits2); + +var _ParseObject2 = _dereq_('./ParseObject'); + +var _ParseObject3 = _interopRequireDefault(_ParseObject2); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { default: obj }; +} + +var Installation = function (_ParseObject) { + (0, _inherits3.default)(Installation, _ParseObject); + + function Installation(attributes) { + (0, _classCallCheck3.default)(this, Installation); + + var _this = (0, _possibleConstructorReturn3.default)(this, (Installation.__proto__ || (0, _getPrototypeOf2.default)(Installation)).call(this, '_Installation')); + + if (attributes && (typeof attributes === 'undefined' ? 'undefined' : (0, _typeof3.default)(attributes)) === 'object') { + if (!_this.set(attributes || {})) { + throw new Error('Can\'t create an invalid Session'); + } + } + return _this; + } + + return Installation; +}(_ParseObject3.default); /** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + */ + +exports.default = Installation; + +_ParseObject3.default.registerSubclass('_Installation', Installation); +},{"./ParseObject":18,"babel-runtime/core-js/object/get-prototype-of":50,"babel-runtime/helpers/classCallCheck":56,"babel-runtime/helpers/inherits":59,"babel-runtime/helpers/possibleConstructorReturn":60,"babel-runtime/helpers/typeof":61}],17:[function(_dereq_,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _EventEmitter = _dereq_('./EventEmitter'); + +var _EventEmitter2 = _interopRequireDefault(_EventEmitter); + +var _LiveQueryClient = _dereq_('./LiveQueryClient'); + +var _LiveQueryClient2 = _interopRequireDefault(_LiveQueryClient); + +var _CoreManager = _dereq_('./CoreManager'); + +var _CoreManager2 = _interopRequireDefault(_CoreManager); + +var _ParsePromise = _dereq_('./ParsePromise'); + +var _ParsePromise2 = _interopRequireDefault(_ParsePromise); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { default: obj }; +} + +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + */ + +function open() { + var LiveQueryController = _CoreManager2.default.getLiveQueryController(); + LiveQueryController.open(); +} + +function close() { + var LiveQueryController = _CoreManager2.default.getLiveQueryController(); + LiveQueryController.close(); +} + +/** + * + * We expose three events to help you monitor the status of the WebSocket connection: + * + *
Open - When we establish the WebSocket connection to the LiveQuery server, you'll get this event. + * + *
+ * Parse.LiveQuery.on('open', () => { + * + * });+ * + *
Close - When we lose the WebSocket connection to the LiveQuery server, you'll get this event. + * + *
+ * Parse.LiveQuery.on('close', () => { + * + * });+ * + *
Error - When some network error or LiveQuery server error happens, you'll get this event. + * + *
+ * Parse.LiveQuery.on('error', (error) => { + * + * });+ * + * @class Parse.LiveQuery + * @static + * + */ +var LiveQuery = new _EventEmitter2.default(); + +/** + * After open is called, the LiveQuery will try to send a connect request + * to the LiveQuery server. + * + * @method open + */ +LiveQuery.open = open; + +/** + * When you're done using LiveQuery, you can call Parse.LiveQuery.close(). + * This function will close the WebSocket connection to the LiveQuery server, + * cancel the auto reconnect, and unsubscribe all subscriptions based on it. + * If you call query.subscribe() after this, we'll create a new WebSocket + * connection to the LiveQuery server. + * + * @method close + */ + +LiveQuery.close = close; +// Register a default onError callback to make sure we do not crash on error +LiveQuery.on('error', function () {}); + +exports.default = LiveQuery; + +function getSessionToken() { + var controller = _CoreManager2.default.getUserController(); + return controller.currentUserAsync().then(function (currentUser) { + return currentUser ? currentUser.getSessionToken() : undefined; + }); +} + +function getLiveQueryClient() { + return _CoreManager2.default.getLiveQueryController().getDefaultLiveQueryClient(); +} + +var defaultLiveQueryClient = void 0; +var DefaultLiveQueryController = { + setDefaultLiveQueryClient: function (liveQueryClient) { + defaultLiveQueryClient = liveQueryClient; + }, + getDefaultLiveQueryClient: function () { + if (defaultLiveQueryClient) { + return _ParsePromise2.default.as(defaultLiveQueryClient); + } + + return getSessionToken().then(function (sessionToken) { + var liveQueryServerURL = _CoreManager2.default.get('LIVEQUERY_SERVER_URL'); + + if (liveQueryServerURL && liveQueryServerURL.indexOf('ws') !== 0) { + throw new Error('You need to set a proper Parse LiveQuery server url before using LiveQueryClient'); + } + + // If we can not find Parse.liveQueryServerURL, we try to extract it from Parse.serverURL + if (!liveQueryServerURL) { + var tempServerURL = _CoreManager2.default.get('SERVER_URL'); + var protocol = 'ws://'; + // If Parse is being served over SSL/HTTPS, ensure LiveQuery Server uses 'wss://' prefix + if (tempServerURL.indexOf('https') === 0) { + protocol = 'wss://'; + } + var host = tempServerURL.replace(/^https?:\/\//, ''); + liveQueryServerURL = protocol + host; + _CoreManager2.default.set('LIVEQUERY_SERVER_URL', liveQueryServerURL); + } + + var applicationId = _CoreManager2.default.get('APPLICATION_ID'); + var javascriptKey = _CoreManager2.default.get('JAVASCRIPT_KEY'); + var masterKey = _CoreManager2.default.get('MASTER_KEY'); + // Get currentUser sessionToken if possible + defaultLiveQueryClient = new _LiveQueryClient2.default({ + applicationId: applicationId, + serverURL: liveQueryServerURL, + javascriptKey: javascriptKey, + masterKey: masterKey, + sessionToken: sessionToken + }); + // Register a default onError callback to make sure we do not crash on error + // Cannot create these events on a nested way because of EventEmiiter from React Native + defaultLiveQueryClient.on('error', function (error) { + LiveQuery.emit('error', error); + }); + defaultLiveQueryClient.on('open', function () { + LiveQuery.emit('open'); + }); + defaultLiveQueryClient.on('close', function () { + LiveQuery.emit('close'); + }); + + return defaultLiveQueryClient; + }); + }, + open: function () { + var _this = this; + + getLiveQueryClient().then(function (liveQueryClient) { + _this.resolve(liveQueryClient.open()); + }); + }, + close: function () { + var _this2 = this; + + getLiveQueryClient().then(function (liveQueryClient) { + _this2.resolve(liveQueryClient.close()); + }); + }, + subscribe: function (query) { + var _this3 = this; + + var subscriptionWrap = new _EventEmitter2.default(); + + getLiveQueryClient().then(function (liveQueryClient) { + if (liveQueryClient.shouldOpen()) { + liveQueryClient.open(); + } + var promiseSessionToken = getSessionToken(); + // new event emitter + return promiseSessionToken.then(function (sessionToken) { + + var subscription = liveQueryClient.subscribe(query, sessionToken); + // enter, leave create, etc + + subscriptionWrap.id = subscription.id; + subscriptionWrap.query = subscription.query; + subscriptionWrap.sessionToken = subscription.sessionToken; + subscriptionWrap.unsubscribe = subscription.unsubscribe; + // Cannot create these events on a nested way because of EventEmiiter from React Native + subscription.on('open', function () { + subscriptionWrap.emit('open'); + }); + subscription.on('create', function (object) { + subscriptionWrap.emit('create', object); + }); + subscription.on('update', function (object) { + subscriptionWrap.emit('update', object); + }); + subscription.on('enter', function (object) { + subscriptionWrap.emit('enter', object); + }); + subscription.on('leave', function (object) { + subscriptionWrap.emit('leave', object); + }); + subscription.on('delete', function (object) { + subscriptionWrap.emit('delete', object); + }); + + _this3.resolve(); + }); + }); + return subscriptionWrap; + }, + unsubscribe: function (subscription) { + var _this4 = this; + + getLiveQueryClient().then(function (liveQueryClient) { + _this4.resolve(liveQueryClient.unsubscribe(subscription)); + }); + }, + _clearCachedDefaultClient: function () { + defaultLiveQueryClient = null; + } +}; + +_CoreManager2.default.setLiveQueryController(DefaultLiveQueryController); +},{"./CoreManager":3,"./EventEmitter":4,"./LiveQueryClient":7,"./ParsePromise":20}],18:[function(_dereq_,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _defineProperty = _dereq_('babel-runtime/core-js/object/define-property'); + +var _defineProperty2 = _interopRequireDefault(_defineProperty); + +var _create = _dereq_('babel-runtime/core-js/object/create'); + +var _create2 = _interopRequireDefault(_create); + +var _freeze = _dereq_('babel-runtime/core-js/object/freeze'); + +var _freeze2 = _interopRequireDefault(_freeze); + +var _stringify = _dereq_('babel-runtime/core-js/json/stringify'); + +var _stringify2 = _interopRequireDefault(_stringify); + +var _keys = _dereq_('babel-runtime/core-js/object/keys'); + +var _keys2 = _interopRequireDefault(_keys); + +var _typeof2 = _dereq_('babel-runtime/helpers/typeof'); + +var _typeof3 = _interopRequireDefault(_typeof2); + +var _classCallCheck2 = _dereq_('babel-runtime/helpers/classCallCheck'); + +var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); + +var _createClass2 = _dereq_('babel-runtime/helpers/createClass'); + +var _createClass3 = _interopRequireDefault(_createClass2); + +var _CoreManager = _dereq_('./CoreManager'); + +var _CoreManager2 = _interopRequireDefault(_CoreManager); + +var _canBeSerialized = _dereq_('./canBeSerialized'); + +var _canBeSerialized2 = _interopRequireDefault(_canBeSerialized); + +var _decode = _dereq_('./decode'); + +var _decode2 = _interopRequireDefault(_decode); + +var _encode = _dereq_('./encode'); + +var _encode2 = _interopRequireDefault(_encode); + +var _equals = _dereq_('./equals'); + +var _equals2 = _interopRequireDefault(_equals); + +var _escape2 = _dereq_('./escape'); + +var _escape3 = _interopRequireDefault(_escape2); + +var _ParseACL = _dereq_('./ParseACL'); + +var _ParseACL2 = _interopRequireDefault(_ParseACL); + +var _parseDate = _dereq_('./parseDate'); + +var _parseDate2 = _interopRequireDefault(_parseDate); + +var _ParseError = _dereq_('./ParseError'); + +var _ParseError2 = _interopRequireDefault(_ParseError); + +var _ParseFile = _dereq_('./ParseFile'); + +var _ParseFile2 = _interopRequireDefault(_ParseFile); + +var _ParseOp = _dereq_('./ParseOp'); + +var _ParsePromise = _dereq_('./ParsePromise'); + +var _ParsePromise2 = _interopRequireDefault(_ParsePromise); + +var _ParseQuery = _dereq_('./ParseQuery'); + +var _ParseQuery2 = _interopRequireDefault(_ParseQuery); + +var _ParseRelation = _dereq_('./ParseRelation'); + +var _ParseRelation2 = _interopRequireDefault(_ParseRelation); + +var _SingleInstanceStateController = _dereq_('./SingleInstanceStateController'); + +var SingleInstanceStateController = _interopRequireWildcard(_SingleInstanceStateController); + +var _unique = _dereq_('./unique'); + +var _unique2 = _interopRequireDefault(_unique); + +var _UniqueInstanceStateController = _dereq_('./UniqueInstanceStateController'); + +var UniqueInstanceStateController = _interopRequireWildcard(_UniqueInstanceStateController); + +var _unsavedChildren = _dereq_('./unsavedChildren'); + +var _unsavedChildren2 = _interopRequireDefault(_unsavedChildren); + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } else { + var newObj = {};if (obj != null) { + for (var key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; + } + }newObj.default = obj;return newObj; + } +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { default: obj }; +} + +// Mapping of class names to constructors, so we can populate objects from the +// server with appropriate subclasses of ParseObject +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + */ + +var classMap = {}; + +// Global counter for generating unique local Ids +var localCount = 0; +// Global counter for generating unique Ids for non-single-instance objects +var objectCount = 0; +// On web clients, objects are single-instance: any two objects with the same Id +// will have the same attributes. However, this may be dangerous default +// behavior in a server scenario +var singleInstance = !_CoreManager2.default.get('IS_NODE'); +if (singleInstance) { + _CoreManager2.default.setObjectStateController(SingleInstanceStateController); +} else { + _CoreManager2.default.setObjectStateController(UniqueInstanceStateController); +} + +function getServerUrlPath() { + var serverUrl = _CoreManager2.default.get('SERVER_URL'); + if (serverUrl[serverUrl.length - 1] !== '/') { + serverUrl += '/'; + } + var url = serverUrl.replace(/https?:\/\//, ''); + return url.substr(url.indexOf('/')); +} + +/** + * Creates a new model with defined attributes. + * + *
You won't normally call this method directly. It is recommended that
+ * you use a subclass of Parse.Object
instead, created by calling
+ * extend
.
However, if you don't want to use a subclass, or aren't sure which + * subclass is appropriate, you can use this form:
+ * var object = new Parse.Object("ClassName"); + *+ * That is basically equivalent to:
+ * var MyClass = Parse.Object.extend("ClassName"); + * var object = new MyClass(); + *+ * + * @class Parse.Object + * @constructor + * @param {String} className The class name for the object + * @param {Object} attributes The initial set of data to store in the object. + * @param {Object} options The options for this object instance. + */ + +var ParseObject = function () { + /** + * The ID of this object, unique within its class. + * @property id + * @type String + */ + function ParseObject(className, attributes, options) { + (0, _classCallCheck3.default)(this, ParseObject); + + // Enable legacy initializers + if (typeof this.initialize === 'function') { + this.initialize.apply(this, arguments); + } + + var toSet = null; + this._objCount = objectCount++; + if (typeof className === 'string') { + this.className = className; + if (attributes && (typeof attributes === 'undefined' ? 'undefined' : (0, _typeof3.default)(attributes)) === 'object') { + toSet = attributes; + } + } else if (className && (typeof className === 'undefined' ? 'undefined' : (0, _typeof3.default)(className)) === 'object') { + this.className = className.className; + toSet = {}; + for (var attr in className) { + if (attr !== 'className') { + toSet[attr] = className[attr]; + } + } + if (attributes && (typeof attributes === 'undefined' ? 'undefined' : (0, _typeof3.default)(attributes)) === 'object') { + options = attributes; + } + } + if (toSet && !this.set(toSet, options)) { + throw new Error('Can\'t create an invalid Parse Object'); + } + } + + /** Prototype getters / setters **/ + + (0, _createClass3.default)(ParseObject, [{ + key: '_getId', + + /** Private methods **/ + + /** + * Returns a local or server Id used uniquely identify this object + */ + value: function () { + if (typeof this.id === 'string') { + return this.id; + } + if (typeof this._localId === 'string') { + return this._localId; + } + var localId = 'local' + String(localCount++); + this._localId = localId; + return localId; + } + + /** + * Returns a unique identifier used to pull data from the State Controller. + */ + + }, { + key: '_getStateIdentifier', + value: function () { + if (singleInstance) { + var _id = this.id; + if (!_id) { + _id = this._getId(); + } + return { + id: _id, + className: this.className + }; + } else { + return this; + } + } + }, { + key: '_getServerData', + value: function () { + var stateController = _CoreManager2.default.getObjectStateController(); + return stateController.getServerData(this._getStateIdentifier()); + } + }, { + key: '_clearServerData', + value: function () { + var serverData = this._getServerData(); + var unset = {}; + for (var attr in serverData) { + unset[attr] = undefined; + } + var stateController = _CoreManager2.default.getObjectStateController(); + stateController.setServerData(this._getStateIdentifier(), unset); + } + }, { + key: '_getPendingOps', + value: function () { + var stateController = _CoreManager2.default.getObjectStateController(); + return stateController.getPendingOps(this._getStateIdentifier()); + } + }, { + key: '_clearPendingOps', + value: function () { + var pending = this._getPendingOps(); + var latest = pending[pending.length - 1]; + var keys = (0, _keys2.default)(latest); + keys.forEach(function (key) { + delete latest[key]; + }); + } + }, { + key: '_getDirtyObjectAttributes', + value: function () { + var attributes = this.attributes; + var stateController = _CoreManager2.default.getObjectStateController(); + var objectCache = stateController.getObjectCache(this._getStateIdentifier()); + var dirty = {}; + for (var attr in attributes) { + var val = attributes[attr]; + if (val && (typeof val === 'undefined' ? 'undefined' : (0, _typeof3.default)(val)) === 'object' && !(val instanceof ParseObject) && !(val instanceof _ParseFile2.default) && !(val instanceof _ParseRelation2.default)) { + // Due to the way browsers construct maps, the key order will not change + // unless the object is changed + try { + var json = (0, _encode2.default)(val, false, true); + var stringified = (0, _stringify2.default)(json); + if (objectCache[attr] !== stringified) { + dirty[attr] = val; + } + } catch (e) { + // Error occurred, possibly by a nested unsaved pointer in a mutable container + // No matter how it happened, it indicates a change in the attribute + dirty[attr] = val; + } + } + } + return dirty; + } + }, { + key: '_toFullJSON', + value: function (seen) { + var json = this.toJSON(seen); + json.__type = 'Object'; + json.className = this.className; + return json; + } + }, { + key: '_getSaveJSON', + value: function () { + var pending = this._getPendingOps(); + var dirtyObjects = this._getDirtyObjectAttributes(); + var json = {}; + + for (var attr in dirtyObjects) { + json[attr] = new _ParseOp.SetOp(dirtyObjects[attr]).toJSON(); + } + for (attr in pending[0]) { + json[attr] = pending[0][attr].toJSON(); + } + return json; + } + }, { + key: '_getSaveParams', + value: function () { + var method = this.id ? 'PUT' : 'POST'; + var body = this._getSaveJSON(); + var path = 'classes/' + this.className; + if (this.id) { + path += '/' + this.id; + } else if (this.className === '_User') { + path = 'users'; + } + return { + method: method, + body: body, + path: path + }; + } + }, { + key: '_finishFetch', + value: function (serverData) { + if (!this.id && serverData.objectId) { + this.id = serverData.objectId; + } + var stateController = _CoreManager2.default.getObjectStateController(); + stateController.initializeState(this._getStateIdentifier()); + var decoded = {}; + for (var attr in serverData) { + if (attr === 'ACL') { + decoded[attr] = new _ParseACL2.default(serverData[attr]); + } else if (attr !== 'objectId') { + decoded[attr] = (0, _decode2.default)(serverData[attr]); + if (decoded[attr] instanceof _ParseRelation2.default) { + decoded[attr]._ensureParentAndKey(this, attr); + } + } + } + if (decoded.createdAt && typeof decoded.createdAt === 'string') { + decoded.createdAt = (0, _parseDate2.default)(decoded.createdAt); + } + if (decoded.updatedAt && typeof decoded.updatedAt === 'string') { + decoded.updatedAt = (0, _parseDate2.default)(decoded.updatedAt); + } + if (!decoded.updatedAt && decoded.createdAt) { + decoded.updatedAt = decoded.createdAt; + } + stateController.commitServerChanges(this._getStateIdentifier(), decoded); + } + }, { + key: '_setExisted', + value: function (existed) { + var stateController = _CoreManager2.default.getObjectStateController(); + var state = stateController.getState(this._getStateIdentifier()); + if (state) { + state.existed = existed; + } + } + }, { + key: '_migrateId', + value: function (serverId) { + if (this._localId && serverId) { + if (singleInstance) { + var stateController = _CoreManager2.default.getObjectStateController(); + var oldState = stateController.removeState(this._getStateIdentifier()); + this.id = serverId; + delete this._localId; + if (oldState) { + stateController.initializeState(this._getStateIdentifier(), oldState); + } + } else { + this.id = serverId; + delete this._localId; + } + } + } + }, { + key: '_handleSaveResponse', + value: function (response, status) { + var changes = {}; + + var stateController = _CoreManager2.default.getObjectStateController(); + var pending = stateController.popPendingState(this._getStateIdentifier()); + for (var attr in pending) { + if (pending[attr] instanceof _ParseOp.RelationOp) { + changes[attr] = pending[attr].applyTo(undefined, this, attr); + } else if (!(attr in response)) { + // Only SetOps and UnsetOps should not come back with results + changes[attr] = pending[attr].applyTo(undefined); + } + } + for (attr in response) { + if ((attr === 'createdAt' || attr === 'updatedAt') && typeof response[attr] === 'string') { + changes[attr] = (0, _parseDate2.default)(response[attr]); + } else if (attr === 'ACL') { + changes[attr] = new _ParseACL2.default(response[attr]); + } else if (attr !== 'objectId') { + changes[attr] = (0, _decode2.default)(response[attr]); + if (changes[attr] instanceof _ParseOp.UnsetOp) { + changes[attr] = undefined; + } + } + } + if (changes.createdAt && !changes.updatedAt) { + changes.updatedAt = changes.createdAt; + } + + this._migrateId(response.objectId); + + if (status !== 201) { + this._setExisted(true); + } + + stateController.commitServerChanges(this._getStateIdentifier(), changes); + } + }, { + key: '_handleSaveError', + value: function () { + this._getPendingOps(); + + var stateController = _CoreManager2.default.getObjectStateController(); + stateController.mergeFirstPendingState(this._getStateIdentifier()); + } + + /** Public methods **/ + + }, { + key: 'initialize', + value: function () {} + // NOOP + + + /** + * Returns a JSON version of the object suitable for saving to Parse. + * @method toJSON + * @return {Object} + */ + + }, { + key: 'toJSON', + value: function (seen) { + var seenEntry = this.id ? this.className + ':' + this.id : this; + var seen = seen || [seenEntry]; + var json = {}; + var attrs = this.attributes; + for (var attr in attrs) { + if ((attr === 'createdAt' || attr === 'updatedAt') && attrs[attr].toJSON) { + json[attr] = attrs[attr].toJSON(); + } else { + json[attr] = (0, _encode2.default)(attrs[attr], false, false, seen); + } + } + var pending = this._getPendingOps(); + for (var attr in pending[0]) { + json[attr] = pending[0][attr].toJSON(); + } + + if (this.id) { + json.objectId = this.id; + } + return json; + } + + /** + * Determines whether this ParseObject is equal to another ParseObject + * @method equals + * @return {Boolean} + */ + + }, { + key: 'equals', + value: function (other) { + if (this === other) { + return true; + } + return other instanceof ParseObject && this.className === other.className && this.id === other.id && typeof this.id !== 'undefined'; + } + + /** + * Returns true if this object has been modified since its last + * save/refresh. If an attribute is specified, it returns true only if that + * particular attribute has been modified since the last save/refresh. + * @method dirty + * @param {String} attr An attribute name (optional). + * @return {Boolean} + */ + + }, { + key: 'dirty', + value: function (attr) { + if (!this.id) { + return true; + } + var pendingOps = this._getPendingOps(); + var dirtyObjects = this._getDirtyObjectAttributes(); + if (attr) { + if (dirtyObjects.hasOwnProperty(attr)) { + return true; + } + for (var i = 0; i < pendingOps.length; i++) { + if (pendingOps[i].hasOwnProperty(attr)) { + return true; + } + } + return false; + } + if ((0, _keys2.default)(pendingOps[0]).length !== 0) { + return true; + } + if ((0, _keys2.default)(dirtyObjects).length !== 0) { + return true; + } + return false; + } + + /** + * Returns an array of keys that have been modified since last save/refresh + * @method dirtyKeys + * @return {Array of string} + */ + + }, { + key: 'dirtyKeys', + value: function () { + var pendingOps = this._getPendingOps(); + var keys = {}; + for (var i = 0; i < pendingOps.length; i++) { + for (var attr in pendingOps[i]) { + keys[attr] = true; + } + } + var dirtyObjects = this._getDirtyObjectAttributes(); + for (var attr in dirtyObjects) { + keys[attr] = true; + } + return (0, _keys2.default)(keys); + } + + /** + * Gets a Pointer referencing this Object. + * @method toPointer + * @return {Object} + */ + + }, { + key: 'toPointer', + value: function () { + if (!this.id) { + throw new Error('Cannot create a pointer to an unsaved ParseObject'); + } + return { + __type: 'Pointer', + className: this.className, + objectId: this.id + }; + } + + /** + * Gets the value of an attribute. + * @method get + * @param {String} attr The string name of an attribute. + */ + + }, { + key: 'get', + value: function (attr) { + return this.attributes[attr]; + } + + /** + * Gets a relation on the given class for the attribute. + * @method relation + * @param String attr The attribute to get the relation for. + */ + + }, { + key: 'relation', + value: function (attr) { + var value = this.get(attr); + if (value) { + if (!(value instanceof _ParseRelation2.default)) { + throw new Error('Called relation() on non-relation field ' + attr); + } + value._ensureParentAndKey(this, attr); + return value; + } + return new _ParseRelation2.default(this, attr); + } + + /** + * Gets the HTML-escaped value of an attribute. + * @method escape + * @param {String} attr The string name of an attribute. + */ + + }, { + key: 'escape', + value: function (attr) { + var val = this.attributes[attr]; + if (val == null) { + return ''; + } + + if (typeof val !== 'string') { + if (typeof val.toString !== 'function') { + return ''; + } + val = val.toString(); + } + return (0, _escape3.default)(val); + } + + /** + * Returns
true
if the attribute contains a value that is not
+ * null or undefined.
+ * @method has
+ * @param {String} attr The string name of the attribute.
+ * @return {Boolean}
+ */
+
+ }, {
+ key: 'has',
+ value: function (attr) {
+ var attributes = this.attributes;
+ if (attributes.hasOwnProperty(attr)) {
+ return attributes[attr] != null;
+ }
+ return false;
+ }
+
+ /**
+ * Sets a hash of model attributes on the object.
+ *
+ * You can call it with an object containing keys and values, or with one + * key and value. For example:
+ * gameTurn.set({ + * player: player1, + * diceRoll: 2 + * }, { + * error: function(gameTurnAgain, error) { + * // The set failed validation. + * } + * }); + * + * game.set("currentPlayer", player2, { + * error: function(gameTurnAgain, error) { + * // The set failed validation. + * } + * }); + * + * game.set("finished", true);+ * + * @method set + * @param {String} key The key to set. + * @param {} value The value to give it. + * @param {Object} options A set of options for the set. + * The only supported option is
error
.
+ * @return {Boolean} true if the set succeeded.
+ */
+
+ }, {
+ key: 'set',
+ value: function (key, value, options) {
+ var changes = {};
+ var newOps = {};
+ if (key && (typeof key === 'undefined' ? 'undefined' : (0, _typeof3.default)(key)) === 'object') {
+ changes = key;
+ options = value;
+ } else if (typeof key === 'string') {
+ changes[key] = value;
+ } else {
+ return this;
+ }
+
+ options = options || {};
+ var readonly = [];
+ if (typeof this.constructor.readOnlyAttributes === 'function') {
+ readonly = readonly.concat(this.constructor.readOnlyAttributes());
+ }
+ for (var k in changes) {
+ if (k === 'createdAt' || k === 'updatedAt') {
+ // This property is read-only, but for legacy reasons we silently
+ // ignore it
+ continue;
+ }
+ if (readonly.indexOf(k) > -1) {
+ throw new Error('Cannot modify readonly attribute: ' + k);
+ }
+ if (options.unset) {
+ newOps[k] = new _ParseOp.UnsetOp();
+ } else if (changes[k] instanceof _ParseOp.Op) {
+ newOps[k] = changes[k];
+ } else if (changes[k] && (0, _typeof3.default)(changes[k]) === 'object' && typeof changes[k].__op === 'string') {
+ newOps[k] = (0, _ParseOp.opFromJSON)(changes[k]);
+ } else if (k === 'objectId' || k === 'id') {
+ if (typeof changes[k] === 'string') {
+ this.id = changes[k];
+ }
+ } else if (k === 'ACL' && (0, _typeof3.default)(changes[k]) === 'object' && !(changes[k] instanceof _ParseACL2.default)) {
+ newOps[k] = new _ParseOp.SetOp(new _ParseACL2.default(changes[k]));
+ } else {
+ newOps[k] = new _ParseOp.SetOp(changes[k]);
+ }
+ }
+
+ // Calculate new values
+ var currentAttributes = this.attributes;
+ var newValues = {};
+ for (var attr in newOps) {
+ if (newOps[attr] instanceof _ParseOp.RelationOp) {
+ newValues[attr] = newOps[attr].applyTo(currentAttributes[attr], this, attr);
+ } else if (!(newOps[attr] instanceof _ParseOp.UnsetOp)) {
+ newValues[attr] = newOps[attr].applyTo(currentAttributes[attr]);
+ }
+ }
+
+ // Validate changes
+ if (!options.ignoreValidation) {
+ var validation = this.validate(newValues);
+ if (validation) {
+ if (typeof options.error === 'function') {
+ options.error(this, validation);
+ }
+ return false;
+ }
+ }
+
+ // Consolidate Ops
+ var pendingOps = this._getPendingOps();
+ var last = pendingOps.length - 1;
+ var stateController = _CoreManager2.default.getObjectStateController();
+ for (var attr in newOps) {
+ var nextOp = newOps[attr].mergeWith(pendingOps[last][attr]);
+ stateController.setPendingOp(this._getStateIdentifier(), attr, nextOp);
+ }
+
+ return this;
+ }
+
+ /**
+ * Remove an attribute from the model. This is a noop if the attribute doesn't
+ * exist.
+ * @method unset
+ * @param {String} attr The string name of an attribute.
+ */
+
+ }, {
+ key: 'unset',
+ value: function (attr, options) {
+ options = options || {};
+ options.unset = true;
+ return this.set(attr, null, options);
+ }
+
+ /**
+ * Atomically increments the value of the given attribute the next time the
+ * object is saved. If no amount is specified, 1 is used by default.
+ *
+ * @method increment
+ * @param attr {String} The key.
+ * @param amount {Number} The amount to increment by (optional).
+ */
+
+ }, {
+ key: 'increment',
+ value: function (attr, amount) {
+ if (typeof amount === 'undefined') {
+ amount = 1;
+ }
+ if (typeof amount !== 'number') {
+ throw new Error('Cannot increment by a non-numeric amount.');
+ }
+ return this.set(attr, new _ParseOp.IncrementOp(amount));
+ }
+
+ /**
+ * Atomically add an object to the end of the array associated with a given
+ * key.
+ * @method add
+ * @param attr {String} The key.
+ * @param item {} The item to add.
+ */
+
+ }, {
+ key: 'add',
+ value: function (attr, item) {
+ return this.set(attr, new _ParseOp.AddOp([item]));
+ }
+
+ /**
+ * Atomically add an object to the array associated with a given key, only
+ * if it is not already present in the array. The position of the insert is
+ * not guaranteed.
+ *
+ * @method addUnique
+ * @param attr {String} The key.
+ * @param item {} The object to add.
+ */
+
+ }, {
+ key: 'addUnique',
+ value: function (attr, item) {
+ return this.set(attr, new _ParseOp.AddUniqueOp([item]));
+ }
+
+ /**
+ * Atomically remove all instances of an object from the array associated
+ * with a given key.
+ *
+ * @method remove
+ * @param attr {String} The key.
+ * @param item {} The object to remove.
+ */
+
+ }, {
+ key: 'remove',
+ value: function (attr, item) {
+ return this.set(attr, new _ParseOp.RemoveOp([item]));
+ }
+
+ /**
+ * Returns an instance of a subclass of Parse.Op describing what kind of
+ * modification has been performed on this field since the last time it was
+ * saved. For example, after calling object.increment("x"), calling
+ * object.op("x") would return an instance of Parse.Op.Increment.
+ *
+ * @method op
+ * @param attr {String} The key.
+ * @returns {Parse.Op} The operation, or undefined if none.
+ */
+
+ }, {
+ key: 'op',
+ value: function (attr) {
+ var pending = this._getPendingOps();
+ for (var i = pending.length; i--;) {
+ if (pending[i][attr]) {
+ return pending[i][attr];
+ }
+ }
+ }
+
+ /**
+ * Creates a new model with identical attributes to this one, similar to Backbone.Model's clone()
+ * @method clone
+ * @return {Parse.Object}
+ */
+
+ }, {
+ key: 'clone',
+ value: function () {
+ var clone = new this.constructor();
+ if (!clone.className) {
+ clone.className = this.className;
+ }
+ var attributes = this.attributes;
+ if (typeof this.constructor.readOnlyAttributes === 'function') {
+ var readonly = this.constructor.readOnlyAttributes() || [];
+ // Attributes are frozen, so we have to rebuild an object,
+ // rather than delete readonly keys
+ var copy = {};
+ for (var a in attributes) {
+ if (readonly.indexOf(a) < 0) {
+ copy[a] = attributes[a];
+ }
+ }
+ attributes = copy;
+ }
+ if (clone.set) {
+ clone.set(attributes);
+ }
+ return clone;
+ }
+
+ /**
+ * Creates a new instance of this object. Not to be confused with clone()
+ * @method newInstance
+ * @return {Parse.Object}
+ */
+
+ }, {
+ key: 'newInstance',
+ value: function () {
+ var clone = new this.constructor();
+ if (!clone.className) {
+ clone.className = this.className;
+ }
+ clone.id = this.id;
+ if (singleInstance) {
+ // Just return an object with the right id
+ return clone;
+ }
+
+ var stateController = _CoreManager2.default.getObjectStateController();
+ if (stateController) {
+ stateController.duplicateState(this._getStateIdentifier(), clone._getStateIdentifier());
+ }
+ return clone;
+ }
+
+ /**
+ * Returns true if this object has never been saved to Parse.
+ * @method isNew
+ * @return {Boolean}
+ */
+
+ }, {
+ key: 'isNew',
+ value: function () {
+ return !this.id;
+ }
+
+ /**
+ * Returns true if this object was created by the Parse server when the
+ * object might have already been there (e.g. in the case of a Facebook
+ * login)
+ * @method existed
+ * @return {Boolean}
+ */
+
+ }, {
+ key: 'existed',
+ value: function () {
+ if (!this.id) {
+ return false;
+ }
+ var stateController = _CoreManager2.default.getObjectStateController();
+ var state = stateController.getState(this._getStateIdentifier());
+ if (state) {
+ return state.existed;
+ }
+ return false;
+ }
+
+ /**
+ * Checks if the model is currently in a valid state.
+ * @method isValid
+ * @return {Boolean}
+ */
+
+ }, {
+ key: 'isValid',
+ value: function () {
+ return !this.validate(this.attributes);
+ }
+
+ /**
+ * You should not call this function directly unless you subclass
+ * Parse.Object
, in which case you can override this method
+ * to provide additional validation on set
and
+ * save
. Your implementation should return
+ *
+ * @method validate
+ * @param {Object} attrs The current data to validate.
+ * @return {} False if the data is valid. An error object otherwise.
+ * @see Parse.Object#set
+ */
+
+ }, {
+ key: 'validate',
+ value: function (attrs) {
+ if (attrs.hasOwnProperty('ACL') && !(attrs.ACL instanceof _ParseACL2.default)) {
+ return new _ParseError2.default(_ParseError2.default.OTHER_CAUSE, 'ACL must be a Parse ACL.');
+ }
+ for (var key in attrs) {
+ if (!/^[A-Za-z][0-9A-Za-z_]*$/.test(key)) {
+ return new _ParseError2.default(_ParseError2.default.INVALID_KEY_NAME);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns the ACL for this object.
+ * @method getACL
+ * @returns {Parse.ACL} An instance of Parse.ACL.
+ * @see Parse.Object#get
+ */
+
+ }, {
+ key: 'getACL',
+ value: function () {
+ var acl = this.get('ACL');
+ if (acl instanceof _ParseACL2.default) {
+ return acl;
+ }
+ return null;
+ }
+
+ /**
+ * Sets the ACL to be used for this object.
+ * @method setACL
+ * @param {Parse.ACL} acl An instance of Parse.ACL.
+ * @param {Object} options Optional Backbone-like options object to be
+ * passed in to set.
+ * @return {Boolean} Whether the set passed validation.
+ * @see Parse.Object#set
+ */
+
+ }, {
+ key: 'setACL',
+ value: function (acl, options) {
+ return this.set('ACL', acl, options);
+ }
+
+ /**
+ * Clears any changes to this object made since the last call to save()
+ * @method revert
+ */
+
+ }, {
+ key: 'revert',
+ value: function () {
+ this._clearPendingOps();
+ }
+
+ /**
+ * Clears all attributes on a model
+ * @method clear
+ */
+
+ }, {
+ key: 'clear',
+ value: function () {
+ var attributes = this.attributes;
+ var erasable = {};
+ var readonly = ['createdAt', 'updatedAt'];
+ if (typeof this.constructor.readOnlyAttributes === 'function') {
+ readonly = readonly.concat(this.constructor.readOnlyAttributes());
+ }
+ for (var attr in attributes) {
+ if (readonly.indexOf(attr) < 0) {
+ erasable[attr] = true;
+ }
+ }
+ return this.set(erasable, { unset: true });
+ }
+
+ /**
+ * Fetch the model from the server. If the server's representation of the
+ * model differs from its current attributes, they will be overriden.
+ *
+ * @method fetch
+ * @param {Object} options A Backbone-style callback object.
+ * Valid options are:+ * object.save();+ * or
+ * object.save(null, options);+ * or
+ * object.save(attrs, options);+ * or
+ * object.save(key, value, options);+ * + * For example,
+ * gameTurn.save({ + * player: "Jake Cutter", + * diceRoll: 2 + * }, { + * success: function(gameTurnAgain) { + * // The save was successful. + * }, + * error: function(gameTurnAgain, error) { + * // The save failed. Error is an instance of Parse.Error. + * } + * });+ * or with promises:
+ * gameTurn.save({ + * player: "Jake Cutter", + * diceRoll: 2 + * }).then(function(gameTurnAgain) { + * // The save was successful. + * }, function(error) { + * // The save failed. Error is an instance of Parse.Error. + * });+ * + * @method save + * @param {Object} options A Backbone-style callback object. + * Valid options are:
+ * Parse.Object.fetchAll([object1, object2, ...], { + * success: function(list) { + * // All the objects were fetched. + * }, + * error: function(error) { + * // An error occurred while fetching one of the objects. + * }, + * }); + *+ * + * @method fetchAll + * @param {Array} list A list of
Parse.Object
.
+ * @param {Object} options A Backbone-style callback object.
+ * @static
+ * Valid options are:+ * Parse.Object.fetchAllIfNeeded([object1, ...], { + * success: function(list) { + * // Objects were fetched and updated. + * }, + * error: function(error) { + * // An error occurred while fetching one of the objects. + * }, + * }); + *+ * + * @method fetchAllIfNeeded + * @param {Array} list A list of
Parse.Object
.
+ * @param {Object} options A Backbone-style callback object.
+ * @static
+ * Valid options are:Unlike saveAll, if an error occurs while deleting an individual model, + * this method will continue trying to delete the rest of the models if + * possible, except in the case of a fatal error like a connection error. + * + *
In particular, the Parse.Error object returned in the case of error may + * be one of two types: + * + *
+ * Parse.Object.destroyAll([object1, object2, ...], { + * success: function() { + * // All the objects were deleted. + * }, + * error: function(error) { + * // An error occurred while deleting one or more of the objects. + * // If this is an aggregate error, then we can inspect each error + * // object individually to determine the reason why a particular + * // object was not deleted. + * if (error.code === Parse.Error.AGGREGATE_ERROR) { + * for (var i = 0; i < error.errors.length; i++) { + * console.log("Couldn't delete " + error.errors[i].object.id + + * "due to " + error.errors[i].message); + * } + * } else { + * console.log("Delete aborted because of " + error.message); + * } + * }, + * }); + *+ * + * @method destroyAll + * @param {Array} list A list of
Parse.Object
.
+ * @param {Object} options A Backbone-style callback object.
+ * @static
+ * Valid options are:+ * Parse.Object.saveAll([object1, object2, ...], { + * success: function(list) { + * // All the objects were saved. + * }, + * error: function(error) { + * // An error occurred while saving one of the objects. + * }, + * }); + *+ * + * @method saveAll + * @param {Array} list A list of
Parse.Object
.
+ * @param {Object} options A Backbone-style callback object.
+ * @static
+ * Valid options are:A shortcut for:
+ * var Foo = Parse.Object.extend("Foo"); + * var pointerToFoo = new Foo(); + * pointerToFoo.id = "myObjectId"; + *+ * + * @method createWithoutData + * @param {String} id The ID of the object to create a reference to. + * @static + * @return {Parse.Object} A Parse.Object reference. + */ + + }, { + key: 'createWithoutData', + value: function (id) { + var obj = new this(); + obj.id = id; + return obj; + } + + /** + * Creates a new instance of a Parse Object from a JSON representation. + * @method fromJSON + * @param {Object} json The JSON map of the Object's data + * @param {boolean} override In single instance mode, all old server data + * is overwritten if this is set to true + * @static + * @return {Parse.Object} A Parse.Object reference + */ + + }, { + key: 'fromJSON', + value: function (json, override) { + if (!json.className) { + throw new Error('Cannot create an object without a className'); + } + var constructor = classMap[json.className]; + var o = constructor ? new constructor() : new ParseObject(json.className); + var otherAttributes = {}; + for (var attr in json) { + if (attr !== 'className' && attr !== '__type') { + otherAttributes[attr] = json[attr]; + } + } + if (override) { + // id needs to be set before clearServerData can work + if (otherAttributes.objectId) { + o.id = otherAttributes.objectId; + } + var preserved = null; + if (typeof o._preserveFieldsOnFetch === 'function') { + preserved = o._preserveFieldsOnFetch(); + } + o._clearServerData(); + if (preserved) { + o._finishFetch(preserved); + } + } + o._finishFetch(otherAttributes); + if (json.objectId) { + o._setExisted(true); + } + return o; + } + + /** + * Registers a subclass of Parse.Object with a specific class name. + * When objects of that class are retrieved from a query, they will be + * instantiated with this subclass. + * This is only necessary when using ES6 subclassing. + * @method registerSubclass + * @param {String} className The class name of the subclass + * @param {Class} constructor The subclass + */ + + }, { + key: 'registerSubclass', + value: function (className, constructor) { + if (typeof className !== 'string') { + throw new TypeError('The first argument must be a valid class name.'); + } + if (typeof constructor === 'undefined') { + throw new TypeError('You must supply a subclass constructor.'); + } + if (typeof constructor !== 'function') { + throw new TypeError('You must register the subclass constructor. ' + 'Did you attempt to register an instance of the subclass?'); + } + classMap[className] = constructor; + if (!constructor.className) { + constructor.className = className; + } + } + + /** + * Creates a new subclass of Parse.Object for the given Parse class name. + * + *
Every extension of a Parse class will inherit from the most recent + * previous extension of that class. When a Parse.Object is automatically + * created by parsing JSON, it will use the most recent extension of that + * class.
+ * + *You should call either:
+ * var MyClass = Parse.Object.extend("MyClass", { + * Instance methods, + * initialize: function(attrs, options) { + * this.someInstanceProperty = [], + * Other instance properties + * } + * }, { + * Class properties + * });+ * or, for Backbone compatibility:
+ * var MyClass = Parse.Object.extend({ + * className: "MyClass", + * Instance methods, + * initialize: function(attrs, options) { + * this.someInstanceProperty = [], + * Other instance properties + * } + * }, { + * Class properties + * });+ * + * @method extend + * @param {String} className The name of the Parse class backing this model. + * @param {Object} protoProps Instance properties to add to instances of the + * class returned from this method. + * @param {Object} classProps Class properties to add the class returned from + * this method. + * @return {Class} A new subclass of Parse.Object. + */ + + }, { + key: 'extend', + value: function (className, protoProps, classProps) { + if (typeof className !== 'string') { + if (className && typeof className.className === 'string') { + return ParseObject.extend(className.className, className, protoProps); + } else { + throw new Error('Parse.Object.extend\'s first argument should be the className.'); + } + } + var adjustedClassName = className; + + if (adjustedClassName === 'User' && _CoreManager2.default.get('PERFORM_USER_REWRITE')) { + adjustedClassName = '_User'; + } + + var parentProto = ParseObject.prototype; + if (this.hasOwnProperty('__super__') && this.__super__) { + parentProto = this.prototype; + } else if (classMap[adjustedClassName]) { + parentProto = classMap[adjustedClassName].prototype; + } + var ParseObjectSubclass = function (attributes, options) { + this.className = adjustedClassName; + this._objCount = objectCount++; + // Enable legacy initializers + if (typeof this.initialize === 'function') { + this.initialize.apply(this, arguments); + } + + if (attributes && (typeof attributes === 'undefined' ? 'undefined' : (0, _typeof3.default)(attributes)) === 'object') { + if (!this.set(attributes || {}, options)) { + throw new Error('Can\'t create an invalid Parse Object'); + } + } + }; + ParseObjectSubclass.className = adjustedClassName; + ParseObjectSubclass.__super__ = parentProto; + + ParseObjectSubclass.prototype = (0, _create2.default)(parentProto, { + constructor: { + value: ParseObjectSubclass, + enumerable: false, + writable: true, + configurable: true + } + }); + + if (protoProps) { + for (var prop in protoProps) { + if (prop !== 'className') { + (0, _defineProperty2.default)(ParseObjectSubclass.prototype, prop, { + value: protoProps[prop], + enumerable: false, + writable: true, + configurable: true + }); + } + } + } + + if (classProps) { + for (var prop in classProps) { + if (prop !== 'className') { + (0, _defineProperty2.default)(ParseObjectSubclass, prop, { + value: classProps[prop], + enumerable: false, + writable: true, + configurable: true + }); + } + } + } + + ParseObjectSubclass.extend = function (name, protoProps, classProps) { + if (typeof name === 'string') { + return ParseObject.extend.call(ParseObjectSubclass, name, protoProps, classProps); + } + return ParseObject.extend.call(ParseObjectSubclass, adjustedClassName, name, protoProps); + }; + ParseObjectSubclass.createWithoutData = ParseObject.createWithoutData; + + classMap[adjustedClassName] = ParseObjectSubclass; + return ParseObjectSubclass; + } + + /** + * Enable single instance objects, where any local objects with the same Id + * share the same attributes, and stay synchronized with each other. + * This is disabled by default in server environments, since it can lead to + * security issues. + * @method enableSingleInstance + */ + + }, { + key: 'enableSingleInstance', + value: function () { + singleInstance = true; + _CoreManager2.default.setObjectStateController(SingleInstanceStateController); + } + + /** + * Disable single instance objects, where any local objects with the same Id + * share the same attributes, and stay synchronized with each other. + * When disabled, you can have two instances of the same object in memory + * without them sharing attributes. + * @method disableSingleInstance + */ + + }, { + key: 'disableSingleInstance', + value: function () { + singleInstance = false; + _CoreManager2.default.setObjectStateController(UniqueInstanceStateController); + } + }]); + return ParseObject; +}(); + +exports.default = ParseObject; + +var DefaultController = { + fetch: function (target, forceFetch, options) { + if (Array.isArray(target)) { + if (target.length < 1) { + return _ParsePromise2.default.as([]); + } + var objs = []; + var ids = []; + var className = null; + var results = []; + var error = null; + target.forEach(function (el, i) { + if (error) { + return; + } + if (!className) { + className = el.className; + } + if (className !== el.className) { + error = new _ParseError2.default(_ParseError2.default.INVALID_CLASS_NAME, 'All objects should be of the same class'); + } + if (!el.id) { + error = new _ParseError2.default(_ParseError2.default.MISSING_OBJECT_ID, 'All objects must have an ID'); + } + if (forceFetch || (0, _keys2.default)(el._getServerData()).length === 0) { + ids.push(el.id); + objs.push(el); + } + results.push(el); + }); + if (error) { + return _ParsePromise2.default.error(error); + } + var query = new _ParseQuery2.default(className); + query.containedIn('objectId', ids); + query._limit = ids.length; + return query.find(options).then(function (objects) { + var idMap = {}; + objects.forEach(function (o) { + idMap[o.id] = o; + }); + for (var i = 0; i < objs.length; i++) { + var obj = objs[i]; + if (!obj || !obj.id || !idMap[obj.id]) { + if (forceFetch) { + return _ParsePromise2.default.error(new _ParseError2.default(_ParseError2.default.OBJECT_NOT_FOUND, 'All objects must exist on the server.')); + } + } + } + if (!singleInstance) { + // If single instance objects are disabled, we need to replace the + for (var i = 0; i < results.length; i++) { + var obj = results[i]; + if (obj && obj.id && idMap[obj.id]) { + var id = obj.id; + obj._finishFetch(idMap[id].toJSON()); + results[i] = idMap[id]; + } + } + } + return _ParsePromise2.default.as(results); + }); + } else { + var RESTController = _CoreManager2.default.getRESTController(); + return RESTController.request('GET', 'classes/' + target.className + '/' + target._getId(), {}, options).then(function (response, status, xhr) { + if (target instanceof ParseObject) { + target._clearPendingOps(); + target._clearServerData(); + target._finishFetch(response); + } + return target; + }); + } + }, + destroy: function (target, options) { + var RESTController = _CoreManager2.default.getRESTController(); + if (Array.isArray(target)) { + if (target.length < 1) { + return _ParsePromise2.default.as([]); + } + var batches = [[]]; + target.forEach(function (obj) { + if (!obj.id) { + return; + } + batches[batches.length - 1].push(obj); + if (batches[batches.length - 1].length >= 20) { + batches.push([]); + } + }); + if (batches[batches.length - 1].length === 0) { + // If the last batch is empty, remove it + batches.pop(); + } + var deleteCompleted = _ParsePromise2.default.as(); + var errors = []; + batches.forEach(function (batch) { + deleteCompleted = deleteCompleted.then(function () { + return RESTController.request('POST', 'batch', { + requests: batch.map(function (obj) { + return { + method: 'DELETE', + path: getServerUrlPath() + 'classes/' + obj.className + '/' + obj._getId(), + body: {} + }; + }) + }, options).then(function (results) { + for (var i = 0; i < results.length; i++) { + if (results[i] && results[i].hasOwnProperty('error')) { + var err = new _ParseError2.default(results[i].error.code, results[i].error.error); + err.object = batch[i]; + errors.push(err); + } + } + }); + }); + }); + return deleteCompleted.then(function () { + if (errors.length) { + var aggregate = new _ParseError2.default(_ParseError2.default.AGGREGATE_ERROR); + aggregate.errors = errors; + return _ParsePromise2.default.error(aggregate); + } + return _ParsePromise2.default.as(target); + }); + } else if (target instanceof ParseObject) { + return RESTController.request('DELETE', 'classes/' + target.className + '/' + target._getId(), {}, options).then(function () { + return _ParsePromise2.default.as(target); + }); + } + return _ParsePromise2.default.as(target); + }, + save: function (target, options) { + var RESTController = _CoreManager2.default.getRESTController(); + var stateController = _CoreManager2.default.getObjectStateController(); + if (Array.isArray(target)) { + if (target.length < 1) { + return _ParsePromise2.default.as([]); + } + + var unsaved = target.concat(); + for (var i = 0; i < target.length; i++) { + if (target[i] instanceof ParseObject) { + unsaved = unsaved.concat((0, _unsavedChildren2.default)(target[i], true)); + } + } + unsaved = (0, _unique2.default)(unsaved); + + var filesSaved = _ParsePromise2.default.as(); + var pending = []; + unsaved.forEach(function (el) { + if (el instanceof _ParseFile2.default) { + filesSaved = filesSaved.then(function () { + return el.save(); + }); + } else if (el instanceof ParseObject) { + pending.push(el); + } + }); + + return filesSaved.then(function () { + var objectError = null; + return _ParsePromise2.default._continueWhile(function () { + return pending.length > 0; + }, function () { + var batch = []; + var nextPending = []; + pending.forEach(function (el) { + if (batch.length < 20 && (0, _canBeSerialized2.default)(el)) { + batch.push(el); + } else { + nextPending.push(el); + } + }); + pending = nextPending; + if (batch.length < 1) { + return _ParsePromise2.default.error(new _ParseError2.default(_ParseError2.default.OTHER_CAUSE, 'Tried to save a batch with a cycle.')); + } + + // Queue up tasks for each object in the batch. + // When every task is ready, the API request will execute + var batchReturned = new _ParsePromise2.default(); + var batchReady = []; + var batchTasks = []; + batch.forEach(function (obj, index) { + var ready = new _ParsePromise2.default(); + batchReady.push(ready); + + stateController.pushPendingState(obj._getStateIdentifier()); + batchTasks.push(stateController.enqueueTask(obj._getStateIdentifier(), function () { + ready.resolve(); + return batchReturned.then(function (responses, status) { + if (responses[index].hasOwnProperty('success')) { + obj._handleSaveResponse(responses[index].success, status); + } else { + if (!objectError && responses[index].hasOwnProperty('error')) { + var serverError = responses[index].error; + objectError = new _ParseError2.default(serverError.code, serverError.error); + // Cancel the rest of the save + pending = []; + } + obj._handleSaveError(); + } + }); + })); + }); + + _ParsePromise2.default.when(batchReady).then(function () { + // Kick off the batch request + return RESTController.request('POST', 'batch', { + requests: batch.map(function (obj) { + var params = obj._getSaveParams(); + params.path = getServerUrlPath() + params.path; + return params; + }) + }, options); + }).then(function (response, status, xhr) { + batchReturned.resolve(response, status); + }); + + return _ParsePromise2.default.when(batchTasks); + }).then(function () { + if (objectError) { + return _ParsePromise2.default.error(objectError); + } + return _ParsePromise2.default.as(target); + }); + }); + } else if (target instanceof ParseObject) { + // copying target lets Flow guarantee the pointer isn't modified elsewhere + var targetCopy = target; + var task = function () { + var params = targetCopy._getSaveParams(); + return RESTController.request(params.method, params.path, params.body, options).then(function (response, status) { + targetCopy._handleSaveResponse(response, status); + }, function (error) { + targetCopy._handleSaveError(); + return _ParsePromise2.default.error(error); + }); + }; + + stateController.pushPendingState(target._getStateIdentifier()); + return stateController.enqueueTask(target._getStateIdentifier(), task).then(function () { + return target; + }, function (error) { + return _ParsePromise2.default.error(error); + }); + } + return _ParsePromise2.default.as(); + } +}; + +_CoreManager2.default.setObjectController(DefaultController); +},{"./CoreManager":3,"./ParseACL":11,"./ParseError":13,"./ParseFile":14,"./ParseOp":19,"./ParsePromise":20,"./ParseQuery":21,"./ParseRelation":22,"./SingleInstanceStateController":28,"./UniqueInstanceStateController":32,"./canBeSerialized":34,"./decode":35,"./encode":36,"./equals":37,"./escape":38,"./parseDate":40,"./unique":41,"./unsavedChildren":42,"babel-runtime/core-js/json/stringify":44,"babel-runtime/core-js/object/create":46,"babel-runtime/core-js/object/define-property":47,"babel-runtime/core-js/object/freeze":48,"babel-runtime/core-js/object/keys":51,"babel-runtime/helpers/classCallCheck":56,"babel-runtime/helpers/createClass":57,"babel-runtime/helpers/typeof":61}],19:[function(_dereq_,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.RelationOp = exports.RemoveOp = exports.AddUniqueOp = exports.AddOp = exports.IncrementOp = exports.UnsetOp = exports.SetOp = exports.Op = undefined; + +var _getPrototypeOf = _dereq_('babel-runtime/core-js/object/get-prototype-of'); + +var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); + +var _possibleConstructorReturn2 = _dereq_('babel-runtime/helpers/possibleConstructorReturn'); + +var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); + +var _inherits2 = _dereq_('babel-runtime/helpers/inherits'); + +var _inherits3 = _interopRequireDefault(_inherits2); + +var _classCallCheck2 = _dereq_('babel-runtime/helpers/classCallCheck'); + +var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); + +var _createClass2 = _dereq_('babel-runtime/helpers/createClass'); + +var _createClass3 = _interopRequireDefault(_createClass2); + +exports.opFromJSON = opFromJSON; + +var _arrayContainsObject = _dereq_('./arrayContainsObject'); + +var _arrayContainsObject2 = _interopRequireDefault(_arrayContainsObject); + +var _decode = _dereq_('./decode'); + +var _decode2 = _interopRequireDefault(_decode); + +var _encode = _dereq_('./encode'); + +var _encode2 = _interopRequireDefault(_encode); + +var _ParseObject = _dereq_('./ParseObject'); + +var _ParseObject2 = _interopRequireDefault(_ParseObject); + +var _ParseRelation = _dereq_('./ParseRelation'); + +var _ParseRelation2 = _interopRequireDefault(_ParseRelation); + +var _unique = _dereq_('./unique'); + +var _unique2 = _interopRequireDefault(_unique); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { default: obj }; +} + +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + */ + +function opFromJSON(json) { + if (!json || !json.__op) { + return null; + } + switch (json.__op) { + case 'Delete': + return new UnsetOp(); + case 'Increment': + return new IncrementOp(json.amount); + case 'Add': + return new AddOp((0, _decode2.default)(json.objects)); + case 'AddUnique': + return new AddUniqueOp((0, _decode2.default)(json.objects)); + case 'Remove': + return new RemoveOp((0, _decode2.default)(json.objects)); + case 'AddRelation': + var toAdd = (0, _decode2.default)(json.objects); + if (!Array.isArray(toAdd)) { + return new RelationOp([], []); + } + return new RelationOp(toAdd, []); + case 'RemoveRelation': + var toRemove = (0, _decode2.default)(json.objects); + if (!Array.isArray(toRemove)) { + return new RelationOp([], []); + } + return new RelationOp([], toRemove); + case 'Batch': + var toAdd = []; + var toRemove = []; + for (var i = 0; i < json.ops.length; i++) { + if (json.ops[i].__op === 'AddRelation') { + toAdd = toAdd.concat((0, _decode2.default)(json.ops[i].objects)); + } else if (json.ops[i].__op === 'RemoveRelation') { + toRemove = toRemove.concat((0, _decode2.default)(json.ops[i].objects)); + } + } + return new RelationOp(toAdd, toRemove); + } + return null; +} + +var Op = exports.Op = function () { + function Op() { + (0, _classCallCheck3.default)(this, Op); + } + + (0, _createClass3.default)(Op, [{ + key: 'applyTo', + + // Empty parent class + value: function (value) {} + }, { + key: 'mergeWith', + value: function (previous) {} + }, { + key: 'toJSON', + value: function () {} + }]); + return Op; +}(); + +var SetOp = exports.SetOp = function (_Op) { + (0, _inherits3.default)(SetOp, _Op); + + function SetOp(value) { + (0, _classCallCheck3.default)(this, SetOp); + + var _this = (0, _possibleConstructorReturn3.default)(this, (SetOp.__proto__ || (0, _getPrototypeOf2.default)(SetOp)).call(this)); + + _this._value = value; + return _this; + } + + (0, _createClass3.default)(SetOp, [{ + key: 'applyTo', + value: function (value) { + return this._value; + } + }, { + key: 'mergeWith', + value: function (previous) { + return new SetOp(this._value); + } + }, { + key: 'toJSON', + value: function () { + return (0, _encode2.default)(this._value, false, true); + } + }]); + return SetOp; +}(Op); + +var UnsetOp = exports.UnsetOp = function (_Op2) { + (0, _inherits3.default)(UnsetOp, _Op2); + + function UnsetOp() { + (0, _classCallCheck3.default)(this, UnsetOp); + return (0, _possibleConstructorReturn3.default)(this, (UnsetOp.__proto__ || (0, _getPrototypeOf2.default)(UnsetOp)).apply(this, arguments)); + } + + (0, _createClass3.default)(UnsetOp, [{ + key: 'applyTo', + value: function (value) { + return undefined; + } + }, { + key: 'mergeWith', + value: function (previous) { + return new UnsetOp(); + } + }, { + key: 'toJSON', + value: function () { + return { __op: 'Delete' }; + } + }]); + return UnsetOp; +}(Op); + +var IncrementOp = exports.IncrementOp = function (_Op3) { + (0, _inherits3.default)(IncrementOp, _Op3); + + function IncrementOp(amount) { + (0, _classCallCheck3.default)(this, IncrementOp); + + var _this3 = (0, _possibleConstructorReturn3.default)(this, (IncrementOp.__proto__ || (0, _getPrototypeOf2.default)(IncrementOp)).call(this)); + + if (typeof amount !== 'number') { + throw new TypeError('Increment Op must be initialized with a numeric amount.'); + } + _this3._amount = amount; + return _this3; + } + + (0, _createClass3.default)(IncrementOp, [{ + key: 'applyTo', + value: function (value) { + if (typeof value === 'undefined') { + return this._amount; + } + if (typeof value !== 'number') { + throw new TypeError('Cannot increment a non-numeric value.'); + } + return this._amount + value; + } + }, { + key: 'mergeWith', + value: function (previous) { + if (!previous) { + return this; + } + if (previous instanceof SetOp) { + return new SetOp(this.applyTo(previous._value)); + } + if (previous instanceof UnsetOp) { + return new SetOp(this._amount); + } + if (previous instanceof IncrementOp) { + return new IncrementOp(this.applyTo(previous._amount)); + } + throw new Error('Cannot merge Increment Op with the previous Op'); + } + }, { + key: 'toJSON', + value: function () { + return { __op: 'Increment', amount: this._amount }; + } + }]); + return IncrementOp; +}(Op); + +var AddOp = exports.AddOp = function (_Op4) { + (0, _inherits3.default)(AddOp, _Op4); + + function AddOp(value) { + (0, _classCallCheck3.default)(this, AddOp); + + var _this4 = (0, _possibleConstructorReturn3.default)(this, (AddOp.__proto__ || (0, _getPrototypeOf2.default)(AddOp)).call(this)); + + _this4._value = Array.isArray(value) ? value : [value]; + return _this4; + } + + (0, _createClass3.default)(AddOp, [{ + key: 'applyTo', + value: function (value) { + if (value == null) { + return this._value; + } + if (Array.isArray(value)) { + return value.concat(this._value); + } + throw new Error('Cannot add elements to a non-array value'); + } + }, { + key: 'mergeWith', + value: function (previous) { + if (!previous) { + return this; + } + if (previous instanceof SetOp) { + return new SetOp(this.applyTo(previous._value)); + } + if (previous instanceof UnsetOp) { + return new SetOp(this._value); + } + if (previous instanceof AddOp) { + return new AddOp(this.applyTo(previous._value)); + } + throw new Error('Cannot merge Add Op with the previous Op'); + } + }, { + key: 'toJSON', + value: function () { + return { __op: 'Add', objects: (0, _encode2.default)(this._value, false, true) }; + } + }]); + return AddOp; +}(Op); + +var AddUniqueOp = exports.AddUniqueOp = function (_Op5) { + (0, _inherits3.default)(AddUniqueOp, _Op5); + + function AddUniqueOp(value) { + (0, _classCallCheck3.default)(this, AddUniqueOp); + + var _this5 = (0, _possibleConstructorReturn3.default)(this, (AddUniqueOp.__proto__ || (0, _getPrototypeOf2.default)(AddUniqueOp)).call(this)); + + _this5._value = (0, _unique2.default)(Array.isArray(value) ? value : [value]); + return _this5; + } + + (0, _createClass3.default)(AddUniqueOp, [{ + key: 'applyTo', + value: function (value) { + if (value == null) { + return this._value || []; + } + if (Array.isArray(value)) { + // copying value lets Flow guarantee the pointer isn't modified elsewhere + var valueCopy = value; + var toAdd = []; + this._value.forEach(function (v) { + if (v instanceof _ParseObject2.default) { + if (!(0, _arrayContainsObject2.default)(valueCopy, v)) { + toAdd.push(v); + } + } else { + if (valueCopy.indexOf(v) < 0) { + toAdd.push(v); + } + } + }); + return value.concat(toAdd); + } + throw new Error('Cannot add elements to a non-array value'); + } + }, { + key: 'mergeWith', + value: function (previous) { + if (!previous) { + return this; + } + if (previous instanceof SetOp) { + return new SetOp(this.applyTo(previous._value)); + } + if (previous instanceof UnsetOp) { + return new SetOp(this._value); + } + if (previous instanceof AddUniqueOp) { + return new AddUniqueOp(this.applyTo(previous._value)); + } + throw new Error('Cannot merge AddUnique Op with the previous Op'); + } + }, { + key: 'toJSON', + value: function () { + return { __op: 'AddUnique', objects: (0, _encode2.default)(this._value, false, true) }; + } + }]); + return AddUniqueOp; +}(Op); + +var RemoveOp = exports.RemoveOp = function (_Op6) { + (0, _inherits3.default)(RemoveOp, _Op6); + + function RemoveOp(value) { + (0, _classCallCheck3.default)(this, RemoveOp); + + var _this6 = (0, _possibleConstructorReturn3.default)(this, (RemoveOp.__proto__ || (0, _getPrototypeOf2.default)(RemoveOp)).call(this)); + + _this6._value = (0, _unique2.default)(Array.isArray(value) ? value : [value]); + return _this6; + } + + (0, _createClass3.default)(RemoveOp, [{ + key: 'applyTo', + value: function (value) { + if (value == null) { + return []; + } + if (Array.isArray(value)) { + var i = value.indexOf(this._value); + var removed = value.concat([]); + for (var i = 0; i < this._value.length; i++) { + var index = removed.indexOf(this._value[i]); + while (index > -1) { + removed.splice(index, 1); + index = removed.indexOf(this._value[i]); + } + if (this._value[i] instanceof _ParseObject2.default && this._value[i].id) { + for (var j = 0; j < removed.length; j++) { + if (removed[j] instanceof _ParseObject2.default && this._value[i].id === removed[j].id) { + removed.splice(j, 1); + j--; + } + } + } + } + return removed; + } + throw new Error('Cannot remove elements from a non-array value'); + } + }, { + key: 'mergeWith', + value: function (previous) { + if (!previous) { + return this; + } + if (previous instanceof SetOp) { + return new SetOp(this.applyTo(previous._value)); + } + if (previous instanceof UnsetOp) { + return new UnsetOp(); + } + if (previous instanceof RemoveOp) { + var uniques = previous._value.concat([]); + for (var i = 0; i < this._value.length; i++) { + if (this._value[i] instanceof _ParseObject2.default) { + if (!(0, _arrayContainsObject2.default)(uniques, this._value[i])) { + uniques.push(this._value[i]); + } + } else { + if (uniques.indexOf(this._value[i]) < 0) { + uniques.push(this._value[i]); + } + } + } + return new RemoveOp(uniques); + } + throw new Error('Cannot merge Remove Op with the previous Op'); + } + }, { + key: 'toJSON', + value: function () { + return { __op: 'Remove', objects: (0, _encode2.default)(this._value, false, true) }; + } + }]); + return RemoveOp; +}(Op); + +var RelationOp = exports.RelationOp = function (_Op7) { + (0, _inherits3.default)(RelationOp, _Op7); + + function RelationOp(adds, removes) { + (0, _classCallCheck3.default)(this, RelationOp); + + var _this7 = (0, _possibleConstructorReturn3.default)(this, (RelationOp.__proto__ || (0, _getPrototypeOf2.default)(RelationOp)).call(this)); + + _this7._targetClassName = null; + + if (Array.isArray(adds)) { + _this7.relationsToAdd = (0, _unique2.default)(adds.map(_this7._extractId, _this7)); + } + + if (Array.isArray(removes)) { + _this7.relationsToRemove = (0, _unique2.default)(removes.map(_this7._extractId, _this7)); + } + return _this7; + } + + (0, _createClass3.default)(RelationOp, [{ + key: '_extractId', + value: function (obj) { + if (typeof obj === 'string') { + return obj; + } + if (!obj.id) { + throw new Error('You cannot add or remove an unsaved Parse Object from a relation'); + } + if (!this._targetClassName) { + this._targetClassName = obj.className; + } + if (this._targetClassName !== obj.className) { + throw new Error('Tried to create a Relation with 2 different object types: ' + this._targetClassName + ' and ' + obj.className + '.'); + } + return obj.id; + } + }, { + key: 'applyTo', + value: function (value, object, key) { + if (!value) { + if (!object || !key) { + throw new Error('Cannot apply a RelationOp without either a previous value, or an object and a key'); + } + var parent = new _ParseObject2.default(object.className); + if (object.id && object.id.indexOf('local') === 0) { + parent._localId = object.id; + } else if (object.id) { + parent.id = object.id; + } + var relation = new _ParseRelation2.default(parent, key); + relation.targetClassName = this._targetClassName; + return relation; + } + if (value instanceof _ParseRelation2.default) { + if (this._targetClassName) { + if (value.targetClassName) { + if (this._targetClassName !== value.targetClassName) { + throw new Error('Related object must be a ' + value.targetClassName + ', but a ' + this._targetClassName + ' was passed in.'); + } + } else { + value.targetClassName = this._targetClassName; + } + } + return value; + } else { + throw new Error('Relation cannot be applied to a non-relation field'); + } + } + }, { + key: 'mergeWith', + value: function (previous) { + if (!previous) { + return this; + } else if (previous instanceof UnsetOp) { + throw new Error('You cannot modify a relation after deleting it.'); + } else if (previous instanceof RelationOp) { + if (previous._targetClassName && previous._targetClassName !== this._targetClassName) { + throw new Error('Related object must be of class ' + previous._targetClassName + ', but ' + (this._targetClassName || 'null') + ' was passed in.'); + } + var newAdd = previous.relationsToAdd.concat([]); + this.relationsToRemove.forEach(function (r) { + var index = newAdd.indexOf(r); + if (index > -1) { + newAdd.splice(index, 1); + } + }); + this.relationsToAdd.forEach(function (r) { + var index = newAdd.indexOf(r); + if (index < 0) { + newAdd.push(r); + } + }); + + var newRemove = previous.relationsToRemove.concat([]); + this.relationsToAdd.forEach(function (r) { + var index = newRemove.indexOf(r); + if (index > -1) { + newRemove.splice(index, 1); + } + }); + this.relationsToRemove.forEach(function (r) { + var index = newRemove.indexOf(r); + if (index < 0) { + newRemove.push(r); + } + }); + + var newRelation = new RelationOp(newAdd, newRemove); + newRelation._targetClassName = this._targetClassName; + return newRelation; + } + throw new Error('Cannot merge Relation Op with the previous Op'); + } + }, { + key: 'toJSON', + value: function () { + var _this8 = this; + + var idToPointer = function (id) { + return { + __type: 'Pointer', + className: _this8._targetClassName, + objectId: id + }; + }; + + var adds = null; + var removes = null; + var pointers = null; + + if (this.relationsToAdd.length > 0) { + pointers = this.relationsToAdd.map(idToPointer); + adds = { __op: 'AddRelation', objects: pointers }; + } + if (this.relationsToRemove.length > 0) { + pointers = this.relationsToRemove.map(idToPointer); + removes = { __op: 'RemoveRelation', objects: pointers }; + } + + if (adds && removes) { + return { __op: 'Batch', ops: [adds, removes] }; + } + + return adds || removes || {}; + } + }]); + return RelationOp; +}(Op); +},{"./ParseObject":18,"./ParseRelation":22,"./arrayContainsObject":33,"./decode":35,"./encode":36,"./unique":41,"babel-runtime/core-js/object/get-prototype-of":50,"babel-runtime/helpers/classCallCheck":56,"babel-runtime/helpers/createClass":57,"babel-runtime/helpers/inherits":59,"babel-runtime/helpers/possibleConstructorReturn":60}],20:[function(_dereq_,module,exports){ +(function (process){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _getIterator2 = _dereq_('babel-runtime/core-js/get-iterator'); + +var _getIterator3 = _interopRequireDefault(_getIterator2); + +var _typeof2 = _dereq_('babel-runtime/helpers/typeof'); + +var _typeof3 = _interopRequireDefault(_typeof2); + +var _classCallCheck2 = _dereq_('babel-runtime/helpers/classCallCheck'); + +var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); + +var _createClass2 = _dereq_('babel-runtime/helpers/createClass'); + +var _createClass3 = _interopRequireDefault(_createClass2); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { default: obj }; +} + +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +var _isPromisesAPlusCompliant = true; + +/** + * A Promise is returned by async methods as a hook to provide callbacks to be + * called when the async task is fulfilled. + * + *
Typical usage would be like:
+ * query.find().then(function(results) { + * results[0].set("foo", "bar"); + * return results[0].saveAsync(); + * }).then(function(result) { + * console.log("Updated " + result.id); + * }); + *+ * + * @class Parse.Promise + * @constructor + */ + +var ParsePromise = function () { + function ParsePromise(executor) { + (0, _classCallCheck3.default)(this, ParsePromise); + + this._resolved = false; + this._rejected = false; + this._resolvedCallbacks = []; + this._rejectedCallbacks = []; + + if (typeof executor === 'function') { + executor(this.resolve.bind(this), this.reject.bind(this)); + } + } + + /** + * Marks this promise as fulfilled, firing any callbacks waiting on it. + * @method resolve + * @param {Object} result the result to pass to the callbacks. + */ + + (0, _createClass3.default)(ParsePromise, [{ + key: 'resolve', + value: function () { + if (this._resolved || this._rejected) { + throw new Error('A promise was resolved even though it had already been ' + (this._resolved ? 'resolved' : 'rejected') + '.'); + } + this._resolved = true; + + for (var _len = arguments.length, results = Array(_len), _key = 0; _key < _len; _key++) { + results[_key] = arguments[_key]; + } + + this._result = results; + for (var i = 0; i < this._resolvedCallbacks.length; i++) { + this._resolvedCallbacks[i].apply(this, results); + } + + this._resolvedCallbacks = []; + this._rejectedCallbacks = []; + } + + /** + * Marks this promise as fulfilled, firing any callbacks waiting on it. + * @method reject + * @param {Object} error the error to pass to the callbacks. + */ + + }, { + key: 'reject', + value: function (error) { + if (this._resolved || this._rejected) { + throw new Error('A promise was rejected even though it had already been ' + (this._resolved ? 'resolved' : 'rejected') + '.'); + } + this._rejected = true; + this._error = error; + for (var i = 0; i < this._rejectedCallbacks.length; i++) { + this._rejectedCallbacks[i](error); + } + this._resolvedCallbacks = []; + this._rejectedCallbacks = []; + } + + /** + * Adds callbacks to be called when this promise is fulfilled. Returns a new + * Promise that will be fulfilled when the callback is complete. It allows + * chaining. If the callback itself returns a Promise, then the one returned + * by "then" will not be fulfilled until that one returned by the callback + * is fulfilled. + * @method then + * @param {Function} resolvedCallback Function that is called when this + * Promise is resolved. Once the callback is complete, then the Promise + * returned by "then" will also be fulfilled. + * @param {Function} rejectedCallback Function that is called when this + * Promise is rejected with an error. Once the callback is complete, then + * the promise returned by "then" with be resolved successfully. If + * rejectedCallback is null, or it returns a rejected Promise, then the + * Promise returned by "then" will be rejected with that error. + * @return {Parse.Promise} A new Promise that will be fulfilled after this + * Promise is fulfilled and either callback has completed. If the callback + * returned a Promise, then this Promise will not be fulfilled until that + * one is. + */ + + }, { + key: 'then', + value: function (resolvedCallback, rejectedCallback) { + var _this = this; + + var promise = new ParsePromise(); + + var wrappedResolvedCallback = function () { + for (var _len2 = arguments.length, results = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { + results[_key2] = arguments[_key2]; + } + + if (typeof resolvedCallback === 'function') { + if (_isPromisesAPlusCompliant) { + try { + results = [resolvedCallback.apply(this, results)]; + } catch (e) { + results = [ParsePromise.error(e)]; + } + } else { + results = [resolvedCallback.apply(this, results)]; + } + } + if (results.length === 1 && ParsePromise.is(results[0])) { + results[0].then(function () { + promise.resolve.apply(promise, arguments); + }, function (error) { + promise.reject(error); + }); + } else { + promise.resolve.apply(promise, results); + } + }; + + var wrappedRejectedCallback = function (error) { + var result = []; + if (typeof rejectedCallback === 'function') { + if (_isPromisesAPlusCompliant) { + try { + result = [rejectedCallback(error)]; + } catch (e) { + result = [ParsePromise.error(e)]; + } + } else { + result = [rejectedCallback(error)]; + } + if (result.length === 1 && ParsePromise.is(result[0])) { + result[0].then(function () { + promise.resolve.apply(promise, arguments); + }, function (error) { + promise.reject(error); + }); + } else { + if (_isPromisesAPlusCompliant) { + promise.resolve.apply(promise, result); + } else { + promise.reject(result[0]); + } + } + } else { + promise.reject(error); + } + }; + + var runLater = function (fn) { + fn.call(); + }; + if (_isPromisesAPlusCompliant) { + if (typeof process !== 'undefined' && typeof process.nextTick === 'function') { + runLater = function (fn) { + process.nextTick(fn); + }; + } else if (typeof setTimeout === 'function') { + runLater = function (fn) { + setTimeout(fn, 0); + }; + } + } + + if (this._resolved) { + runLater(function () { + wrappedResolvedCallback.apply(_this, _this._result); + }); + } else if (this._rejected) { + runLater(function () { + wrappedRejectedCallback(_this._error); + }); + } else { + this._resolvedCallbacks.push(wrappedResolvedCallback); + this._rejectedCallbacks.push(wrappedRejectedCallback); + } + + return promise; + } + + /** + * Add handlers to be called when the promise + * is either resolved or rejected + * @method always + */ + + }, { + key: 'always', + value: function (callback) { + return this.then(callback, callback); + } + + /** + * Add handlers to be called when the Promise object is resolved + * @method done + */ + + }, { + key: 'done', + value: function (callback) { + return this.then(callback); + } + + /** + * Add handlers to be called when the Promise object is rejected + * Alias for catch(). + * @method fail + */ + + }, { + key: 'fail', + value: function (callback) { + return this.then(null, callback); + } + + /** + * Add handlers to be called when the Promise object is rejected + * @method catch + */ + + }, { + key: 'catch', + value: function (callback) { + return this.then(null, callback); + } + + /** + * Run the given callbacks after this promise is fulfilled. + * @method _thenRunCallbacks + * @param optionsOrCallback {} A Backbone-style options callback, or a + * callback function. If this is an options object and contains a "model" + * attributes, that will be passed to error callbacks as the first argument. + * @param model {} If truthy, this will be passed as the first result of + * error callbacks. This is for Backbone-compatability. + * @return {Parse.Promise} A promise that will be resolved after the + * callbacks are run, with the same result as this. + */ + + }, { + key: '_thenRunCallbacks', + value: function (optionsOrCallback, model) { + var options = {}; + if (typeof optionsOrCallback === 'function') { + options.success = function (result) { + optionsOrCallback(result, null); + }; + options.error = function (error) { + optionsOrCallback(null, error); + }; + } else if ((typeof optionsOrCallback === 'undefined' ? 'undefined' : (0, _typeof3.default)(optionsOrCallback)) === 'object') { + if (typeof optionsOrCallback.success === 'function') { + options.success = optionsOrCallback.success; + } + if (typeof optionsOrCallback.error === 'function') { + options.error = optionsOrCallback.error; + } + } + + return this.then(function () { + for (var _len3 = arguments.length, results = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) { + results[_key3] = arguments[_key3]; + } + + if (options.success) { + options.success.apply(this, results); + } + return ParsePromise.as.apply(ParsePromise, arguments); + }, function (error) { + if (options.error) { + if (typeof model !== 'undefined') { + options.error(model, error); + } else { + options.error(error); + } + } + // By explicitly returning a rejected Promise, this will work with + // either jQuery or Promises/A+ semantics. + return ParsePromise.error(error); + }); + } + + /** + * Adds a callback function that should be called regardless of whether + * this promise failed or succeeded. The callback will be given either the + * array of results for its first argument, or the error as its second, + * depending on whether this Promise was rejected or resolved. Returns a + * new Promise, like "then" would. + * @method _continueWith + * @param {Function} continuation the callback. + */ + + }, { + key: '_continueWith', + value: function (continuation) { + return this.then(function () { + for (var _len4 = arguments.length, args = Array(_len4), _key4 = 0; _key4 < _len4; _key4++) { + args[_key4] = arguments[_key4]; + } + + return continuation(args, null); + }, function (error) { + return continuation(null, error); + }); + } + + /** + * Returns true iff the given object fulfils the Promise interface. + * @method is + * @param {Object} promise The object to test + * @static + * @return {Boolean} + */ + + }], [{ + key: 'is', + value: function (promise) { + return promise != null && typeof promise.then === 'function'; + } + + /** + * Returns a new promise that is resolved with a given value. + * @method as + * @param value The value to resolve the promise with + * @static + * @return {Parse.Promise} the new promise. + */ + + }, { + key: 'as', + value: function () { + var promise = new ParsePromise(); + + for (var _len5 = arguments.length, values = Array(_len5), _key5 = 0; _key5 < _len5; _key5++) { + values[_key5] = arguments[_key5]; + } + + promise.resolve.apply(promise, values); + return promise; + } + + /** + * Returns a new promise that is resolved with a given value. + * If that value is a thenable Promise (has a .then() prototype + * method), the new promise will be chained to the end of the + * value. + * @method resolve + * @param value The value to resolve the promise with + * @static + * @return {Parse.Promise} the new promise. + */ + + }, { + key: 'resolve', + value: function (value) { + return new ParsePromise(function (resolve, reject) { + if (ParsePromise.is(value)) { + value.then(resolve, reject); + } else { + resolve(value); + } + }); + } + + /** + * Returns a new promise that is rejected with a given error. + * @method error + * @param error The error to reject the promise with + * @static + * @return {Parse.Promise} the new promise. + */ + + }, { + key: 'error', + value: function () { + var promise = new ParsePromise(); + + for (var _len6 = arguments.length, errors = Array(_len6), _key6 = 0; _key6 < _len6; _key6++) { + errors[_key6] = arguments[_key6]; + } + + promise.reject.apply(promise, errors); + return promise; + } + + /** + * Returns a new promise that is rejected with a given error. + * This is an alias for Parse.Promise.error, for compliance with + * the ES6 implementation. + * @method reject + * @param error The error to reject the promise with + * @static + * @return {Parse.Promise} the new promise. + */ + + }, { + key: 'reject', + value: function () { + for (var _len7 = arguments.length, errors = Array(_len7), _key7 = 0; _key7 < _len7; _key7++) { + errors[_key7] = arguments[_key7]; + } + + return ParsePromise.error.apply(null, errors); + } + + /** + * Returns a new promise that is fulfilled when all of the input promises + * are resolved. If any promise in the list fails, then the returned promise + * will be rejected with an array containing the error from each promise. + * If they all succeed, then the returned promise will succeed, with the + * results being the results of all the input + * promises. For example:
+ * var p1 = Parse.Promise.as(1); + * var p2 = Parse.Promise.as(2); + * var p3 = Parse.Promise.as(3); + * + * Parse.Promise.when(p1, p2, p3).then(function(r1, r2, r3) { + * console.log(r1); // prints 1 + * console.log(r2); // prints 2 + * console.log(r3); // prints 3 + * });+ * + * The input promises can also be specified as an array:
+ * var promises = [p1, p2, p3]; + * Parse.Promise.when(promises).then(function(results) { + * console.log(results); // prints [1,2,3] + * }); + *+ * @method when + * @param {Array} promises a list of promises to wait for. + * @static + * @return {Parse.Promise} the new promise. + */ + + }, { + key: 'when', + value: function (promises) { + var objects; + var arrayArgument = Array.isArray(promises); + if (arrayArgument) { + objects = promises; + } else { + objects = arguments; + } + + var total = objects.length; + var hadError = false; + var results = []; + var returnValue = arrayArgument ? [results] : results; + var errors = []; + results.length = objects.length; + errors.length = objects.length; + + if (total === 0) { + return ParsePromise.as.apply(this, returnValue); + } + + var promise = new ParsePromise(); + + var resolveOne = function () { + total--; + if (total <= 0) { + if (hadError) { + promise.reject(errors); + } else { + promise.resolve.apply(promise, returnValue); + } + } + }; + + var chain = function (object, index) { + if (ParsePromise.is(object)) { + object.then(function (result) { + results[index] = result; + resolveOne(); + }, function (error) { + errors[index] = error; + hadError = true; + resolveOne(); + }); + } else { + results[i] = object; + resolveOne(); + } + }; + for (var i = 0; i < objects.length; i++) { + chain(objects[i], i); + } + + return promise; + } + + /** + * Returns a new promise that is fulfilled when all of the promises in the + * iterable argument are resolved. If any promise in the list fails, then + * the returned promise will be immediately rejected with the reason that + * single promise rejected. If they all succeed, then the returned promise + * will succeed, with the results being the results of all the input + * promises. If the iterable provided is empty, the returned promise will + * be immediately resolved. + * + * For example:
+ * var p1 = Parse.Promise.as(1); + * var p2 = Parse.Promise.as(2); + * var p3 = Parse.Promise.as(3); + * + * Parse.Promise.all([p1, p2, p3]).then(function([r1, r2, r3]) { + * console.log(r1); // prints 1 + * console.log(r2); // prints 2 + * console.log(r3); // prints 3 + * });+ * + * @method all + * @param {Iterable} promises an iterable of promises to wait for. + * @static + * @return {Parse.Promise} the new promise. + */ + + }, { + key: 'all', + value: function (promises) { + var total = 0; + var objects = []; + + var _iteratorNormalCompletion = true; + var _didIteratorError = false; + var _iteratorError = undefined; + + try { + for (var _iterator = (0, _getIterator3.default)(promises), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { + var p = _step.value; + + objects[total++] = p; + } + } catch (err) { + _didIteratorError = true; + _iteratorError = err; + } finally { + try { + if (!_iteratorNormalCompletion && _iterator.return) { + _iterator.return(); + } + } finally { + if (_didIteratorError) { + throw _iteratorError; + } + } + } + + if (total === 0) { + return ParsePromise.as([]); + } + + var hadError = false; + var promise = new ParsePromise(); + var resolved = 0; + var results = []; + objects.forEach(function (object, i) { + if (ParsePromise.is(object)) { + object.then(function (result) { + if (hadError) { + return false; + } + results[i] = result; + resolved++; + if (resolved >= total) { + promise.resolve(results); + } + }, function (error) { + // Reject immediately + promise.reject(error); + hadError = true; + }); + } else { + results[i] = object; + resolved++; + if (!hadError && resolved >= total) { + promise.resolve(results); + } + } + }); + + return promise; + } + + /** + * Returns a new promise that is immediately fulfilled when any of the + * promises in the iterable argument are resolved or rejected. If the + * first promise to complete is resolved, the returned promise will be + * resolved with the same value. Likewise, if the first promise to + * complete is rejected, the returned promise will be rejected with the + * same reason. + * + * @method race + * @param {Iterable} promises an iterable of promises to wait for. + * @static + * @return {Parse.Promise} the new promise. + */ + + }, { + key: 'race', + value: function (promises) { + var completed = false; + var promise = new ParsePromise(); + var _iteratorNormalCompletion2 = true; + var _didIteratorError2 = false; + var _iteratorError2 = undefined; + + try { + for (var _iterator2 = (0, _getIterator3.default)(promises), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { + var p = _step2.value; + + if (ParsePromise.is(p)) { + p.then(function (result) { + if (completed) { + return; + } + completed = true; + promise.resolve(result); + }, function (error) { + if (completed) { + return; + } + completed = true; + promise.reject(error); + }); + } else if (!completed) { + completed = true; + promise.resolve(p); + } + } + } catch (err) { + _didIteratorError2 = true; + _iteratorError2 = err; + } finally { + try { + if (!_iteratorNormalCompletion2 && _iterator2.return) { + _iterator2.return(); + } + } finally { + if (_didIteratorError2) { + throw _iteratorError2; + } + } + } + + return promise; + } + + /** + * Runs the given asyncFunction repeatedly, as long as the predicate + * function returns a truthy value. Stops repeating if asyncFunction returns + * a rejected promise. + * @method _continueWhile + * @param {Function} predicate should return false when ready to stop. + * @param {Function} asyncFunction should return a Promise. + * @static + */ + + }, { + key: '_continueWhile', + value: function (predicate, asyncFunction) { + if (predicate()) { + return asyncFunction().then(function () { + return ParsePromise._continueWhile(predicate, asyncFunction); + }); + } + return ParsePromise.as(); + } + }, { + key: 'isPromisesAPlusCompliant', + value: function () { + return _isPromisesAPlusCompliant; + } + }, { + key: 'enableAPlusCompliant', + value: function () { + _isPromisesAPlusCompliant = true; + } + }, { + key: 'disableAPlusCompliant', + value: function () { + _isPromisesAPlusCompliant = false; + } + }]); + return ParsePromise; +}(); + +exports.default = ParsePromise; +}).call(this,_dereq_('_process')) +},{"_process":168,"babel-runtime/core-js/get-iterator":43,"babel-runtime/helpers/classCallCheck":56,"babel-runtime/helpers/createClass":57,"babel-runtime/helpers/typeof":61}],21:[function(_dereq_,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _typeof2 = _dereq_('babel-runtime/helpers/typeof'); + +var _typeof3 = _interopRequireDefault(_typeof2); + +var _classCallCheck2 = _dereq_('babel-runtime/helpers/classCallCheck'); + +var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); + +var _createClass2 = _dereq_('babel-runtime/helpers/createClass'); + +var _createClass3 = _interopRequireDefault(_createClass2); + +var _keys = _dereq_('babel-runtime/core-js/object/keys'); + +var _keys2 = _interopRequireDefault(_keys); + +var _CoreManager = _dereq_('./CoreManager'); + +var _CoreManager2 = _interopRequireDefault(_CoreManager); + +var _encode = _dereq_('./encode'); + +var _encode2 = _interopRequireDefault(_encode); + +var _ParseError = _dereq_('./ParseError'); + +var _ParseError2 = _interopRequireDefault(_ParseError); + +var _ParseGeoPoint = _dereq_('./ParseGeoPoint'); + +var _ParseGeoPoint2 = _interopRequireDefault(_ParseGeoPoint); + +var _ParseObject = _dereq_('./ParseObject'); + +var _ParseObject2 = _interopRequireDefault(_ParseObject); + +var _ParsePromise = _dereq_('./ParsePromise'); + +var _ParsePromise2 = _interopRequireDefault(_ParsePromise); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { default: obj }; +} + +/** + * Converts a string into a regex that matches it. + * Surrounding with \Q .. \E does this, we just need to escape any \E's in + * the text separately. + */ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + */ + +function quote(s) { + return '\\Q' + s.replace('\\E', '\\E\\\\E\\Q') + '\\E'; +} + +/** + * Handles pre-populating the result data of a query with select fields, + * making sure that the data object contains keys for all objects that have + * been requested with a select, so that our cached state updates correctly. + */ +function handleSelectResult(data, select) { + var serverDataMask = {}; + + select.forEach(function (field) { + var hasSubObjectSelect = field.indexOf(".") !== -1; + if (!hasSubObjectSelect && !data.hasOwnProperty(field)) { + // this field was selected, but is missing from the retrieved data + data[field] = undefined; + } else if (hasSubObjectSelect) { + // this field references a sub-object, + // so we need to walk down the path components + var pathComponents = field.split("."); + var obj = data; + var serverMask = serverDataMask; + + pathComponents.forEach(function (component, index, arr) { + // add keys if the expected data is missing + if (!obj[component]) { + obj[component] = index == arr.length - 1 ? undefined : {}; + } + obj = obj[component]; + + //add this path component to the server mask so we can fill it in later if needed + if (index < arr.length - 1) { + if (!serverMask[component]) { + serverMask[component] = {}; + } + } + }); + } + }); + + if ((0, _keys2.default)(serverDataMask).length > 0) { + var copyMissingDataWithMask = function copyMissingDataWithMask(src, dest, mask, copyThisLevel) { + //copy missing elements at this level + if (copyThisLevel) { + for (var key in src) { + if (src.hasOwnProperty(key) && !dest.hasOwnProperty(key)) { + dest[key] = src[key]; + } + } + } + for (var key in mask) { + //traverse into objects as needed + copyMissingDataWithMask(src[key], dest[key], mask[key], true); + } + }; + + // When selecting from sub-objects, we don't want to blow away the missing + // information that we may have retrieved before. We've already added any + // missing selected keys to sub-objects, but we still need to add in the + // data for any previously retrieved sub-objects that were not selected. + + var serverData = _CoreManager2.default.getObjectStateController().getServerData({ id: data.objectId, className: data.className }); + + copyMissingDataWithMask(serverData, data, serverDataMask, false); + } +} + +/** + * Creates a new parse Parse.Query for the given Parse.Object subclass. + * @class Parse.Query + * @constructor + * @param {} objectClass An instance of a subclass of Parse.Object, or a Parse className string. + * + *
Parse.Query defines a query that is used to fetch Parse.Objects. The
+ * most common use case is finding all objects that match a query through the
+ * find
method. For example, this sample code fetches all objects
+ * of class MyClass
. It calls a different function depending on
+ * whether the fetch succeeded or not.
+ *
+ *
+ * var query = new Parse.Query(MyClass); + * query.find({ + * success: function(results) { + * // results is an array of Parse.Object. + * }, + * + * error: function(error) { + * // error is an instance of Parse.Error. + * } + * });+ * + *
A Parse.Query can also be used to retrieve a single object whose id is
+ * known, through the get method. For example, this sample code fetches an
+ * object of class MyClass
and id myId
. It calls a
+ * different function depending on whether the fetch succeeded or not.
+ *
+ *
+ * var query = new Parse.Query(MyClass); + * query.get(myId, { + * success: function(object) { + * // object is an instance of Parse.Object. + * }, + * + * error: function(object, error) { + * // error is an instance of Parse.Error. + * } + * });+ * + *
A Parse.Query can also be used to count the number of objects that match
+ * the query without retrieving all of those objects. For example, this
+ * sample code counts the number of objects of the class MyClass
+ *
+ * var query = new Parse.Query(MyClass); + * query.count({ + * success: function(number) { + * // There are number instances of MyClass. + * }, + * + * error: function(error) { + * // error is an instance of Parse.Error. + * } + * });+ */ + +var ParseQuery = function () { + function ParseQuery(objectClass) { + (0, _classCallCheck3.default)(this, ParseQuery); + + if (typeof objectClass === 'string') { + if (objectClass === 'User' && _CoreManager2.default.get('PERFORM_USER_REWRITE')) { + this.className = '_User'; + } else { + this.className = objectClass; + } + } else if (objectClass instanceof _ParseObject2.default) { + this.className = objectClass.className; + } else if (typeof objectClass === 'function') { + if (typeof objectClass.className === 'string') { + this.className = objectClass.className; + } else { + var obj = new objectClass(); + this.className = obj.className; + } + } else { + throw new TypeError('A ParseQuery must be constructed with a ParseObject or class name.'); + } + + this._where = {}; + this._include = []; + this._limit = -1; // negative limit is not sent in the server request + this._skip = 0; + this._extraOptions = {}; + } + + /** + * Adds constraint that at least one of the passed in queries matches. + * @method _orQuery + * @param {Array} queries + * @return {Parse.Query} Returns the query, so you can chain this call. + */ + + (0, _createClass3.default)(ParseQuery, [{ + key: '_orQuery', + value: function (queries) { + var queryJSON = queries.map(function (q) { + return q.toJSON().where; + }); + + this._where.$or = queryJSON; + return this; + } + + /** + * Helper for condition queries + */ + + }, { + key: '_addCondition', + value: function (key, condition, value) { + if (!this._where[key] || typeof this._where[key] === 'string') { + this._where[key] = {}; + } + this._where[key][condition] = (0, _encode2.default)(value, false, true); + return this; + } + + /** + * Converts string for regular expression at the beginning + */ + + }, { + key: '_regexStartWith', + value: function (string) { + return '^' + quote(string); + } + + /** + * Returns a JSON representation of this query. + * @method toJSON + * @return {Object} The JSON representation of the query. + */ + + }, { + key: 'toJSON', + value: function () { + var params = { + where: this._where + }; + + if (this._include.length) { + params.include = this._include.join(','); + } + if (this._select) { + params.keys = this._select.join(','); + } + if (this._limit >= 0) { + params.limit = this._limit; + } + if (this._skip > 0) { + params.skip = this._skip; + } + if (this._order) { + params.order = this._order.join(','); + } + for (var key in this._extraOptions) { + params[key] = this._extraOptions[key]; + } + + return params; + } + + /** + * Constructs a Parse.Object whose id is already known by fetching data from + * the server. Either options.success or options.error is called when the + * find completes. + * + * @method get + * @param {String} objectId The id of the object to be fetched. + * @param {Object} options A Backbone-style options object. + * Valid options are:
var compoundQuery = Parse.Query.or(query1, query2, query3);+ * + * will create a compoundQuery that is an or of the query1, query2, and + * query3. + * @method or + * @param {...Parse.Query} var_args The list of queries to OR. + * @static + * @return {Parse.Query} The query that is the OR of the passed in queries. + */ + + }], [{ + key: 'or', + value: function () { + var className = null; + + for (var _len7 = arguments.length, queries = Array(_len7), _key7 = 0; _key7 < _len7; _key7++) { + queries[_key7] = arguments[_key7]; + } + + queries.forEach(function (q) { + if (!className) { + className = q.className; + } + + if (className !== q.className) { + throw new Error('All queries must be for the same class.'); + } + }); + + var query = new ParseQuery(className); + query._orQuery(queries); + return query; + } + }]); + return ParseQuery; +}(); + +exports.default = ParseQuery; + +var DefaultController = { + find: function (className, params, options) { + var RESTController = _CoreManager2.default.getRESTController(); + + return RESTController.request('GET', 'classes/' + className, params, options); + } +}; + +_CoreManager2.default.setQueryController(DefaultController); +},{"./CoreManager":3,"./ParseError":13,"./ParseGeoPoint":15,"./ParseObject":18,"./ParsePromise":20,"./encode":36,"babel-runtime/core-js/object/keys":51,"babel-runtime/helpers/classCallCheck":56,"babel-runtime/helpers/createClass":57,"babel-runtime/helpers/typeof":61}],22:[function(_dereq_,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _classCallCheck2 = _dereq_('babel-runtime/helpers/classCallCheck'); + +var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); + +var _createClass2 = _dereq_('babel-runtime/helpers/createClass'); + +var _createClass3 = _interopRequireDefault(_createClass2); + +var _ParseOp = _dereq_('./ParseOp'); + +var _ParseObject = _dereq_('./ParseObject'); + +var _ParseObject2 = _interopRequireDefault(_ParseObject); + +var _ParseQuery = _dereq_('./ParseQuery'); + +var _ParseQuery2 = _interopRequireDefault(_ParseQuery); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { default: obj }; +} + +/** + * Creates a new Relation for the given parent object and key. This + * constructor should rarely be used directly, but rather created by + * Parse.Object.relation. + * @class Parse.Relation + * @constructor + * @param {Parse.Object} parent The parent of this relation. + * @param {String} key The key for this relation on the parent. + * + *
+ * A class that is used to access all of the children of a many-to-many + * relationship. Each instance of Parse.Relation is associated with a + * particular parent object and key. + *
+ */ +var ParseRelation = function () { + function ParseRelation(parent, key) { + (0, _classCallCheck3.default)(this, ParseRelation); + + this.parent = parent; + this.key = key; + this.targetClassName = null; + } + + /** + * Makes sure that this relation has the right parent and key. + */ + + (0, _createClass3.default)(ParseRelation, [{ + key: '_ensureParentAndKey', + value: function (parent, key) { + this.key = this.key || key; + if (this.key !== key) { + throw new Error('Internal Error. Relation retrieved from two different keys.'); + } + if (this.parent) { + if (this.parent.className !== parent.className) { + throw new Error('Internal Error. Relation retrieved from two different Objects.'); + } + if (this.parent.id) { + if (this.parent.id !== parent.id) { + throw new Error('Internal Error. Relation retrieved from two different Objects.'); + } + } else if (parent.id) { + this.parent = parent; + } + } else { + this.parent = parent; + } + } + + /** + * Adds a Parse.Object or an array of Parse.Objects to the relation. + * @method add + * @param {} objects The item or items to add. + */ + + }, { + key: 'add', + value: function (objects) { + if (!Array.isArray(objects)) { + objects = [objects]; + } + + var change = new _ParseOp.RelationOp(objects, []); + var parent = this.parent; + if (!parent) { + throw new Error('Cannot add to a Relation without a parent'); + } + parent.set(this.key, change); + this.targetClassName = change._targetClassName; + return parent; + } + + /** + * Removes a Parse.Object or an array of Parse.Objects from this relation. + * @method remove + * @param {} objects The item or items to remove. + */ + + }, { + key: 'remove', + value: function (objects) { + if (!Array.isArray(objects)) { + objects = [objects]; + } + + var change = new _ParseOp.RelationOp([], objects); + if (!this.parent) { + throw new Error('Cannot remove from a Relation without a parent'); + } + this.parent.set(this.key, change); + this.targetClassName = change._targetClassName; + } + + /** + * Returns a JSON version of the object suitable for saving to disk. + * @method toJSON + * @return {Object} + */ + + }, { + key: 'toJSON', + value: function () { + return { + __type: 'Relation', + className: this.targetClassName + }; + } + + /** + * Returns a Parse.Query that is limited to objects in this + * relation. + * @method query + * @return {Parse.Query} + */ + + }, { + key: 'query', + value: function () { + var query; + var parent = this.parent; + if (!parent) { + throw new Error('Cannot construct a query for a Relation without a parent'); + } + if (!this.targetClassName) { + query = new _ParseQuery2.default(parent.className); + query._extraOptions.redirectClassNameForKey = this.key; + } else { + query = new _ParseQuery2.default(this.targetClassName); + } + query._addCondition('$relatedTo', 'object', { + __type: 'Pointer', + className: parent.className, + objectId: parent.id + }); + query._addCondition('$relatedTo', 'key', this.key); + + return query; + } + }]); + return ParseRelation; +}(); /** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + */ + +exports.default = ParseRelation; +},{"./ParseObject":18,"./ParseOp":19,"./ParseQuery":21,"babel-runtime/helpers/classCallCheck":56,"babel-runtime/helpers/createClass":57}],23:[function(_dereq_,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _getPrototypeOf = _dereq_('babel-runtime/core-js/object/get-prototype-of'); + +var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); + +var _classCallCheck2 = _dereq_('babel-runtime/helpers/classCallCheck'); + +var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); + +var _createClass2 = _dereq_('babel-runtime/helpers/createClass'); + +var _createClass3 = _interopRequireDefault(_createClass2); + +var _possibleConstructorReturn2 = _dereq_('babel-runtime/helpers/possibleConstructorReturn'); + +var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); + +var _get2 = _dereq_('babel-runtime/helpers/get'); + +var _get3 = _interopRequireDefault(_get2); + +var _inherits2 = _dereq_('babel-runtime/helpers/inherits'); + +var _inherits3 = _interopRequireDefault(_inherits2); + +var _ParseACL = _dereq_('./ParseACL'); + +var _ParseACL2 = _interopRequireDefault(_ParseACL); + +var _ParseError = _dereq_('./ParseError'); + +var _ParseError2 = _interopRequireDefault(_ParseError); + +var _ParseObject2 = _dereq_('./ParseObject'); + +var _ParseObject3 = _interopRequireDefault(_ParseObject2); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { default: obj }; +} + +/** + * Represents a Role on the Parse server. Roles represent groupings of + * Users for the purposes of granting permissions (e.g. specifying an ACL + * for an Object). Roles are specified by their sets of child users and + * child roles, all of which are granted any permissions that the parent + * role has. + * + *Roles must have a name (which cannot be changed after creation of the + * role), and must specify an ACL.
+ * @class Parse.Role + * @constructor + * @param {String} name The name of the Role to create. + * @param {Parse.ACL} acl The ACL for this role. Roles must have an ACL. + * A Parse.Role is a local representation of a role persisted to the Parse + * cloud. + */ +var ParseRole = function (_ParseObject) { + (0, _inherits3.default)(ParseRole, _ParseObject); + + function ParseRole(name, acl) { + (0, _classCallCheck3.default)(this, ParseRole); + + var _this = (0, _possibleConstructorReturn3.default)(this, (ParseRole.__proto__ || (0, _getPrototypeOf2.default)(ParseRole)).call(this, '_Role')); + + if (typeof name === 'string' && acl instanceof _ParseACL2.default) { + _this.setName(name); + _this.setACL(acl); + } + return _this; + } + + /** + * Gets the name of the role. You can alternatively call role.get("name") + * + * @method getName + * @return {String} the name of the role. + */ + + (0, _createClass3.default)(ParseRole, [{ + key: 'getName', + value: function () { + var name = this.get('name'); + if (name == null || typeof name === 'string') { + return name; + } + return ''; + } + + /** + * Sets the name for a role. This value must be set before the role has + * been saved to the server, and cannot be set once the role has been + * saved. + * + *+ * A role's name can only contain alphanumeric characters, _, -, and + * spaces. + *
+ * + *This is equivalent to calling role.set("name", name)
+ * + * @method setName + * @param {String} name The name of the role. + * @param {Object} options Standard options object with success and error + * callbacks. + */ + + }, { + key: 'setName', + value: function (name, options) { + return this.set('name', name, options); + } + + /** + * Gets the Parse.Relation for the Parse.Users that are direct + * children of this role. These users are granted any privileges that this + * role has been granted (e.g. read or write access through ACLs). You can + * add or remove users from the role through this relation. + * + *This is equivalent to calling role.relation("users")
+ * + * @method getUsers + * @return {Parse.Relation} the relation for the users belonging to this + * role. + */ + + }, { + key: 'getUsers', + value: function () { + return this.relation('users'); + } + + /** + * Gets the Parse.Relation for the Parse.Roles that are direct + * children of this role. These roles' users are granted any privileges that + * this role has been granted (e.g. read or write access through ACLs). You + * can add or remove child roles from this role through this relation. + * + *This is equivalent to calling role.relation("roles")
+ * + * @method getRoles + * @return {Parse.Relation} the relation for the roles belonging to this + * role. + */ + + }, { + key: 'getRoles', + value: function () { + return this.relation('roles'); + } + }, { + key: 'validate', + value: function (attrs, options) { + var isInvalid = (0, _get3.default)(ParseRole.prototype.__proto__ || (0, _getPrototypeOf2.default)(ParseRole.prototype), 'validate', this).call(this, attrs, options); + if (isInvalid) { + return isInvalid; + } + + if ('name' in attrs && attrs.name !== this.getName()) { + var newName = attrs.name; + if (this.id && this.id !== attrs.objectId) { + // Check to see if the objectId being set matches this.id + // This happens during a fetch -- the id is set before calling fetch + // Let the name be set in this case + return new _ParseError2.default(_ParseError2.default.OTHER_CAUSE, 'A role\'s name can only be set before it has been saved.'); + } + if (typeof newName !== 'string') { + return new _ParseError2.default(_ParseError2.default.OTHER_CAUSE, 'A role\'s name must be a String.'); + } + if (!/^[0-9a-zA-Z\-_ ]+$/.test(newName)) { + return new _ParseError2.default(_ParseError2.default.OTHER_CAUSE, 'A role\'s name can be only contain alphanumeric characters, _, ' + '-, and spaces.'); + } + } + return false; + } + }]); + return ParseRole; +}(_ParseObject3.default); /** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + */ + +exports.default = ParseRole; + +_ParseObject3.default.registerSubclass('_Role', ParseRole); +},{"./ParseACL":11,"./ParseError":13,"./ParseObject":18,"babel-runtime/core-js/object/get-prototype-of":50,"babel-runtime/helpers/classCallCheck":56,"babel-runtime/helpers/createClass":57,"babel-runtime/helpers/get":58,"babel-runtime/helpers/inherits":59,"babel-runtime/helpers/possibleConstructorReturn":60}],24:[function(_dereq_,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _typeof2 = _dereq_('babel-runtime/helpers/typeof'); + +var _typeof3 = _interopRequireDefault(_typeof2); + +var _getPrototypeOf = _dereq_('babel-runtime/core-js/object/get-prototype-of'); + +var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); + +var _classCallCheck2 = _dereq_('babel-runtime/helpers/classCallCheck'); + +var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); + +var _createClass2 = _dereq_('babel-runtime/helpers/createClass'); + +var _createClass3 = _interopRequireDefault(_createClass2); + +var _possibleConstructorReturn2 = _dereq_('babel-runtime/helpers/possibleConstructorReturn'); + +var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); + +var _inherits2 = _dereq_('babel-runtime/helpers/inherits'); + +var _inherits3 = _interopRequireDefault(_inherits2); + +var _CoreManager = _dereq_('./CoreManager'); + +var _CoreManager2 = _interopRequireDefault(_CoreManager); + +var _isRevocableSession = _dereq_('./isRevocableSession'); + +var _isRevocableSession2 = _interopRequireDefault(_isRevocableSession); + +var _ParseObject2 = _dereq_('./ParseObject'); + +var _ParseObject3 = _interopRequireDefault(_ParseObject2); + +var _ParsePromise = _dereq_('./ParsePromise'); + +var _ParsePromise2 = _interopRequireDefault(_ParsePromise); + +var _ParseUser = _dereq_('./ParseUser'); + +var _ParseUser2 = _interopRequireDefault(_ParseUser); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { default: obj }; +} + +/** + * @class Parse.Session + * @constructor + * + *A Parse.Session object is a local representation of a revocable session. + * This class is a subclass of a Parse.Object, and retains the same + * functionality of a Parse.Object.
+ */ +var ParseSession = function (_ParseObject) { + (0, _inherits3.default)(ParseSession, _ParseObject); + + function ParseSession(attributes) { + (0, _classCallCheck3.default)(this, ParseSession); + + var _this = (0, _possibleConstructorReturn3.default)(this, (ParseSession.__proto__ || (0, _getPrototypeOf2.default)(ParseSession)).call(this, '_Session')); + + if (attributes && (typeof attributes === 'undefined' ? 'undefined' : (0, _typeof3.default)(attributes)) === 'object') { + if (!_this.set(attributes || {})) { + throw new Error('Can\'t create an invalid Session'); + } + } + return _this; + } + + /** + * Returns the session token string. + * @method getSessionToken + * @return {String} + */ + + (0, _createClass3.default)(ParseSession, [{ + key: 'getSessionToken', + value: function () { + var token = this.get('sessionToken'); + if (typeof token === 'string') { + return token; + } + return ''; + } + }], [{ + key: 'readOnlyAttributes', + value: function () { + return ['createdWith', 'expiresAt', 'installationId', 'restricted', 'sessionToken', 'user']; + } + + /** + * Retrieves the Session object for the currently logged in session. + * @method current + * @static + * @return {Parse.Promise} A promise that is resolved with the Parse.Session + * object after it has been fetched. If there is no current user, the + * promise will be rejected. + */ + + }, { + key: 'current', + value: function (options) { + options = options || {}; + var controller = _CoreManager2.default.getSessionController(); + + var sessionOptions = {}; + if (options.hasOwnProperty('useMasterKey')) { + sessionOptions.useMasterKey = options.useMasterKey; + } + return _ParseUser2.default.currentAsync().then(function (user) { + if (!user) { + return _ParsePromise2.default.error('There is no current user.'); + } + user.getSessionToken(); + + sessionOptions.sessionToken = user.getSessionToken(); + return controller.getSession(sessionOptions); + }); + } + + /** + * Determines whether the current session token is revocable. + * This method is useful for migrating Express.js or Node.js web apps to + * use revocable sessions. If you are migrating an app that uses the Parse + * SDK in the browser only, please use Parse.User.enableRevocableSession() + * instead, so that sessions can be automatically upgraded. + * @method isCurrentSessionRevocable + * @static + * @return {Boolean} + */ + + }, { + key: 'isCurrentSessionRevocable', + value: function () { + var currentUser = _ParseUser2.default.current(); + if (currentUser) { + return (0, _isRevocableSession2.default)(currentUser.getSessionToken() || ''); + } + return false; + } + }]); + return ParseSession; +}(_ParseObject3.default); /** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + */ + +exports.default = ParseSession; + +_ParseObject3.default.registerSubclass('_Session', ParseSession); + +var DefaultController = { + getSession: function (options) { + var RESTController = _CoreManager2.default.getRESTController(); + var session = new ParseSession(); + + return RESTController.request('GET', 'sessions/me', {}, options).then(function (sessionData) { + session._finishFetch(sessionData); + session._setExisted(true); + return session; + }); + } +}; + +_CoreManager2.default.setSessionController(DefaultController); +},{"./CoreManager":3,"./ParseObject":18,"./ParsePromise":20,"./ParseUser":25,"./isRevocableSession":39,"babel-runtime/core-js/object/get-prototype-of":50,"babel-runtime/helpers/classCallCheck":56,"babel-runtime/helpers/createClass":57,"babel-runtime/helpers/inherits":59,"babel-runtime/helpers/possibleConstructorReturn":60,"babel-runtime/helpers/typeof":61}],25:[function(_dereq_,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _stringify = _dereq_('babel-runtime/core-js/json/stringify'); + +var _stringify2 = _interopRequireDefault(_stringify); + +var _defineProperty = _dereq_('babel-runtime/core-js/object/define-property'); + +var _defineProperty2 = _interopRequireDefault(_defineProperty); + +var _typeof2 = _dereq_('babel-runtime/helpers/typeof'); + +var _typeof3 = _interopRequireDefault(_typeof2); + +var _getPrototypeOf = _dereq_('babel-runtime/core-js/object/get-prototype-of'); + +var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); + +var _classCallCheck2 = _dereq_('babel-runtime/helpers/classCallCheck'); + +var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); + +var _createClass2 = _dereq_('babel-runtime/helpers/createClass'); + +var _createClass3 = _interopRequireDefault(_createClass2); + +var _possibleConstructorReturn2 = _dereq_('babel-runtime/helpers/possibleConstructorReturn'); + +var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); + +var _get2 = _dereq_('babel-runtime/helpers/get'); + +var _get3 = _interopRequireDefault(_get2); + +var _inherits2 = _dereq_('babel-runtime/helpers/inherits'); + +var _inherits3 = _interopRequireDefault(_inherits2); + +var _CoreManager = _dereq_('./CoreManager'); + +var _CoreManager2 = _interopRequireDefault(_CoreManager); + +var _isRevocableSession = _dereq_('./isRevocableSession'); + +var _isRevocableSession2 = _interopRequireDefault(_isRevocableSession); + +var _ParseError = _dereq_('./ParseError'); + +var _ParseError2 = _interopRequireDefault(_ParseError); + +var _ParseObject2 = _dereq_('./ParseObject'); + +var _ParseObject3 = _interopRequireDefault(_ParseObject2); + +var _ParsePromise = _dereq_('./ParsePromise'); + +var _ParsePromise2 = _interopRequireDefault(_ParsePromise); + +var _ParseSession = _dereq_('./ParseSession'); + +var _ParseSession2 = _interopRequireDefault(_ParseSession); + +var _Storage = _dereq_('./Storage'); + +var _Storage2 = _interopRequireDefault(_Storage); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { default: obj }; +} + +var CURRENT_USER_KEY = 'currentUser'; /** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + */ + +var canUseCurrentUser = !_CoreManager2.default.get('IS_NODE'); +var currentUserCacheMatchesDisk = false; +var currentUserCache = null; + +var authProviders = {}; + +/** + * @class Parse.User + * @constructor + * + *A Parse.User object is a local representation of a user persisted to the + * Parse cloud. This class is a subclass of a Parse.Object, and retains the + * same functionality of a Parse.Object, but also extends it with various + * user specific methods, like authentication, signing up, and validation of + * uniqueness.
+ */ + +var ParseUser = function (_ParseObject) { + (0, _inherits3.default)(ParseUser, _ParseObject); + + function ParseUser(attributes) { + (0, _classCallCheck3.default)(this, ParseUser); + + var _this = (0, _possibleConstructorReturn3.default)(this, (ParseUser.__proto__ || (0, _getPrototypeOf2.default)(ParseUser)).call(this, '_User')); + + if (attributes && (typeof attributes === 'undefined' ? 'undefined' : (0, _typeof3.default)(attributes)) === 'object') { + if (!_this.set(attributes || {})) { + throw new Error('Can\'t create an invalid Parse User'); + } + } + return _this; + } + + /** + * Request a revocable session token to replace the older style of token. + * @method _upgradeToRevocableSession + * @param {Object} options A Backbone-style options object. + * @return {Parse.Promise} A promise that is resolved when the replacement + * token has been fetched. + */ + + (0, _createClass3.default)(ParseUser, [{ + key: '_upgradeToRevocableSession', + value: function (options) { + options = options || {}; + + var upgradeOptions = {}; + if (options.hasOwnProperty('useMasterKey')) { + upgradeOptions.useMasterKey = options.useMasterKey; + } + + var controller = _CoreManager2.default.getUserController(); + return controller.upgradeToRevocableSession(this, upgradeOptions)._thenRunCallbacks(options); + } + + /** + * Unlike in the Android/iOS SDKs, logInWith is unnecessary, since you can + * call linkWith on the user (even if it doesn't exist yet on the server). + * @method _linkWith + */ + + }, { + key: '_linkWith', + value: function (provider, options) { + var _this2 = this; + + var authType; + if (typeof provider === 'string') { + authType = provider; + provider = authProviders[provider]; + } else { + authType = provider.getAuthType(); + } + if (options && options.hasOwnProperty('authData')) { + var authData = this.get('authData') || {}; + if ((typeof authData === 'undefined' ? 'undefined' : (0, _typeof3.default)(authData)) !== 'object') { + throw new Error('Invalid type: authData field should be an object'); + } + authData[authType] = options.authData; + + var controller = _CoreManager2.default.getUserController(); + return controller.linkWith(this, authData)._thenRunCallbacks(options, this); + } else { + var promise = new _ParsePromise2.default(); + provider.authenticate({ + success: function (provider, result) { + var opts = {}; + opts.authData = result; + if (options.success) { + opts.success = options.success; + } + if (options.error) { + opts.error = options.error; + } + _this2._linkWith(provider, opts).then(function () { + promise.resolve(_this2); + }, function (error) { + promise.reject(error); + }); + }, + error: function (provider, _error) { + if (typeof options.error === 'function') { + options.error(_this2, _error); + } + promise.reject(_error); + } + }); + return promise; + } + } + + /** + * Synchronizes auth data for a provider (e.g. puts the access token in the + * right place to be used by the Facebook SDK). + * @method _synchronizeAuthData + */ + + }, { + key: '_synchronizeAuthData', + value: function (provider) { + if (!this.isCurrent() || !provider) { + return; + } + var authType; + if (typeof provider === 'string') { + authType = provider; + provider = authProviders[authType]; + } else { + authType = provider.getAuthType(); + } + var authData = this.get('authData'); + if (!provider || !authData || (typeof authData === 'undefined' ? 'undefined' : (0, _typeof3.default)(authData)) !== 'object') { + return; + } + var success = provider.restoreAuthentication(authData[authType]); + if (!success) { + this._unlinkFrom(provider); + } + } + + /** + * Synchronizes authData for all providers. + * @method _synchronizeAllAuthData + */ + + }, { + key: '_synchronizeAllAuthData', + value: function () { + var authData = this.get('authData'); + if ((typeof authData === 'undefined' ? 'undefined' : (0, _typeof3.default)(authData)) !== 'object') { + return; + } + + for (var key in authData) { + this._synchronizeAuthData(key); + } + } + + /** + * Removes null values from authData (which exist temporarily for + * unlinking) + * @method _cleanupAuthData + */ + + }, { + key: '_cleanupAuthData', + value: function () { + if (!this.isCurrent()) { + return; + } + var authData = this.get('authData'); + if ((typeof authData === 'undefined' ? 'undefined' : (0, _typeof3.default)(authData)) !== 'object') { + return; + } + + for (var key in authData) { + if (!authData[key]) { + delete authData[key]; + } + } + } + + /** + * Unlinks a user from a service. + * @method _unlinkFrom + */ + + }, { + key: '_unlinkFrom', + value: function (provider, options) { + var _this3 = this; + + if (typeof provider === 'string') { + provider = authProviders[provider]; + } else { + provider.getAuthType(); + } + return this._linkWith(provider, { authData: null }).then(function () { + _this3._synchronizeAuthData(provider); + return _ParsePromise2.default.as(_this3); + })._thenRunCallbacks(options); + } + + /** + * Checks whether a user is linked to a service. + * @method _isLinked + */ + + }, { + key: '_isLinked', + value: function (provider) { + var authType; + if (typeof provider === 'string') { + authType = provider; + } else { + authType = provider.getAuthType(); + } + var authData = this.get('authData') || {}; + if ((typeof authData === 'undefined' ? 'undefined' : (0, _typeof3.default)(authData)) !== 'object') { + return false; + } + return !!authData[authType]; + } + + /** + * Deauthenticates all providers. + * @method _logOutWithAll + */ + + }, { + key: '_logOutWithAll', + value: function () { + var authData = this.get('authData'); + if ((typeof authData === 'undefined' ? 'undefined' : (0, _typeof3.default)(authData)) !== 'object') { + return; + } + + for (var key in authData) { + this._logOutWith(key); + } + } + + /** + * Deauthenticates a single provider (e.g. removing access tokens from the + * Facebook SDK). + * @method _logOutWith + */ + + }, { + key: '_logOutWith', + value: function (provider) { + if (!this.isCurrent()) { + return; + } + if (typeof provider === 'string') { + provider = authProviders[provider]; + } + if (provider && provider.deauthenticate) { + provider.deauthenticate(); + } + } + + /** + * Class instance method used to maintain specific keys when a fetch occurs. + * Used to ensure that the session token is not lost. + */ + + }, { + key: '_preserveFieldsOnFetch', + value: function () { + return { + sessionToken: this.get('sessionToken') + }; + } + + /** + * Returns true ifcurrent
would return this user.
+ * @method isCurrent
+ * @return {Boolean}
+ */
+
+ }, {
+ key: 'isCurrent',
+ value: function () {
+ var current = ParseUser.current();
+ return !!current && current.id === this.id;
+ }
+
+ /**
+ * Returns get("username").
+ * @method getUsername
+ * @return {String}
+ */
+
+ }, {
+ key: 'getUsername',
+ value: function () {
+ var username = this.get('username');
+ if (username == null || typeof username === 'string') {
+ return username;
+ }
+ return '';
+ }
+
+ /**
+ * Calls set("username", username, options) and returns the result.
+ * @method setUsername
+ * @param {String} username
+ * @param {Object} options A Backbone-style options object.
+ * @return {Boolean}
+ */
+
+ }, {
+ key: 'setUsername',
+ value: function (username) {
+ // Strip anonymity, even we do not support anonymous user in js SDK, we may
+ // encounter anonymous user created by android/iOS in cloud code.
+ var authData = this.get('authData');
+ if (authData && (typeof authData === 'undefined' ? 'undefined' : (0, _typeof3.default)(authData)) === 'object' && authData.hasOwnProperty('anonymous')) {
+ // We need to set anonymous to null instead of deleting it in order to remove it from Parse.
+ authData.anonymous = null;
+ }
+ this.set('username', username);
+ }
+
+ /**
+ * Calls set("password", password, options) and returns the result.
+ * @method setPassword
+ * @param {String} password
+ * @param {Object} options A Backbone-style options object.
+ * @return {Boolean}
+ */
+
+ }, {
+ key: 'setPassword',
+ value: function (password) {
+ this.set('password', password);
+ }
+
+ /**
+ * Returns get("email").
+ * @method getEmail
+ * @return {String}
+ */
+
+ }, {
+ key: 'getEmail',
+ value: function () {
+ var email = this.get('email');
+ if (email == null || typeof email === 'string') {
+ return email;
+ }
+ return '';
+ }
+
+ /**
+ * Calls set("email", email, options) and returns the result.
+ * @method setEmail
+ * @param {String} email
+ * @param {Object} options A Backbone-style options object.
+ * @return {Boolean}
+ */
+
+ }, {
+ key: 'setEmail',
+ value: function (email) {
+ this.set('email', email);
+ }
+
+ /**
+ * Returns the session token for this user, if the user has been logged in,
+ * or if it is the result of a query with the master key. Otherwise, returns
+ * undefined.
+ * @method getSessionToken
+ * @return {String} the session token, or undefined
+ */
+
+ }, {
+ key: 'getSessionToken',
+ value: function () {
+ var token = this.get('sessionToken');
+ if (token == null || typeof token === 'string') {
+ return token;
+ }
+ return '';
+ }
+
+ /**
+ * Checks whether this user is the current user and has been authenticated.
+ * @method authenticated
+ * @return (Boolean) whether this user is the current user and is logged in.
+ */
+
+ }, {
+ key: 'authenticated',
+ value: function () {
+ var current = ParseUser.current();
+ return !!this.get('sessionToken') && !!current && current.id === this.id;
+ }
+
+ /**
+ * Signs up a new user. You should call this instead of save for
+ * new Parse.Users. This will create a new Parse.User on the server, and
+ * also persist the session on disk so that you can access the user using
+ * current
.
+ *
+ * A username and password must be set before calling signUp.
+ * + *Calls options.success or options.error on completion.
+ * + * @method signUp + * @param {Object} attrs Extra fields to set on the new user, or null. + * @param {Object} options A Backbone-style options object. + * @return {Parse.Promise} A promise that is fulfilled when the signup + * finishes. + */ + + }, { + key: 'signUp', + value: function (attrs, options) { + options = options || {}; + + var signupOptions = {}; + if (options.hasOwnProperty('useMasterKey')) { + signupOptions.useMasterKey = options.useMasterKey; + } + if (options.hasOwnProperty('installationId')) { + signupOptions.installationId = options.installationId; + } + + var controller = _CoreManager2.default.getUserController(); + return controller.signUp(this, attrs, signupOptions)._thenRunCallbacks(options, this); + } + + /** + * Logs in a Parse.User. On success, this saves the session to disk, + * so you can retrieve the currently logged in user using + *current
.
+ *
+ * A username and password must be set before calling logIn.
+ * + *Calls options.success or options.error on completion.
+ * + * @method logIn + * @param {Object} options A Backbone-style options object. + * @return {Parse.Promise} A promise that is fulfilled with the user when + * the login is complete. + */ + + }, { + key: 'logIn', + value: function (options) { + options = options || {}; + + var loginOptions = {}; + if (options.hasOwnProperty('useMasterKey')) { + loginOptions.useMasterKey = options.useMasterKey; + } + if (options.hasOwnProperty('installationId')) { + loginOptions.installationId = options.installationId; + } + + var controller = _CoreManager2.default.getUserController(); + return controller.logIn(this, loginOptions)._thenRunCallbacks(options, this); + } + + /** + * Wrap the default save behavior with functionality to save to local + * storage if this is current user. + */ + + }, { + key: 'save', + value: function () { + var _this4 = this; + + for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + return (0, _get3.default)(ParseUser.prototype.__proto__ || (0, _getPrototypeOf2.default)(ParseUser.prototype), 'save', this).apply(this, args).then(function () { + if (_this4.isCurrent()) { + return _CoreManager2.default.getUserController().updateUserOnDisk(_this4); + } + return _this4; + }); + } + + /** + * Wrap the default destroy behavior with functionality that logs out + * the current user when it is destroyed + */ + + }, { + key: 'destroy', + value: function () { + var _this5 = this; + + for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { + args[_key2] = arguments[_key2]; + } + + return (0, _get3.default)(ParseUser.prototype.__proto__ || (0, _getPrototypeOf2.default)(ParseUser.prototype), 'destroy', this).apply(this, args).then(function () { + if (_this5.isCurrent()) { + return _CoreManager2.default.getUserController().removeUserFromDisk(); + } + return _this5; + }); + } + + /** + * Wrap the default fetch behavior with functionality to save to local + * storage if this is current user. + */ + + }, { + key: 'fetch', + value: function () { + var _this6 = this; + + for (var _len3 = arguments.length, args = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) { + args[_key3] = arguments[_key3]; + } + + return (0, _get3.default)(ParseUser.prototype.__proto__ || (0, _getPrototypeOf2.default)(ParseUser.prototype), 'fetch', this).apply(this, args).then(function () { + if (_this6.isCurrent()) { + return _CoreManager2.default.getUserController().updateUserOnDisk(_this6); + } + return _this6; + }); + } + }], [{ + key: 'readOnlyAttributes', + value: function () { + return ['sessionToken']; + } + + /** + * Adds functionality to the existing Parse.User class + * @method extend + * @param {Object} protoProps A set of properties to add to the prototype + * @param {Object} classProps A set of static properties to add to the class + * @static + * @return {Class} The newly extended Parse.User class + */ + + }, { + key: 'extend', + value: function (protoProps, classProps) { + if (protoProps) { + for (var prop in protoProps) { + if (prop !== 'className') { + (0, _defineProperty2.default)(ParseUser.prototype, prop, { + value: protoProps[prop], + enumerable: false, + writable: true, + configurable: true + }); + } + } + } + + if (classProps) { + for (var prop in classProps) { + if (prop !== 'className') { + (0, _defineProperty2.default)(ParseUser, prop, { + value: classProps[prop], + enumerable: false, + writable: true, + configurable: true + }); + } + } + } + + return ParseUser; + } + + /** + * Retrieves the currently logged in ParseUser with a valid session, + * either from memory or localStorage, if necessary. + * @method current + * @static + * @return {Parse.Object} The currently logged in Parse.User. + */ + + }, { + key: 'current', + value: function () { + if (!canUseCurrentUser) { + return null; + } + var controller = _CoreManager2.default.getUserController(); + return controller.currentUser(); + } + + /** + * Retrieves the currently logged in ParseUser from asynchronous Storage. + * @method currentAsync + * @static + * @return {Parse.Promise} A Promise that is resolved with the currently + * logged in Parse User + */ + + }, { + key: 'currentAsync', + value: function () { + if (!canUseCurrentUser) { + return _ParsePromise2.default.as(null); + } + var controller = _CoreManager2.default.getUserController(); + return controller.currentUserAsync(); + } + + /** + * Signs up a new user with a username (or email) and password. + * This will create a new Parse.User on the server, and also persist the + * session in localStorage so that you can access the user using + * {@link #current}. + * + *Calls options.success or options.error on completion.
+ * + * @method signUp + * @param {String} username The username (or email) to sign up with. + * @param {String} password The password to sign up with. + * @param {Object} attrs Extra fields to set on the new user. + * @param {Object} options A Backbone-style options object. + * @static + * @return {Parse.Promise} A promise that is fulfilled with the user when + * the signup completes. + */ + + }, { + key: 'signUp', + value: function (username, password, attrs, options) { + attrs = attrs || {}; + attrs.username = username; + attrs.password = password; + var user = new ParseUser(attrs); + return user.signUp({}, options); + } + + /** + * Logs in a user with a username (or email) and password. On success, this + * saves the session to disk, so you can retrieve the currently logged in + * user usingcurrent
.
+ *
+ * Calls options.success or options.error on completion.
+ * + * @method logIn + * @param {String} username The username (or email) to log in with. + * @param {String} password The password to log in with. + * @param {Object} options A Backbone-style options object. + * @static + * @return {Parse.Promise} A promise that is fulfilled with the user when + * the login completes. + */ + + }, { + key: 'logIn', + value: function (username, password, options) { + if (typeof username !== 'string') { + return _ParsePromise2.default.error(new _ParseError2.default(_ParseError2.default.OTHER_CAUSE, 'Username must be a string.')); + } else if (typeof password !== 'string') { + return _ParsePromise2.default.error(new _ParseError2.default(_ParseError2.default.OTHER_CAUSE, 'Password must be a string.')); + } + var user = new ParseUser(); + user._finishFetch({ username: username, password: password }); + return user.logIn(options); + } + + /** + * Logs in a user with a session token. On success, this saves the session + * to disk, so you can retrieve the currently logged in user using + *current
.
+ *
+ * Calls options.success or options.error on completion.
+ * + * @method become + * @param {String} sessionToken The sessionToken to log in with. + * @param {Object} options A Backbone-style options object. + * @static + * @return {Parse.Promise} A promise that is fulfilled with the user when + * the login completes. + */ + + }, { + key: 'become', + value: function (sessionToken, options) { + if (!canUseCurrentUser) { + throw new Error('It is not memory-safe to become a user in a server environment'); + } + options = options || {}; + + var becomeOptions = { + sessionToken: sessionToken + }; + if (options.hasOwnProperty('useMasterKey')) { + becomeOptions.useMasterKey = options.useMasterKey; + } + + var controller = _CoreManager2.default.getUserController(); + return controller.become(becomeOptions)._thenRunCallbacks(options); + } + }, { + key: 'logInWith', + value: function (provider, options) { + return ParseUser._logInWith(provider, options); + } + + /** + * Logs out the currently logged in user session. This will remove the + * session from disk, log out of linked services, and future calls to + *current
will return null
.
+ * @method logOut
+ * @static
+ * @return {Parse.Promise} A promise that is resolved when the session is
+ * destroyed on the server.
+ */
+
+ }, {
+ key: 'logOut',
+ value: function () {
+ if (!canUseCurrentUser) {
+ throw new Error('There is no current user user on a node.js server environment.');
+ }
+
+ var controller = _CoreManager2.default.getUserController();
+ return controller.logOut();
+ }
+
+ /**
+ * Requests a password reset email to be sent to the specified email address
+ * associated with the user account. This email allows the user to securely
+ * reset their password on the Parse site.
+ *
+ * Calls options.success or options.error on completion.
+ * + * @method requestPasswordReset + * @param {String} email The email address associated with the user that + * forgot their password. + * @param {Object} options A Backbone-style options object. + * @static + */ + + }, { + key: 'requestPasswordReset', + value: function (email, options) { + options = options || {}; + + var requestOptions = {}; + if (options.hasOwnProperty('useMasterKey')) { + requestOptions.useMasterKey = options.useMasterKey; + } + + var controller = _CoreManager2.default.getUserController(); + return controller.requestPasswordReset(email, requestOptions)._thenRunCallbacks(options); + } + + /** + * Allow someone to define a custom User class without className + * being rewritten to _User. The default behavior is to rewrite + * User to _User for legacy reasons. This allows developers to + * override that behavior. + * + * @method allowCustomUserClass + * @param {Boolean} isAllowed Whether or not to allow custom User class + * @static + */ + + }, { + key: 'allowCustomUserClass', + value: function (isAllowed) { + _CoreManager2.default.set('PERFORM_USER_REWRITE', !isAllowed); + } + + /** + * Allows a legacy application to start using revocable sessions. If the + * current session token is not revocable, a request will be made for a new, + * revocable session. + * It is not necessary to call this method from cloud code unless you are + * handling user signup or login from the server side. In a cloud code call, + * this function will not attempt to upgrade the current token. + * @method enableRevocableSession + * @param {Object} options A Backbone-style options object. + * @static + * @return {Parse.Promise} A promise that is resolved when the process has + * completed. If a replacement session token is requested, the promise + * will be resolved after a new token has been fetched. + */ + + }, { + key: 'enableRevocableSession', + value: function (options) { + options = options || {}; + _CoreManager2.default.set('FORCE_REVOCABLE_SESSION', true); + if (canUseCurrentUser) { + var current = ParseUser.current(); + if (current) { + return current._upgradeToRevocableSession(options); + } + } + return _ParsePromise2.default.as()._thenRunCallbacks(options); + } + + /** + * Enables the use of become or the current user in a server + * environment. These features are disabled by default, since they depend on + * global objects that are not memory-safe for most servers. + * @method enableUnsafeCurrentUser + * @static + */ + + }, { + key: 'enableUnsafeCurrentUser', + value: function () { + canUseCurrentUser = true; + } + + /** + * Disables the use of become or the current user in any environment. + * These features are disabled on servers by default, since they depend on + * global objects that are not memory-safe for most servers. + * @method disableUnsafeCurrentUser + * @static + */ + + }, { + key: 'disableUnsafeCurrentUser', + value: function () { + canUseCurrentUser = false; + } + }, { + key: '_registerAuthenticationProvider', + value: function (provider) { + authProviders[provider.getAuthType()] = provider; + // Synchronize the current user with the auth provider. + ParseUser.currentAsync().then(function (current) { + if (current) { + current._synchronizeAuthData(provider.getAuthType()); + } + }); + } + }, { + key: '_logInWith', + value: function (provider, options) { + var user = new ParseUser(); + return user._linkWith(provider, options); + } + }, { + key: '_clearCache', + value: function () { + currentUserCache = null; + currentUserCacheMatchesDisk = false; + } + }, { + key: '_setCurrentUserCache', + value: function (user) { + currentUserCache = user; + } + }]); + return ParseUser; +}(_ParseObject3.default); + +exports.default = ParseUser; + +_ParseObject3.default.registerSubclass('_User', ParseUser); + +var DefaultController = { + updateUserOnDisk: function (user) { + var path = _Storage2.default.generatePath(CURRENT_USER_KEY); + var json = user.toJSON(); + json.className = '_User'; + return _Storage2.default.setItemAsync(path, (0, _stringify2.default)(json)).then(function () { + return user; + }); + }, + removeUserFromDisk: function () { + var path = _Storage2.default.generatePath(CURRENT_USER_KEY); + currentUserCacheMatchesDisk = true; + currentUserCache = null; + return _Storage2.default.removeItemAsync(path); + }, + setCurrentUser: function (user) { + currentUserCache = user; + user._cleanupAuthData(); + user._synchronizeAllAuthData(); + return DefaultController.updateUserOnDisk(user); + }, + currentUser: function () { + if (currentUserCache) { + return currentUserCache; + } + if (currentUserCacheMatchesDisk) { + return null; + } + if (_Storage2.default.async()) { + throw new Error('Cannot call currentUser() when using a platform with an async ' + 'storage system. Call currentUserAsync() instead.'); + } + var path = _Storage2.default.generatePath(CURRENT_USER_KEY); + var userData = _Storage2.default.getItem(path); + currentUserCacheMatchesDisk = true; + if (!userData) { + currentUserCache = null; + return null; + } + userData = JSON.parse(userData); + if (!userData.className) { + userData.className = '_User'; + } + if (userData._id) { + if (userData.objectId !== userData._id) { + userData.objectId = userData._id; + } + delete userData._id; + } + if (userData._sessionToken) { + userData.sessionToken = userData._sessionToken; + delete userData._sessionToken; + } + var current = _ParseObject3.default.fromJSON(userData); + currentUserCache = current; + current._synchronizeAllAuthData(); + return current; + }, + currentUserAsync: function () { + if (currentUserCache) { + return _ParsePromise2.default.as(currentUserCache); + } + if (currentUserCacheMatchesDisk) { + return _ParsePromise2.default.as(null); + } + var path = _Storage2.default.generatePath(CURRENT_USER_KEY); + return _Storage2.default.getItemAsync(path).then(function (userData) { + currentUserCacheMatchesDisk = true; + if (!userData) { + currentUserCache = null; + return _ParsePromise2.default.as(null); + } + userData = JSON.parse(userData); + if (!userData.className) { + userData.className = '_User'; + } + if (userData._id) { + if (userData.objectId !== userData._id) { + userData.objectId = userData._id; + } + delete userData._id; + } + if (userData._sessionToken) { + userData.sessionToken = userData._sessionToken; + delete userData._sessionToken; + } + var current = _ParseObject3.default.fromJSON(userData); + currentUserCache = current; + current._synchronizeAllAuthData(); + return _ParsePromise2.default.as(current); + }); + }, + signUp: function (user, attrs, options) { + var username = attrs && attrs.username || user.get('username'); + var password = attrs && attrs.password || user.get('password'); + + if (!username || !username.length) { + return _ParsePromise2.default.error(new _ParseError2.default(_ParseError2.default.OTHER_CAUSE, 'Cannot sign up user with an empty name.')); + } + if (!password || !password.length) { + return _ParsePromise2.default.error(new _ParseError2.default(_ParseError2.default.OTHER_CAUSE, 'Cannot sign up user with an empty password.')); + } + + return user.save(attrs, options).then(function () { + // Clear the password field + user._finishFetch({ password: undefined }); + + if (canUseCurrentUser) { + return DefaultController.setCurrentUser(user); + } + return user; + }); + }, + logIn: function (user, options) { + var RESTController = _CoreManager2.default.getRESTController(); + var stateController = _CoreManager2.default.getObjectStateController(); + var auth = { + username: user.get('username'), + password: user.get('password') + }; + return RESTController.request('GET', 'login', auth, options).then(function (response, status) { + user._migrateId(response.objectId); + user._setExisted(true); + stateController.setPendingOp(user._getStateIdentifier(), 'username', undefined); + stateController.setPendingOp(user._getStateIdentifier(), 'password', undefined); + response.password = undefined; + user._finishFetch(response); + if (!canUseCurrentUser) { + // We can't set the current user, so just return the one we logged in + return _ParsePromise2.default.as(user); + } + return DefaultController.setCurrentUser(user); + }); + }, + become: function (options) { + var user = new ParseUser(); + var RESTController = _CoreManager2.default.getRESTController(); + return RESTController.request('GET', 'users/me', {}, options).then(function (response, status) { + user._finishFetch(response); + user._setExisted(true); + return DefaultController.setCurrentUser(user); + }); + }, + logOut: function () { + return DefaultController.currentUserAsync().then(function (currentUser) { + var path = _Storage2.default.generatePath(CURRENT_USER_KEY); + var promise = _Storage2.default.removeItemAsync(path); + var RESTController = _CoreManager2.default.getRESTController(); + if (currentUser !== null) { + var currentSession = currentUser.getSessionToken(); + if (currentSession && (0, _isRevocableSession2.default)(currentSession)) { + promise = promise.then(function () { + return RESTController.request('POST', 'logout', {}, { sessionToken: currentSession }); + }); + } + currentUser._logOutWithAll(); + currentUser._finishFetch({ sessionToken: undefined }); + } + currentUserCacheMatchesDisk = true; + currentUserCache = null; + + return promise; + }); + }, + requestPasswordReset: function (email, options) { + var RESTController = _CoreManager2.default.getRESTController(); + return RESTController.request('POST', 'requestPasswordReset', { email: email }, options); + }, + upgradeToRevocableSession: function (user, options) { + var token = user.getSessionToken(); + if (!token) { + return _ParsePromise2.default.error(new _ParseError2.default(_ParseError2.default.SESSION_MISSING, 'Cannot upgrade a user with no session token')); + } + + options.sessionToken = token; + + var RESTController = _CoreManager2.default.getRESTController(); + return RESTController.request('POST', 'upgradeToRevocableSession', {}, options).then(function (result) { + var session = new _ParseSession2.default(); + session._finishFetch(result); + user._finishFetch({ sessionToken: session.getSessionToken() }); + if (user.isCurrent()) { + return DefaultController.setCurrentUser(user); + } + return _ParsePromise2.default.as(user); + }); + }, + linkWith: function (user, authData) { + return user.save({ authData: authData }).then(function () { + if (canUseCurrentUser) { + return DefaultController.setCurrentUser(user); + } + return user; + }); + } +}; + +_CoreManager2.default.setUserController(DefaultController); +},{"./CoreManager":3,"./ParseError":13,"./ParseObject":18,"./ParsePromise":20,"./ParseSession":24,"./Storage":29,"./isRevocableSession":39,"babel-runtime/core-js/json/stringify":44,"babel-runtime/core-js/object/define-property":47,"babel-runtime/core-js/object/get-prototype-of":50,"babel-runtime/helpers/classCallCheck":56,"babel-runtime/helpers/createClass":57,"babel-runtime/helpers/get":58,"babel-runtime/helpers/inherits":59,"babel-runtime/helpers/possibleConstructorReturn":60,"babel-runtime/helpers/typeof":61}],26:[function(_dereq_,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _typeof2 = _dereq_('babel-runtime/helpers/typeof'); + +var _typeof3 = _interopRequireDefault(_typeof2); + +exports.send = send; + +var _CoreManager = _dereq_('./CoreManager'); + +var _CoreManager2 = _interopRequireDefault(_CoreManager); + +var _ParseQuery = _dereq_('./ParseQuery'); + +var _ParseQuery2 = _interopRequireDefault(_ParseQuery); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { default: obj }; +} + +/** + * Contains functions to deal with Push in Parse. + * @class Parse.Push + * @static + */ + +/** + * Sends a push notification. + * @method send + * @param {Object} data - The data of the push notification. Valid fields + * are: + *+ * var dimensions = { + * gender: 'm', + * source: 'web', + * dayType: 'weekend' + * }; + * Parse.Analytics.track('signup', dimensions); + *+ * + * There is a default limit of 8 dimensions per event tracked. + * + * @method track + * @param {String} name The name of the custom event to report to Parse as + * having happened. + * @param {Object} dimensions The dictionary of information by which to + * segment this event. + * @param {Object} options A Backbone-style callback object. + * @return {Parse.Promise} A promise that is resolved when the round-trip + * to the server completes. + */ +function track(name, dimensions, options) { + name = name || ''; + name = name.replace(/^\s*/, ''); + name = name.replace(/\s*$/, ''); + if (name.length === 0) { + throw new TypeError('A name for the custom event must be provided'); + } + + for (var key in dimensions) { + if (typeof key !== 'string' || typeof dimensions[key] !== 'string') { + throw new TypeError('track() dimensions expects keys and values of type "string".'); + } + } + + options = options || {}; + return _CoreManager2.default.getAnalyticsController().track(name, dimensions)._thenRunCallbacks(options); +} /** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + */ + +var DefaultController = { + track: function (name, dimensions) { + var RESTController = _CoreManager2.default.getRESTController(); + return RESTController.request('POST', 'events/' + name, { dimensions: dimensions }); + } +}; + +_CoreManager2.default.setAnalyticsController(DefaultController); \ No newline at end of file diff --git a/lib/browser/Cloud.js b/lib/browser/Cloud.js new file mode 100644 index 000000000..72c333750 --- /dev/null +++ b/lib/browser/Cloud.js @@ -0,0 +1,109 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.run = run; + +var _CoreManager = require('./CoreManager'); + +var _CoreManager2 = _interopRequireDefault(_CoreManager); + +var _decode = require('./decode'); + +var _decode2 = _interopRequireDefault(_decode); + +var _encode = require('./encode'); + +var _encode2 = _interopRequireDefault(_encode); + +var _ParseError = require('./ParseError'); + +var _ParseError2 = _interopRequireDefault(_ParseError); + +var _ParsePromise = require('./ParsePromise'); + +var _ParsePromise2 = _interopRequireDefault(_ParsePromise); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { default: obj }; +} + +/** + * Contains functions for calling and declaring + * cloud functions. + *
+ * Some functions are only available from Cloud Code. + *
+ * + * @class Parse.Cloud + * @static + */ + +/** + * Makes a call to a cloud function. + * @method run + * @param {String} name The function name. + * @param {Object} data The parameters to send to the cloud function. + * @param {Object} options A Backbone-style options object + * options.success, if set, should be a function to handle a successful + * call to a cloud function. options.error should be a function that + * handles an error running the cloud function. Both functions are + * optional. Both functions take a single argument. + * @return {Parse.Promise} A promise that will be resolved with the result + * of the function. + */ +function run(name, data, options) { + options = options || {}; + + if (typeof name !== 'string' || name.length === 0) { + throw new TypeError('Cloud function name must be a string.'); + } + + var requestOptions = {}; + if (options.useMasterKey) { + requestOptions.useMasterKey = options.useMasterKey; + } + if (options.sessionToken) { + requestOptions.sessionToken = options.sessionToken; + } + + return _CoreManager2.default.getCloudController().run(name, data, requestOptions)._thenRunCallbacks(options); +} /** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + */ + +var DefaultController = { + run: function (name, data, options) { + var RESTController = _CoreManager2.default.getRESTController(); + + var payload = (0, _encode2.default)(data, true); + + var requestOptions = {}; + if (options.hasOwnProperty('useMasterKey')) { + requestOptions.useMasterKey = options.useMasterKey; + } + if (options.hasOwnProperty('sessionToken')) { + requestOptions.sessionToken = options.sessionToken; + } + + var request = RESTController.request('POST', 'functions/' + name, payload, requestOptions); + + return request.then(function (res) { + var decoded = (0, _decode2.default)(res); + if (decoded && decoded.hasOwnProperty('result')) { + return _ParsePromise2.default.as(decoded.result); + } + return _ParsePromise2.default.error(new _ParseError2.default(_ParseError2.default.INVALID_JSON, 'The server returned an invalid response.')); + })._thenRunCallbacks(options); + } +}; + +_CoreManager2.default.setCloudController(DefaultController); \ No newline at end of file diff --git a/lib/browser/CoreManager.js b/lib/browser/CoreManager.js new file mode 100644 index 000000000..20d66a5f1 --- /dev/null +++ b/lib/browser/CoreManager.js @@ -0,0 +1,161 @@ +'use strict'; + +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + */ + +var config = { + // Defaults + IS_NODE: typeof process !== 'undefined' && !!process.versions && !!process.versions.node && !process.versions.electron, + REQUEST_ATTEMPT_LIMIT: 5, + SERVER_URL: 'https://api.parse.com/1', + LIVEQUERY_SERVER_URL: null, + VERSION: 'js' + '1.9.2', + APPLICATION_ID: null, + JAVASCRIPT_KEY: null, + MASTER_KEY: null, + USE_MASTER_KEY: false, + PERFORM_USER_REWRITE: true, + FORCE_REVOCABLE_SESSION: false +}; + +function requireMethods(name, methods, controller) { + methods.forEach(function (func) { + if (typeof controller[func] !== 'function') { + throw new Error(name + ' must implement ' + func + '()'); + } + }); +} + +module.exports = { + get: function (key) { + if (config.hasOwnProperty(key)) { + return config[key]; + } + throw new Error('Configuration key not found: ' + key); + }, + + set: function (key, value) { + config[key] = value; + }, + + /* Specialized Controller Setters/Getters */ + + setAnalyticsController: function (controller) { + requireMethods('AnalyticsController', ['track'], controller); + config['AnalyticsController'] = controller; + }, + getAnalyticsController: function () { + return config['AnalyticsController']; + }, + setCloudController: function (controller) { + requireMethods('CloudController', ['run'], controller); + config['CloudController'] = controller; + }, + getCloudController: function () { + return config['CloudController']; + }, + setConfigController: function (controller) { + requireMethods('ConfigController', ['current', 'get'], controller); + config['ConfigController'] = controller; + }, + getConfigController: function () { + return config['ConfigController']; + }, + setFileController: function (controller) { + requireMethods('FileController', ['saveFile', 'saveBase64'], controller); + config['FileController'] = controller; + }, + getFileController: function () { + return config['FileController']; + }, + setInstallationController: function (controller) { + requireMethods('InstallationController', ['currentInstallationId'], controller); + config['InstallationController'] = controller; + }, + getInstallationController: function () { + return config['InstallationController']; + }, + setObjectController: function (controller) { + requireMethods('ObjectController', ['save', 'fetch', 'destroy'], controller); + config['ObjectController'] = controller; + }, + getObjectController: function () { + return config['ObjectController']; + }, + setObjectStateController: function (controller) { + requireMethods('ObjectStateController', ['getState', 'initializeState', 'removeState', 'getServerData', 'setServerData', 'getPendingOps', 'setPendingOp', 'pushPendingState', 'popPendingState', 'mergeFirstPendingState', 'getObjectCache', 'estimateAttribute', 'estimateAttributes', 'commitServerChanges', 'enqueueTask', 'clearAllState'], controller); + + config['ObjectStateController'] = controller; + }, + getObjectStateController: function () { + return config['ObjectStateController']; + }, + setPushController: function (controller) { + requireMethods('PushController', ['send'], controller); + config['PushController'] = controller; + }, + getPushController: function () { + return config['PushController']; + }, + setQueryController: function (controller) { + requireMethods('QueryController', ['find'], controller); + config['QueryController'] = controller; + }, + getQueryController: function () { + return config['QueryController']; + }, + setRESTController: function (controller) { + requireMethods('RESTController', ['request', 'ajax'], controller); + config['RESTController'] = controller; + }, + getRESTController: function () { + return config['RESTController']; + }, + setSessionController: function (controller) { + requireMethods('SessionController', ['getSession'], controller); + config['SessionController'] = controller; + }, + getSessionController: function () { + return config['SessionController']; + }, + setStorageController: function (controller) { + if (controller.async) { + requireMethods('An async StorageController', ['getItemAsync', 'setItemAsync', 'removeItemAsync'], controller); + } else { + requireMethods('A synchronous StorageController', ['getItem', 'setItem', 'removeItem'], controller); + } + config['StorageController'] = controller; + }, + getStorageController: function () { + return config['StorageController']; + }, + setUserController: function (controller) { + requireMethods('UserController', ['setCurrentUser', 'currentUser', 'currentUserAsync', 'signUp', 'logIn', 'become', 'logOut', 'requestPasswordReset', 'upgradeToRevocableSession', 'linkWith'], controller); + config['UserController'] = controller; + }, + getUserController: function () { + return config['UserController']; + }, + setLiveQueryController: function (controller) { + requireMethods('LiveQueryController', ['subscribe', 'unsubscribe', 'open', 'close'], controller); + config['LiveQueryController'] = controller; + }, + getLiveQueryController: function () { + return config['LiveQueryController']; + }, + setHooksController: function (controller) { + requireMethods('HooksController', ['create', 'get', 'update', 'remove'], controller); + config['HooksController'] = controller; + }, + getHooksController: function () { + return config['HooksController']; + } +}; \ No newline at end of file diff --git a/lib/browser/EventEmitter.js b/lib/browser/EventEmitter.js new file mode 100644 index 000000000..e9414f0bf --- /dev/null +++ b/lib/browser/EventEmitter.js @@ -0,0 +1,15 @@ +'use strict'; + +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * This is a simple wrapper to unify EventEmitter implementations across platforms. + */ + +module.exports = require('events').EventEmitter; +var EventEmitter; \ No newline at end of file diff --git a/lib/browser/FacebookUtils.js b/lib/browser/FacebookUtils.js new file mode 100644 index 000000000..bb8b7ef51 --- /dev/null +++ b/lib/browser/FacebookUtils.js @@ -0,0 +1,243 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _parseDate = require('./parseDate'); + +var _parseDate2 = _interopRequireDefault(_parseDate); + +var _ParseUser = require('./ParseUser'); + +var _ParseUser2 = _interopRequireDefault(_ParseUser); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { default: obj }; +} + +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * -weak + */ + +var PUBLIC_KEY = "*"; + +var initialized = false; +var requestedPermissions; +var initOptions; +var provider = { + authenticate: function (options) { + var _this = this; + + if (typeof FB === 'undefined') { + options.error(this, 'Facebook SDK not found.'); + } + FB.login(function (response) { + if (response.authResponse) { + if (options.success) { + options.success(_this, { + id: response.authResponse.userID, + access_token: response.authResponse.accessToken, + expiration_date: new Date(response.authResponse.expiresIn * 1000 + new Date().getTime()).toJSON() + }); + } + } else { + if (options.error) { + options.error(_this, response); + } + } + }, { + scope: requestedPermissions + }); + }, + restoreAuthentication: function (authData) { + if (authData) { + var expiration = (0, _parseDate2.default)(authData.expiration_date); + var expiresIn = expiration ? (expiration.getTime() - new Date().getTime()) / 1000 : 0; + + var authResponse = { + userID: authData.id, + accessToken: authData.access_token, + expiresIn: expiresIn + }; + var newOptions = {}; + if (initOptions) { + for (var key in initOptions) { + newOptions[key] = initOptions[key]; + } + } + newOptions.authResponse = authResponse; + + // Suppress checks for login status from the browser. + newOptions.status = false; + + // If the user doesn't match the one known by the FB SDK, log out. + // Most of the time, the users will match -- it's only in cases where + // the FB SDK knows of a different user than the one being restored + // from a Parse User that logged in with username/password. + var existingResponse = FB.getAuthResponse(); + if (existingResponse && existingResponse.userID !== authResponse.userID) { + FB.logout(); + } + + FB.init(newOptions); + } + return true; + }, + getAuthType: function () { + return 'facebook'; + }, + deauthenticate: function () { + this.restoreAuthentication(null); + } +}; + +/** + * Provides a set of utilities for using Parse with Facebook. + * @class Parse.FacebookUtils + * @static + */ +var FacebookUtils = { + /** + * Initializes Parse Facebook integration. Call this function after you + * have loaded the Facebook Javascript SDK with the same parameters + * as you would pass to
+ *
+ * FB.init()
. Parse.FacebookUtils will invoke FB.init() for you
+ * with these arguments.
+ *
+ * @method init
+ * @param {Object} options Facebook options argument as described here:
+ *
+ * FB.init(). The status flag will be coerced to 'false' because it
+ * interferes with Parse Facebook integration. Call FB.getLoginStatus()
+ * explicitly if this behavior is required by your application.
+ */
+ init: function (options) {
+ if (typeof FB === 'undefined') {
+ throw new Error('The Facebook JavaScript SDK must be loaded before calling init.');
+ }
+ initOptions = {};
+ if (options) {
+ for (var key in options) {
+ initOptions[key] = options[key];
+ }
+ }
+ if (initOptions.status && typeof console !== 'undefined') {
+ var warn = console.warn || console.log || function () {};
+ warn.call(console, 'The "status" flag passed into' + ' FB.init, when set to true, can interfere with Parse Facebook' + ' integration, so it has been suppressed. Please call' + ' FB.getLoginStatus() explicitly if you require this behavior.');
+ }
+ initOptions.status = false;
+ FB.init(initOptions);
+ _ParseUser2.default._registerAuthenticationProvider(provider);
+ initialized = true;
+ },
+
+ /**
+ * Gets whether the user has their account linked to Facebook.
+ *
+ * @method isLinked
+ * @param {Parse.User} user User to check for a facebook link.
+ * The user must be logged in on this device.
+ * @return {Boolean} true
if the user has their account
+ * linked to Facebook.
+ */
+ isLinked: function (user) {
+ return user._isLinked('facebook');
+ },
+
+ /**
+ * Logs in a user using Facebook. This method delegates to the Facebook
+ * SDK to authenticate the user, and then automatically logs in (or
+ * creates, in the case where it is a new user) a Parse.User.
+ *
+ * @method logIn
+ * @param {String, Object} permissions The permissions required for Facebook
+ * log in. This is a comma-separated string of permissions.
+ * Alternatively, supply a Facebook authData object as described in our
+ * REST API docs if you want to handle getting facebook auth tokens
+ * yourself.
+ * @param {Object} options Standard options object with success and error
+ * callbacks.
+ */
+ logIn: function (permissions, options) {
+ if (!permissions || typeof permissions === 'string') {
+ if (!initialized) {
+ throw new Error('You must initialize FacebookUtils before calling logIn.');
+ }
+ requestedPermissions = permissions;
+ return _ParseUser2.default._logInWith('facebook', options);
+ } else {
+ var newOptions = {};
+ if (options) {
+ for (var key in options) {
+ newOptions[key] = options[key];
+ }
+ }
+ newOptions.authData = permissions;
+ return _ParseUser2.default._logInWith('facebook', newOptions);
+ }
+ },
+
+ /**
+ * Links Facebook to an existing PFUser. This method delegates to the
+ * Facebook SDK to authenticate the user, and then automatically links
+ * the account to the Parse.User.
+ *
+ * @method link
+ * @param {Parse.User} user User to link to Facebook. This must be the
+ * current user.
+ * @param {String, Object} permissions The permissions required for Facebook
+ * log in. This is a comma-separated string of permissions.
+ * Alternatively, supply a Facebook authData object as described in our
+ * REST API docs if you want to handle getting facebook auth tokens
+ * yourself.
+ * @param {Object} options Standard options object with success and error
+ * callbacks.
+ */
+ link: function (user, permissions, options) {
+ if (!permissions || typeof permissions === 'string') {
+ if (!initialized) {
+ throw new Error('You must initialize FacebookUtils before calling link.');
+ }
+ requestedPermissions = permissions;
+ return user._linkWith('facebook', options);
+ } else {
+ var newOptions = {};
+ if (options) {
+ for (var key in options) {
+ newOptions[key] = options[key];
+ }
+ }
+ newOptions.authData = permissions;
+ return user._linkWith('facebook', newOptions);
+ }
+ },
+
+ /**
+ * Unlinks the Parse.User from a Facebook account.
+ *
+ * @method unlink
+ * @param {Parse.User} user User to unlink from Facebook. This must be the
+ * current user.
+ * @param {Object} options Standard options object with success and error
+ * callbacks.
+ */
+ unlink: function (user, options) {
+ if (!initialized) {
+ throw new Error('You must initialize FacebookUtils before calling unlink.');
+ }
+ return user._unlinkFrom('facebook', options);
+ }
+};
+
+exports.default = FacebookUtils;
\ No newline at end of file
diff --git a/lib/browser/InstallationController.js b/lib/browser/InstallationController.js
new file mode 100644
index 000000000..7b01e3cad
--- /dev/null
+++ b/lib/browser/InstallationController.js
@@ -0,0 +1,64 @@
+'use strict';
+
+var _CoreManager = require('./CoreManager');
+
+var _CoreManager2 = _interopRequireDefault(_CoreManager);
+
+var _ParsePromise = require('./ParsePromise');
+
+var _ParsePromise2 = _interopRequireDefault(_ParsePromise);
+
+var _Storage = require('./Storage');
+
+var _Storage2 = _interopRequireDefault(_Storage);
+
+function _interopRequireDefault(obj) {
+ return obj && obj.__esModule ? obj : { default: obj };
+}
+
+var iidCache = null; /**
+ * Copyright (c) 2015-present, Parse, LLC.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ *
+ *
+ */
+
+function hexOctet() {
+ return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
+}
+
+function generateId() {
+ return hexOctet() + hexOctet() + '-' + hexOctet() + '-' + hexOctet() + '-' + hexOctet() + '-' + hexOctet() + hexOctet() + hexOctet();
+}
+
+var InstallationController = {
+ currentInstallationId: function () {
+ if (typeof iidCache === 'string') {
+ return _ParsePromise2.default.as(iidCache);
+ }
+ var path = _Storage2.default.generatePath('installationId');
+ return _Storage2.default.getItemAsync(path).then(function (iid) {
+ if (!iid) {
+ iid = generateId();
+ return _Storage2.default.setItemAsync(path, iid).then(function () {
+ iidCache = iid;
+ return iid;
+ });
+ }
+ iidCache = iid;
+ return iid;
+ });
+ },
+ _clearCache: function () {
+ iidCache = null;
+ },
+ _setInstallationIdCache: function (iid) {
+ iidCache = iid;
+ }
+};
+
+module.exports = InstallationController;
\ No newline at end of file
diff --git a/lib/browser/LiveQueryClient.js b/lib/browser/LiveQueryClient.js
new file mode 100644
index 000000000..bc9603a62
--- /dev/null
+++ b/lib/browser/LiveQueryClient.js
@@ -0,0 +1,595 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+
+var _typeof2 = require('babel-runtime/helpers/typeof');
+
+var _typeof3 = _interopRequireDefault(_typeof2);
+
+var _getIterator2 = require('babel-runtime/core-js/get-iterator');
+
+var _getIterator3 = _interopRequireDefault(_getIterator2);
+
+var _stringify = require('babel-runtime/core-js/json/stringify');
+
+var _stringify2 = _interopRequireDefault(_stringify);
+
+var _map = require('babel-runtime/core-js/map');
+
+var _map2 = _interopRequireDefault(_map);
+
+var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of');
+
+var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
+
+var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
+
+var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
+
+var _createClass2 = require('babel-runtime/helpers/createClass');
+
+var _createClass3 = _interopRequireDefault(_createClass2);
+
+var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn');
+
+var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
+
+var _inherits2 = require('babel-runtime/helpers/inherits');
+
+var _inherits3 = _interopRequireDefault(_inherits2);
+
+var _EventEmitter2 = require('./EventEmitter');
+
+var _EventEmitter3 = _interopRequireDefault(_EventEmitter2);
+
+var _ParsePromise = require('./ParsePromise');
+
+var _ParsePromise2 = _interopRequireDefault(_ParsePromise);
+
+var _ParseObject = require('./ParseObject');
+
+var _ParseObject2 = _interopRequireDefault(_ParseObject);
+
+var _LiveQuerySubscription = require('./LiveQuerySubscription');
+
+var _LiveQuerySubscription2 = _interopRequireDefault(_LiveQuerySubscription);
+
+function _interopRequireDefault(obj) {
+ return obj && obj.__esModule ? obj : { default: obj };
+}
+
+// The LiveQuery client inner state
+/**
+ * Copyright (c) 2015-present, Parse, LLC.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+var CLIENT_STATE = {
+ INITIALIZED: 'initialized',
+ CONNECTING: 'connecting',
+ CONNECTED: 'connected',
+ CLOSED: 'closed',
+ RECONNECTING: 'reconnecting',
+ DISCONNECTED: 'disconnected'
+};
+
+// The event type the LiveQuery client should sent to server
+var OP_TYPES = {
+ CONNECT: 'connect',
+ SUBSCRIBE: 'subscribe',
+ UNSUBSCRIBE: 'unsubscribe',
+ ERROR: 'error'
+};
+
+// The event we get back from LiveQuery server
+var OP_EVENTS = {
+ CONNECTED: 'connected',
+ SUBSCRIBED: 'subscribed',
+ UNSUBSCRIBED: 'unsubscribed',
+ ERROR: 'error',
+ CREATE: 'create',
+ UPDATE: 'update',
+ ENTER: 'enter',
+ LEAVE: 'leave',
+ DELETE: 'delete'
+};
+
+// The event the LiveQuery client should emit
+var CLIENT_EMMITER_TYPES = {
+ CLOSE: 'close',
+ ERROR: 'error',
+ OPEN: 'open'
+};
+
+// The event the LiveQuery subscription should emit
+var SUBSCRIPTION_EMMITER_TYPES = {
+ OPEN: 'open',
+ CLOSE: 'close',
+ ERROR: 'error',
+ CREATE: 'create',
+ UPDATE: 'update',
+ ENTER: 'enter',
+ LEAVE: 'leave',
+ DELETE: 'delete'
+};
+
+var generateInterval = function (k) {
+ return Math.random() * Math.min(30, Math.pow(2, k) - 1) * 1000;
+};
+
+/**
+ * Creates a new LiveQueryClient.
+ * Extends events.EventEmitter
+ * cloud functions.
+ *
+ * A wrapper of a standard WebSocket client. We add several useful methods to
+ * help you connect/disconnect to LiveQueryServer, subscribe/unsubscribe a ParseQuery easily.
+ *
+ * javascriptKey and masterKey are used for verifying the LiveQueryClient when it tries
+ * to connect to the LiveQuery server
+ *
+ * @class Parse.LiveQueryClient
+ * @constructor
+ * @param {Object} options
+ * @param {string} options.applicationId - applicationId of your Parse app
+ * @param {string} options.serverURL - the URL of your LiveQuery server
+ * @param {string} options.javascriptKey (optional)
+ * @param {string} options.masterKey (optional) Your Parse Master Key. (Node.js only!)
+ * @param {string} options.sessionToken (optional)
+ *
+ *
+ * We expose three events to help you monitor the status of the LiveQueryClient.
+ *
+ * + * let Parse = require('parse/node'); + * let LiveQueryClient = Parse.LiveQueryClient; + * let client = new LiveQueryClient({ + * applicationId: '', + * serverURL: '', + * javascriptKey: '', + * masterKey: '' + * }); + *+ * + * Open - When we establish the WebSocket connection to the LiveQuery server, you'll get this event. + *
+ * client.on('open', () => { + * + * });+ * + * Close - When we lose the WebSocket connection to the LiveQuery server, you'll get this event. + *
+ * client.on('close', () => { + * + * });+ * + * Error - When some network error or LiveQuery server error happens, you'll get this event. + *
+ * client.on('error', (error) => { + * + * });+ * + * + */ + +var LiveQueryClient = function (_EventEmitter) { + (0, _inherits3.default)(LiveQueryClient, _EventEmitter); + + function LiveQueryClient(_ref) { + var applicationId = _ref.applicationId, + serverURL = _ref.serverURL, + javascriptKey = _ref.javascriptKey, + masterKey = _ref.masterKey, + sessionToken = _ref.sessionToken; + (0, _classCallCheck3.default)(this, LiveQueryClient); + + var _this = (0, _possibleConstructorReturn3.default)(this, (LiveQueryClient.__proto__ || (0, _getPrototypeOf2.default)(LiveQueryClient)).call(this)); + + if (!serverURL || serverURL.indexOf('ws') !== 0) { + throw new Error('You need to set a proper Parse LiveQuery server url before using LiveQueryClient'); + } + + _this.reconnectHandle = null; + _this.attempts = 1;; + _this.id = 0; + _this.requestId = 1; + _this.serverURL = serverURL; + _this.applicationId = applicationId; + _this.javascriptKey = javascriptKey; + _this.masterKey = masterKey; + _this.sessionToken = sessionToken; + _this.connectPromise = new _ParsePromise2.default(); + _this.subscriptions = new _map2.default(); + _this.state = CLIENT_STATE.INITIALIZED; + return _this; + } + + (0, _createClass3.default)(LiveQueryClient, [{ + key: 'shouldOpen', + value: function () { + return this.state === CLIENT_STATE.INITIALIZED || this.state === CLIENT_STATE.DISCONNECTED; + } + + /** + * Subscribes to a ParseQuery + * + * If you provide the sessionToken, when the LiveQuery server gets ParseObject's + * updates from parse server, it'll try to check whether the sessionToken fulfills + * the ParseObject's ACL. The LiveQuery server will only send updates to clients whose + * sessionToken is fit for the ParseObject's ACL. You can check the LiveQuery protocol + * here for more details. The subscription you get is the same subscription you get + * from our Standard API. + * + * @method subscribe + * @param {Object} query - the ParseQuery you want to subscribe to + * @param {string} sessionToken (optional) + * @return {Object} subscription + */ + + }, { + key: 'subscribe', + value: function (query, sessionToken) { + var _this2 = this; + + if (!query) { + return; + } + var where = query.toJSON().where; + var className = query.className; + var subscribeRequest = { + op: OP_TYPES.SUBSCRIBE, + requestId: this.requestId, + query: { + className: className, + where: where + } + }; + + if (sessionToken) { + subscribeRequest.sessionToken = sessionToken; + } + + var subscription = new _LiveQuerySubscription2.default(this.requestId, query, sessionToken); + this.subscriptions.set(this.requestId, subscription); + this.requestId += 1; + this.connectPromise.then(function () { + _this2.socket.send((0, _stringify2.default)(subscribeRequest)); + }); + + // adding listener so process does not crash + // best practice is for developer to register their own listener + subscription.on('error', function () {}); + + return subscription; + } + + /** + * After calling unsubscribe you'll stop receiving events from the subscription object. + * + * @method unsubscribe + * @param {Object} subscription - subscription you would like to unsubscribe from. + */ + + }, { + key: 'unsubscribe', + value: function (subscription) { + var _this3 = this; + + if (!subscription) { + return; + } + + this.subscriptions.delete(subscription.id); + var unsubscribeRequest = { + op: OP_TYPES.UNSUBSCRIBE, + requestId: subscription.id + }; + this.connectPromise.then(function () { + _this3.socket.send((0, _stringify2.default)(unsubscribeRequest)); + }); + } + + /** + * After open is called, the LiveQueryClient will try to send a connect request + * to the LiveQuery server. + * + * @method open + */ + + }, { + key: 'open', + value: function () { + var _this4 = this; + + var WebSocketImplementation = this._getWebSocketImplementation(); + if (!WebSocketImplementation) { + this.emit(CLIENT_EMMITER_TYPES.ERROR, 'Can not find WebSocket implementation'); + return; + } + + if (this.state !== CLIENT_STATE.RECONNECTING) { + this.state = CLIENT_STATE.CONNECTING; + } + + // Get WebSocket implementation + this.socket = new WebSocketImplementation(this.serverURL); + + // Bind WebSocket callbacks + this.socket.onopen = function () { + _this4._handleWebSocketOpen(); + }; + + this.socket.onmessage = function (event) { + _this4._handleWebSocketMessage(event); + }; + + this.socket.onclose = function () { + _this4._handleWebSocketClose(); + }; + + this.socket.onerror = function (error) { + _this4._handleWebSocketError(error); + }; + } + }, { + key: 'resubscribe', + value: function () { + var _this5 = this; + + this.subscriptions.forEach(function (subscription, requestId) { + var query = subscription.query; + var where = query.toJSON().where; + var className = query.className; + var sessionToken = subscription.sessionToken; + var subscribeRequest = { + op: OP_TYPES.SUBSCRIBE, + requestId: requestId, + query: { + className: className, + where: where + } + }; + + if (sessionToken) { + subscribeRequest.sessionToken = sessionToken; + } + + _this5.connectPromise.then(function () { + _this5.socket.send((0, _stringify2.default)(subscribeRequest)); + }); + }); + } + + /** + * This method will close the WebSocket connection to this LiveQueryClient, + * cancel the auto reconnect and unsubscribe all subscriptions based on it. + * + * @method close + */ + + }, { + key: 'close', + value: function () { + if (this.state === CLIENT_STATE.INITIALIZED || this.state === CLIENT_STATE.DISCONNECTED) { + return; + } + this.state = CLIENT_STATE.DISCONNECTED; + this.socket.close(); + // Notify each subscription about the close + var _iteratorNormalCompletion = true; + var _didIteratorError = false; + var _iteratorError = undefined; + + try { + for (var _iterator = (0, _getIterator3.default)(this.subscriptions.values()), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { + var subscription = _step.value; + + subscription.emit(SUBSCRIPTION_EMMITER_TYPES.CLOSE); + } + } catch (err) { + _didIteratorError = true; + _iteratorError = err; + } finally { + try { + if (!_iteratorNormalCompletion && _iterator.return) { + _iterator.return(); + } + } finally { + if (_didIteratorError) { + throw _iteratorError; + } + } + } + + this._handleReset(); + this.emit(CLIENT_EMMITER_TYPES.CLOSE); + } + }, { + key: '_getWebSocketImplementation', + value: function () { + return typeof WebSocket === 'function' || (typeof WebSocket === 'undefined' ? 'undefined' : (0, _typeof3.default)(WebSocket)) === 'object' ? WebSocket : null; + } + + // ensure we start with valid state if connect is called again after close + + }, { + key: '_handleReset', + value: function () { + this.attempts = 1;; + this.id = 0; + this.requestId = 1; + this.connectPromise = new _ParsePromise2.default(); + this.subscriptions = new _map2.default(); + } + }, { + key: '_handleWebSocketOpen', + value: function () { + this.attempts = 1; + var connectRequest = { + op: OP_TYPES.CONNECT, + applicationId: this.applicationId, + javascriptKey: this.javascriptKey, + masterKey: this.masterKey, + sessionToken: this.sessionToken + }; + this.socket.send((0, _stringify2.default)(connectRequest)); + } + }, { + key: '_handleWebSocketMessage', + value: function (event) { + var data = event.data; + if (typeof data === 'string') { + data = JSON.parse(data); + } + var subscription = null; + if (data.requestId) { + subscription = this.subscriptions.get(data.requestId); + } + switch (data.op) { + case OP_EVENTS.CONNECTED: + if (this.state === CLIENT_STATE.RECONNECTING) { + this.resubscribe(); + } + this.emit(CLIENT_EMMITER_TYPES.OPEN); + this.id = data.clientId; + this.connectPromise.resolve(); + this.state = CLIENT_STATE.CONNECTED; + break; + case OP_EVENTS.SUBSCRIBED: + if (subscription) { + subscription.emit(SUBSCRIPTION_EMMITER_TYPES.OPEN); + } + break; + case OP_EVENTS.ERROR: + if (data.requestId) { + if (subscription) { + subscription.emit(SUBSCRIPTION_EMMITER_TYPES.ERROR, data.error); + } + } else { + this.emit(CLIENT_EMMITER_TYPES.ERROR, data.error); + } + break; + case OP_EVENTS.UNSUBSCRIBED: + // We have already deleted subscription in unsubscribe(), do nothing here + break; + default: + // create, update, enter, leave, delete cases + var className = data.object.className; + // Delete the extrea __type and className fields during transfer to full JSON + delete data.object.__type; + delete data.object.className; + var parseObject = new _ParseObject2.default(className); + parseObject._finishFetch(data.object); + if (!subscription) { + break; + } + subscription.emit(data.op, parseObject); + } + } + }, { + key: '_handleWebSocketClose', + value: function () { + if (this.state === CLIENT_STATE.DISCONNECTED) { + return; + } + this.state = CLIENT_STATE.CLOSED; + this.emit(CLIENT_EMMITER_TYPES.CLOSE); + // Notify each subscription about the close + var _iteratorNormalCompletion2 = true; + var _didIteratorError2 = false; + var _iteratorError2 = undefined; + + try { + for (var _iterator2 = (0, _getIterator3.default)(this.subscriptions.values()), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { + var subscription = _step2.value; + + subscription.emit(SUBSCRIPTION_EMMITER_TYPES.CLOSE); + } + } catch (err) { + _didIteratorError2 = true; + _iteratorError2 = err; + } finally { + try { + if (!_iteratorNormalCompletion2 && _iterator2.return) { + _iterator2.return(); + } + } finally { + if (_didIteratorError2) { + throw _iteratorError2; + } + } + } + + this._handleReconnect(); + } + }, { + key: '_handleWebSocketError', + value: function (error) { + this.emit(CLIENT_EMMITER_TYPES.ERROR, error); + var _iteratorNormalCompletion3 = true; + var _didIteratorError3 = false; + var _iteratorError3 = undefined; + + try { + for (var _iterator3 = (0, _getIterator3.default)(this.subscriptions.values()), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) { + var subscription = _step3.value; + + subscription.emit(SUBSCRIPTION_EMMITER_TYPES.ERROR); + } + } catch (err) { + _didIteratorError3 = true; + _iteratorError3 = err; + } finally { + try { + if (!_iteratorNormalCompletion3 && _iterator3.return) { + _iterator3.return(); + } + } finally { + if (_didIteratorError3) { + throw _iteratorError3; + } + } + } + + this._handleReconnect(); + } + }, { + key: '_handleReconnect', + value: function () { + var _this6 = this; + + // if closed or currently reconnecting we stop attempting to reconnect + if (this.state === CLIENT_STATE.DISCONNECTED) { + return; + } + + this.state = CLIENT_STATE.RECONNECTING; + var time = generateInterval(this.attempts); + + // handle case when both close/error occur at frequent rates we ensure we do not reconnect unnecessarily. + // we're unable to distinguish different between close/error when we're unable to reconnect therefore + // we try to reonnect in both cases + // server side ws and browser WebSocket behave differently in when close/error get triggered + + if (this.reconnectHandle) { + clearTimeout(this.reconnectHandle); + } + + this.reconnectHandle = setTimeout(function () { + _this6.attempts++; + _this6.connectPromise = new _ParsePromise2.default(); + _this6.open(); + }.bind(this), time); + } + }]); + return LiveQueryClient; +}(_EventEmitter3.default); + +exports.default = LiveQueryClient; \ No newline at end of file diff --git a/lib/browser/LiveQuerySubscription.js b/lib/browser/LiveQuerySubscription.js new file mode 100644 index 000000000..4078b65d5 --- /dev/null +++ b/lib/browser/LiveQuerySubscription.js @@ -0,0 +1,162 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); + +var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); + +var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); + +var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); + +var _createClass2 = require('babel-runtime/helpers/createClass'); + +var _createClass3 = _interopRequireDefault(_createClass2); + +var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); + +var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); + +var _inherits2 = require('babel-runtime/helpers/inherits'); + +var _inherits3 = _interopRequireDefault(_inherits2); + +var _EventEmitter2 = require('./EventEmitter'); + +var _EventEmitter3 = _interopRequireDefault(_EventEmitter2); + +var _CoreManager = require('./CoreManager'); + +var _CoreManager2 = _interopRequireDefault(_CoreManager); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { default: obj }; +} + +/** + * Creates a new LiveQuery Subscription. + * Extends events.EventEmitter + * cloud functions. + * + * @constructor + * @param {string} id - subscription id + * @param {string} query - query to subscribe to + * @param {string} sessionToken - optional session token + * + *
Open Event - When you call query.subscribe(), we send a subscribe request to + * the LiveQuery server, when we get the confirmation from the LiveQuery server, + * this event will be emitted. When the client loses WebSocket connection to the + * LiveQuery server, we will try to auto reconnect the LiveQuery server. If we + * reconnect the LiveQuery server and successfully resubscribe the ParseQuery, + * you'll also get this event. + * + *
+ * subscription.on('open', () => { + * + * });+ * + *
Create Event - When a new ParseObject is created and it fulfills the ParseQuery you subscribe, + * you'll get this event. The object is the ParseObject which is created. + * + *
+ * subscription.on('create', (object) => { + * + * });+ * + *
Update Event - When an existing ParseObject which fulfills the ParseQuery you subscribe + * is updated (The ParseObject fulfills the ParseQuery before and after changes), + * you'll get this event. The object is the ParseObject which is updated. + * Its content is the latest value of the ParseObject. + * + *
+ * subscription.on('update', (object) => { + * + * });+ * + *
Enter Event - When an existing ParseObject's old value doesn't fulfill the ParseQuery + * but its new value fulfills the ParseQuery, you'll get this event. The object is the + * ParseObject which enters the ParseQuery. Its content is the latest value of the ParseObject. + * + *
+ * subscription.on('enter', (object) => { + * + * });+ * + * + *
Update Event - When an existing ParseObject's old value fulfills the ParseQuery but its new value + * doesn't fulfill the ParseQuery, you'll get this event. The object is the ParseObject + * which leaves the ParseQuery. Its content is the latest value of the ParseObject. + * + *
+ * subscription.on('leave', (object) => { + * + * });+ * + * + *
Delete Event - When an existing ParseObject which fulfills the ParseQuery is deleted, you'll + * get this event. The object is the ParseObject which is deleted. + * + *
+ * subscription.on('delete', (object) => { + * + * });+ * + * + *
Close Event - When the client loses the WebSocket connection to the LiveQuery + * server and we stop receiving events, you'll get this event. + * + *
+ * subscription.on('close', () => { + * + * });+ * + * + */ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ + +var Subscription = function (_EventEmitter) { + (0, _inherits3.default)(Subscription, _EventEmitter); + + function Subscription(id, query, sessionToken) { + (0, _classCallCheck3.default)(this, Subscription); + + var _this2 = (0, _possibleConstructorReturn3.default)(this, (Subscription.__proto__ || (0, _getPrototypeOf2.default)(Subscription)).call(this)); + + _this2.id = id; + _this2.query = query; + _this2.sessionToken = sessionToken; + return _this2; + } + + /** + * @method unsubscribe + */ + + (0, _createClass3.default)(Subscription, [{ + key: 'unsubscribe', + value: function () { + var _this3 = this; + + var _this = this; + _CoreManager2.default.getLiveQueryController().getDefaultLiveQueryClient().then(function (liveQueryClient) { + liveQueryClient.unsubscribe(_this); + _this.emit('close'); + _this3.resolve(); + }); + } + }]); + return Subscription; +}(_EventEmitter3.default); + +exports.default = Subscription; \ No newline at end of file diff --git a/lib/browser/ObjectStateMutations.js b/lib/browser/ObjectStateMutations.js new file mode 100644 index 000000000..b476a0e2f --- /dev/null +++ b/lib/browser/ObjectStateMutations.js @@ -0,0 +1,165 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _stringify = require('babel-runtime/core-js/json/stringify'); + +var _stringify2 = _interopRequireDefault(_stringify); + +var _typeof2 = require('babel-runtime/helpers/typeof'); + +var _typeof3 = _interopRequireDefault(_typeof2); + +exports.defaultState = defaultState; +exports.setServerData = setServerData; +exports.setPendingOp = setPendingOp; +exports.pushPendingState = pushPendingState; +exports.popPendingState = popPendingState; +exports.mergeFirstPendingState = mergeFirstPendingState; +exports.estimateAttribute = estimateAttribute; +exports.estimateAttributes = estimateAttributes; +exports.commitServerChanges = commitServerChanges; + +var _encode = require('./encode'); + +var _encode2 = _interopRequireDefault(_encode); + +var _ParseFile = require('./ParseFile'); + +var _ParseFile2 = _interopRequireDefault(_ParseFile); + +var _ParseObject = require('./ParseObject'); + +var _ParseObject2 = _interopRequireDefault(_ParseObject); + +var _ParsePromise = require('./ParsePromise'); + +var _ParsePromise2 = _interopRequireDefault(_ParsePromise); + +var _ParseRelation = require('./ParseRelation'); + +var _ParseRelation2 = _interopRequireDefault(_ParseRelation); + +var _TaskQueue = require('./TaskQueue'); + +var _TaskQueue2 = _interopRequireDefault(_TaskQueue); + +var _ParseOp = require('./ParseOp'); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { default: obj }; +} + +function defaultState() { + return { + serverData: {}, + pendingOps: [{}], + objectCache: {}, + tasks: new _TaskQueue2.default(), + existed: false + }; +} /** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + */ + +function setServerData(serverData, attributes) { + for (var _attr in attributes) { + if (typeof attributes[_attr] !== 'undefined') { + serverData[_attr] = attributes[_attr]; + } else { + delete serverData[_attr]; + } + } +} + +function setPendingOp(pendingOps, attr, op) { + var last = pendingOps.length - 1; + if (op) { + pendingOps[last][attr] = op; + } else { + delete pendingOps[last][attr]; + } +} + +function pushPendingState(pendingOps) { + pendingOps.push({}); +} + +function popPendingState(pendingOps) { + var first = pendingOps.shift(); + if (!pendingOps.length) { + pendingOps[0] = {}; + } + return first; +} + +function mergeFirstPendingState(pendingOps) { + var first = popPendingState(pendingOps); + var next = pendingOps[0]; + for (var _attr2 in first) { + if (next[_attr2] && first[_attr2]) { + var merged = next[_attr2].mergeWith(first[_attr2]); + if (merged) { + next[_attr2] = merged; + } + } else { + next[_attr2] = first[_attr2]; + } + } +} + +function estimateAttribute(serverData, pendingOps, className, id, attr) { + var value = serverData[attr]; + for (var i = 0; i < pendingOps.length; i++) { + if (pendingOps[i][attr]) { + if (pendingOps[i][attr] instanceof _ParseOp.RelationOp) { + if (id) { + value = pendingOps[i][attr].applyTo(value, { className: className, id: id }, attr); + } + } else { + value = pendingOps[i][attr].applyTo(value); + } + } + } + return value; +} + +function estimateAttributes(serverData, pendingOps, className, id) { + var data = {}; + var attr = void 0; + for (attr in serverData) { + data[attr] = serverData[attr]; + } + for (var i = 0; i < pendingOps.length; i++) { + for (attr in pendingOps[i]) { + if (pendingOps[i][attr] instanceof _ParseOp.RelationOp) { + if (id) { + data[attr] = pendingOps[i][attr].applyTo(data[attr], { className: className, id: id }, attr); + } + } else { + data[attr] = pendingOps[i][attr].applyTo(data[attr]); + } + } + } + return data; +} + +function commitServerChanges(serverData, objectCache, changes) { + for (var _attr3 in changes) { + var val = changes[_attr3]; + serverData[_attr3] = val; + if (val && (typeof val === 'undefined' ? 'undefined' : (0, _typeof3.default)(val)) === 'object' && !(val instanceof _ParseObject2.default) && !(val instanceof _ParseFile2.default) && !(val instanceof _ParseRelation2.default)) { + var json = (0, _encode2.default)(val, false, true); + objectCache[_attr3] = (0, _stringify2.default)(json); + } + } +} \ No newline at end of file diff --git a/lib/browser/Parse.js b/lib/browser/Parse.js new file mode 100644 index 000000000..2499dc786 --- /dev/null +++ b/lib/browser/Parse.js @@ -0,0 +1,186 @@ +'use strict'; + +var _decode = require('./decode'); + +var _decode2 = _interopRequireDefault(_decode); + +var _encode = require('./encode'); + +var _encode2 = _interopRequireDefault(_encode); + +var _CoreManager = require('./CoreManager'); + +var _CoreManager2 = _interopRequireDefault(_CoreManager); + +var _InstallationController = require('./InstallationController'); + +var _InstallationController2 = _interopRequireDefault(_InstallationController); + +var _ParseOp = require('./ParseOp'); + +var ParseOp = _interopRequireWildcard(_ParseOp); + +var _RESTController = require('./RESTController'); + +var _RESTController2 = _interopRequireDefault(_RESTController); + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } else { + var newObj = {};if (obj != null) { + for (var key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; + } + }newObj.default = obj;return newObj; + } +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { default: obj }; +} + +/** + * Contains all Parse API classes and functions. + * @class Parse + * @static + */ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +var Parse = { + /** + * Call this method first to set up your authentication tokens for Parse. + * You can get your keys from the Data Browser on parse.com. + * @method initialize + * @param {String} applicationId Your Parse Application ID. + * @param {String} javaScriptKey (optional) Your Parse JavaScript Key (Not needed for parse-server) + * @param {String} masterKey (optional) Your Parse Master Key. (Node.js only!) + * @static + */ + initialize: function (applicationId, javaScriptKey) { + if ('browser' === 'browser' && _CoreManager2.default.get('IS_NODE')) { + console.log('It looks like you\'re using the browser version of the SDK in a ' + 'node.js environment. You should require(\'parse/node\') instead.'); + } + Parse._initialize(applicationId, javaScriptKey); + }, + _initialize: function (applicationId, javaScriptKey, masterKey) { + _CoreManager2.default.set('APPLICATION_ID', applicationId); + _CoreManager2.default.set('JAVASCRIPT_KEY', javaScriptKey); + _CoreManager2.default.set('MASTER_KEY', masterKey); + _CoreManager2.default.set('USE_MASTER_KEY', false); + } +}; + +/** These legacy setters may eventually be deprecated **/ +Object.defineProperty(Parse, 'applicationId', { + get: function () { + return _CoreManager2.default.get('APPLICATION_ID'); + }, + set: function (value) { + _CoreManager2.default.set('APPLICATION_ID', value); + } +}); +Object.defineProperty(Parse, 'javaScriptKey', { + get: function () { + return _CoreManager2.default.get('JAVASCRIPT_KEY'); + }, + set: function (value) { + _CoreManager2.default.set('JAVASCRIPT_KEY', value); + } +}); +Object.defineProperty(Parse, 'masterKey', { + get: function () { + return _CoreManager2.default.get('MASTER_KEY'); + }, + set: function (value) { + _CoreManager2.default.set('MASTER_KEY', value); + } +}); +Object.defineProperty(Parse, 'serverURL', { + get: function () { + return _CoreManager2.default.get('SERVER_URL'); + }, + set: function (value) { + _CoreManager2.default.set('SERVER_URL', value); + } +}); +Object.defineProperty(Parse, 'liveQueryServerURL', { + get: function () { + return _CoreManager2.default.get('LIVEQUERY_SERVER_URL'); + }, + set: function (value) { + _CoreManager2.default.set('LIVEQUERY_SERVER_URL', value); + } +}); +/** End setters **/ + +Parse.ACL = require('./ParseACL').default; +Parse.Analytics = require('./Analytics'); +Parse.Cloud = require('./Cloud'); +Parse.CoreManager = require('./CoreManager'); +Parse.Config = require('./ParseConfig').default; +Parse.Error = require('./ParseError').default; +Parse.FacebookUtils = require('./FacebookUtils').default; +Parse.File = require('./ParseFile').default; +Parse.GeoPoint = require('./ParseGeoPoint').default; +Parse.Installation = require('./ParseInstallation').default; +Parse.Object = require('./ParseObject').default; +Parse.Op = { + Set: ParseOp.SetOp, + Unset: ParseOp.UnsetOp, + Increment: ParseOp.IncrementOp, + Add: ParseOp.AddOp, + Remove: ParseOp.RemoveOp, + AddUnique: ParseOp.AddUniqueOp, + Relation: ParseOp.RelationOp +}; +Parse.Promise = require('./ParsePromise').default; +Parse.Push = require('./Push'); +Parse.Query = require('./ParseQuery').default; +Parse.Relation = require('./ParseRelation').default; +Parse.Role = require('./ParseRole').default; +Parse.Session = require('./ParseSession').default; +Parse.Storage = require('./Storage'); +Parse.User = require('./ParseUser').default; +Parse.LiveQuery = require('./ParseLiveQuery').default; +Parse.LiveQueryClient = require('./LiveQueryClient').default; + +Parse._request = function () { + for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + return _CoreManager2.default.getRESTController().request.apply(null, args); +}; +Parse._ajax = function () { + for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { + args[_key2] = arguments[_key2]; + } + + return _CoreManager2.default.getRESTController().ajax.apply(null, args); +}; +// We attempt to match the signatures of the legacy versions of these methods +Parse._decode = function (_, value) { + return (0, _decode2.default)(value); +}; +Parse._encode = function (value, _, disallowObjects) { + return (0, _encode2.default)(value, disallowObjects); +}; +Parse._getInstallationId = function () { + return _CoreManager2.default.getInstallationController().currentInstallationId(); +}; + +_CoreManager2.default.setInstallationController(_InstallationController2.default); +_CoreManager2.default.setRESTController(_RESTController2.default); + +// For legacy requires, of the form `var Parse = require('parse').Parse` +Parse.Parse = Parse; + +module.exports = Parse; \ No newline at end of file diff --git a/lib/browser/ParseACL.js b/lib/browser/ParseACL.js new file mode 100644 index 000000000..2aa232c18 --- /dev/null +++ b/lib/browser/ParseACL.js @@ -0,0 +1,406 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _keys = require('babel-runtime/core-js/object/keys'); + +var _keys2 = _interopRequireDefault(_keys); + +var _typeof2 = require('babel-runtime/helpers/typeof'); + +var _typeof3 = _interopRequireDefault(_typeof2); + +var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); + +var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); + +var _createClass2 = require('babel-runtime/helpers/createClass'); + +var _createClass3 = _interopRequireDefault(_createClass2); + +var _ParseRole = require('./ParseRole'); + +var _ParseRole2 = _interopRequireDefault(_ParseRole); + +var _ParseUser = require('./ParseUser'); + +var _ParseUser2 = _interopRequireDefault(_ParseUser); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { default: obj }; +} + +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + */ + +var PUBLIC_KEY = '*'; + +/** + * Creates a new ACL. + * If no argument is given, the ACL has no permissions for anyone. + * If the argument is a Parse.User, the ACL will have read and write + * permission for only that user. + * If the argument is any other JSON object, that object will be interpretted + * as a serialized ACL created with toJSON(). + * @class Parse.ACL + * @constructor + * + *
An ACL, or Access Control List can be added to any
+ * Parse.Object
to restrict access to only a subset of users
+ * of your application.
Parse.Error
.
+ * @param {String} message A detailed description of the error.
+ */
+var ParseError = function ParseError(code, message) {
+ (0, _classCallCheck3.default)(this, ParseError);
+
+ this.code = code;
+ this.message = message;
+};
+
+/**
+ * Error code indicating some error other than those enumerated here.
+ * @property OTHER_CAUSE
+ * @static
+ * @final
+ */
+
+exports.default = ParseError;
+ParseError.OTHER_CAUSE = -1;
+
+/**
+ * Error code indicating that something has gone wrong with the server.
+ * If you get this error code, it is Parse's fault. Contact us at
+ * https://parse.com/help
+ * @property INTERNAL_SERVER_ERROR
+ * @static
+ * @final
+ */
+ParseError.INTERNAL_SERVER_ERROR = 1;
+
+/**
+ * Error code indicating the connection to the Parse servers failed.
+ * @property CONNECTION_FAILED
+ * @static
+ * @final
+ */
+ParseError.CONNECTION_FAILED = 100;
+
+/**
+ * Error code indicating the specified object doesn't exist.
+ * @property OBJECT_NOT_FOUND
+ * @static
+ * @final
+ */
+ParseError.OBJECT_NOT_FOUND = 101;
+
+/**
+ * Error code indicating you tried to query with a datatype that doesn't
+ * support it, like exact matching an array or object.
+ * @property INVALID_QUERY
+ * @static
+ * @final
+ */
+ParseError.INVALID_QUERY = 102;
+
+/**
+ * Error code indicating a missing or invalid classname. Classnames are
+ * case-sensitive. They must start with a letter, and a-zA-Z0-9_ are the
+ * only valid characters.
+ * @property INVALID_CLASS_NAME
+ * @static
+ * @final
+ */
+ParseError.INVALID_CLASS_NAME = 103;
+
+/**
+ * Error code indicating an unspecified object id.
+ * @property MISSING_OBJECT_ID
+ * @static
+ * @final
+ */
+ParseError.MISSING_OBJECT_ID = 104;
+
+/**
+ * Error code indicating an invalid key name. Keys are case-sensitive. They
+ * must start with a letter, and a-zA-Z0-9_ are the only valid characters.
+ * @property INVALID_KEY_NAME
+ * @static
+ * @final
+ */
+ParseError.INVALID_KEY_NAME = 105;
+
+/**
+ * Error code indicating a malformed pointer. You should not see this unless
+ * you have been mucking about changing internal Parse code.
+ * @property INVALID_POINTER
+ * @static
+ * @final
+ */
+ParseError.INVALID_POINTER = 106;
+
+/**
+ * Error code indicating that badly formed JSON was received upstream. This
+ * either indicates you have done something unusual with modifying how
+ * things encode to JSON, or the network is failing badly.
+ * @property INVALID_JSON
+ * @static
+ * @final
+ */
+ParseError.INVALID_JSON = 107;
+
+/**
+ * Error code indicating that the feature you tried to access is only
+ * available internally for testing purposes.
+ * @property COMMAND_UNAVAILABLE
+ * @static
+ * @final
+ */
+ParseError.COMMAND_UNAVAILABLE = 108;
+
+/**
+ * You must call Parse.initialize before using the Parse library.
+ * @property NOT_INITIALIZED
+ * @static
+ * @final
+ */
+ParseError.NOT_INITIALIZED = 109;
+
+/**
+ * Error code indicating that a field was set to an inconsistent type.
+ * @property INCORRECT_TYPE
+ * @static
+ * @final
+ */
+ParseError.INCORRECT_TYPE = 111;
+
+/**
+ * Error code indicating an invalid channel name. A channel name is either
+ * an empty string (the broadcast channel) or contains only a-zA-Z0-9_
+ * characters and starts with a letter.
+ * @property INVALID_CHANNEL_NAME
+ * @static
+ * @final
+ */
+ParseError.INVALID_CHANNEL_NAME = 112;
+
+/**
+ * Error code indicating that push is misconfigured.
+ * @property PUSH_MISCONFIGURED
+ * @static
+ * @final
+ */
+ParseError.PUSH_MISCONFIGURED = 115;
+
+/**
+ * Error code indicating that the object is too large.
+ * @property OBJECT_TOO_LARGE
+ * @static
+ * @final
+ */
+ParseError.OBJECT_TOO_LARGE = 116;
+
+/**
+ * Error code indicating that the operation isn't allowed for clients.
+ * @property OPERATION_FORBIDDEN
+ * @static
+ * @final
+ */
+ParseError.OPERATION_FORBIDDEN = 119;
+
+/**
+ * Error code indicating the result was not found in the cache.
+ * @property CACHE_MISS
+ * @static
+ * @final
+ */
+ParseError.CACHE_MISS = 120;
+
+/**
+ * Error code indicating that an invalid key was used in a nested
+ * JSONObject.
+ * @property INVALID_NESTED_KEY
+ * @static
+ * @final
+ */
+ParseError.INVALID_NESTED_KEY = 121;
+
+/**
+ * Error code indicating that an invalid filename was used for ParseFile.
+ * A valid file name contains only a-zA-Z0-9_. characters and is between 1
+ * and 128 characters.
+ * @property INVALID_FILE_NAME
+ * @static
+ * @final
+ */
+ParseError.INVALID_FILE_NAME = 122;
+
+/**
+ * Error code indicating an invalid ACL was provided.
+ * @property INVALID_ACL
+ * @static
+ * @final
+ */
+ParseError.INVALID_ACL = 123;
+
+/**
+ * Error code indicating that the request timed out on the server. Typically
+ * this indicates that the request is too expensive to run.
+ * @property TIMEOUT
+ * @static
+ * @final
+ */
+ParseError.TIMEOUT = 124;
+
+/**
+ * Error code indicating that the email address was invalid.
+ * @property INVALID_EMAIL_ADDRESS
+ * @static
+ * @final
+ */
+ParseError.INVALID_EMAIL_ADDRESS = 125;
+
+/**
+ * Error code indicating a missing content type.
+ * @property MISSING_CONTENT_TYPE
+ * @static
+ * @final
+ */
+ParseError.MISSING_CONTENT_TYPE = 126;
+
+/**
+ * Error code indicating a missing content length.
+ * @property MISSING_CONTENT_LENGTH
+ * @static
+ * @final
+ */
+ParseError.MISSING_CONTENT_LENGTH = 127;
+
+/**
+ * Error code indicating an invalid content length.
+ * @property INVALID_CONTENT_LENGTH
+ * @static
+ * @final
+ */
+ParseError.INVALID_CONTENT_LENGTH = 128;
+
+/**
+ * Error code indicating a file that was too large.
+ * @property FILE_TOO_LARGE
+ * @static
+ * @final
+ */
+ParseError.FILE_TOO_LARGE = 129;
+
+/**
+ * Error code indicating an error saving a file.
+ * @property FILE_SAVE_ERROR
+ * @static
+ * @final
+ */
+ParseError.FILE_SAVE_ERROR = 130;
+
+/**
+ * Error code indicating that a unique field was given a value that is
+ * already taken.
+ * @property DUPLICATE_VALUE
+ * @static
+ * @final
+ */
+ParseError.DUPLICATE_VALUE = 137;
+
+/**
+ * Error code indicating that a role's name is invalid.
+ * @property INVALID_ROLE_NAME
+ * @static
+ * @final
+ */
+ParseError.INVALID_ROLE_NAME = 139;
+
+/**
+ * Error code indicating that an application quota was exceeded. Upgrade to
+ * resolve.
+ * @property EXCEEDED_QUOTA
+ * @static
+ * @final
+ */
+ParseError.EXCEEDED_QUOTA = 140;
+
+/**
+ * Error code indicating that a Cloud Code script failed.
+ * @property SCRIPT_FAILED
+ * @static
+ * @final
+ */
+ParseError.SCRIPT_FAILED = 141;
+
+/**
+ * Error code indicating that a Cloud Code validation failed.
+ * @property VALIDATION_ERROR
+ * @static
+ * @final
+ */
+ParseError.VALIDATION_ERROR = 142;
+
+/**
+ * Error code indicating that invalid image data was provided.
+ * @property INVALID_IMAGE_DATA
+ * @static
+ * @final
+ */
+ParseError.INVALID_IMAGE_DATA = 143;
+
+/**
+ * Error code indicating an unsaved file.
+ * @property UNSAVED_FILE_ERROR
+ * @static
+ * @final
+ */
+ParseError.UNSAVED_FILE_ERROR = 151;
+
+/**
+ * Error code indicating an invalid push time.
+ * @property INVALID_PUSH_TIME_ERROR
+ * @static
+ * @final
+ */
+ParseError.INVALID_PUSH_TIME_ERROR = 152;
+
+/**
+ * Error code indicating an error deleting a file.
+ * @property FILE_DELETE_ERROR
+ * @static
+ * @final
+ */
+ParseError.FILE_DELETE_ERROR = 153;
+
+/**
+ * Error code indicating that the application has exceeded its request
+ * limit.
+ * @property REQUEST_LIMIT_EXCEEDED
+ * @static
+ * @final
+ */
+ParseError.REQUEST_LIMIT_EXCEEDED = 155;
+
+/**
+ * Error code indicating an invalid event name.
+ * @property INVALID_EVENT_NAME
+ * @static
+ * @final
+ */
+ParseError.INVALID_EVENT_NAME = 160;
+
+/**
+ * Error code indicating that the username is missing or empty.
+ * @property USERNAME_MISSING
+ * @static
+ * @final
+ */
+ParseError.USERNAME_MISSING = 200;
+
+/**
+ * Error code indicating that the password is missing or empty.
+ * @property PASSWORD_MISSING
+ * @static
+ * @final
+ */
+ParseError.PASSWORD_MISSING = 201;
+
+/**
+ * Error code indicating that the username has already been taken.
+ * @property USERNAME_TAKEN
+ * @static
+ * @final
+ */
+ParseError.USERNAME_TAKEN = 202;
+
+/**
+ * Error code indicating that the email has already been taken.
+ * @property EMAIL_TAKEN
+ * @static
+ * @final
+ */
+ParseError.EMAIL_TAKEN = 203;
+
+/**
+ * Error code indicating that the email is missing, but must be specified.
+ * @property EMAIL_MISSING
+ * @static
+ * @final
+ */
+ParseError.EMAIL_MISSING = 204;
+
+/**
+ * Error code indicating that a user with the specified email was not found.
+ * @property EMAIL_NOT_FOUND
+ * @static
+ * @final
+ */
+ParseError.EMAIL_NOT_FOUND = 205;
+
+/**
+ * Error code indicating that a user object without a valid session could
+ * not be altered.
+ * @property SESSION_MISSING
+ * @static
+ * @final
+ */
+ParseError.SESSION_MISSING = 206;
+
+/**
+ * Error code indicating that a user can only be created through signup.
+ * @property MUST_CREATE_USER_THROUGH_SIGNUP
+ * @static
+ * @final
+ */
+ParseError.MUST_CREATE_USER_THROUGH_SIGNUP = 207;
+
+/**
+ * Error code indicating that an an account being linked is already linked
+ * to another user.
+ * @property ACCOUNT_ALREADY_LINKED
+ * @static
+ * @final
+ */
+ParseError.ACCOUNT_ALREADY_LINKED = 208;
+
+/**
+ * Error code indicating that the current session token is invalid.
+ * @property INVALID_SESSION_TOKEN
+ * @static
+ * @final
+ */
+ParseError.INVALID_SESSION_TOKEN = 209;
+
+/**
+ * Error code indicating that a user cannot be linked to an account because
+ * that account's id could not be found.
+ * @property LINKED_ID_MISSING
+ * @static
+ * @final
+ */
+ParseError.LINKED_ID_MISSING = 250;
+
+/**
+ * Error code indicating that a user with a linked (e.g. Facebook) account
+ * has an invalid session.
+ * @property INVALID_LINKED_SESSION
+ * @static
+ * @final
+ */
+ParseError.INVALID_LINKED_SESSION = 251;
+
+/**
+ * Error code indicating that a service being linked (e.g. Facebook or
+ * Twitter) is unsupported.
+ * @property UNSUPPORTED_SERVICE
+ * @static
+ * @final
+ */
+ParseError.UNSUPPORTED_SERVICE = 252;
+
+/**
+ * Error code indicating that there were multiple errors. Aggregate errors
+ * have an "errors" property, which is an array of error objects with more
+ * detail about each error that occurred.
+ * @property AGGREGATE_ERROR
+ * @static
+ * @final
+ */
+ParseError.AGGREGATE_ERROR = 600;
+
+/**
+ * Error code indicating the client was unable to read an input file.
+ * @property FILE_READ_ERROR
+ * @static
+ * @final
+ */
+ParseError.FILE_READ_ERROR = 601;
+
+/**
+ * Error code indicating a real error code is unavailable because
+ * we had to use an XDomainRequest object to allow CORS requests in
+ * Internet Explorer, which strips the body from HTTP responses that have
+ * a non-2XX status code.
+ * @property X_DOMAIN_REQUEST
+ * @static
+ * @final
+ */
+ParseError.X_DOMAIN_REQUEST = 602;
\ No newline at end of file
diff --git a/lib/browser/ParseFile.js b/lib/browser/ParseFile.js
new file mode 100644
index 000000000..48efdb9d8
--- /dev/null
+++ b/lib/browser/ParseFile.js
@@ -0,0 +1,291 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+
+var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
+
+var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
+
+var _createClass2 = require('babel-runtime/helpers/createClass');
+
+var _createClass3 = _interopRequireDefault(_createClass2);
+
+var _CoreManager = require('./CoreManager');
+
+var _CoreManager2 = _interopRequireDefault(_CoreManager);
+
+var _ParsePromise = require('./ParsePromise');
+
+var _ParsePromise2 = _interopRequireDefault(_ParsePromise);
+
+function _interopRequireDefault(obj) {
+ return obj && obj.__esModule ? obj : { default: obj };
+}
+
+/**
+ * Copyright (c) 2015-present, Parse, LLC.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ *
+ *
+ */
+
+var dataUriRegexp = /^data:([a-zA-Z]*\/[a-zA-Z+.-]*);(charset=[a-zA-Z0-9\-\/\s]*,)?base64,/;
+
+function b64Digit(number) {
+ if (number < 26) {
+ return String.fromCharCode(65 + number);
+ }
+ if (number < 52) {
+ return String.fromCharCode(97 + (number - 26));
+ }
+ if (number < 62) {
+ return String.fromCharCode(48 + (number - 52));
+ }
+ if (number === 62) {
+ return '+';
+ }
+ if (number === 63) {
+ return '/';
+ }
+ throw new TypeError('Tried to encode large digit ' + number + ' in base64.');
+}
+
+/**
+ * A Parse.File is a local representation of a file that is saved to the Parse
+ * cloud.
+ * @class Parse.File
+ * @constructor
+ * @param name {String} The file's name. This will be prefixed by a unique
+ * value once the file has finished saving. The file name must begin with
+ * an alphanumeric character, and consist of alphanumeric characters,
+ * periods, spaces, underscores, or dashes.
+ * @param data {Array} The data for the file, as either:
+ * 1. an Array of byte value Numbers, or
+ * 2. an Object like { base64: "..." } with a base64-encoded String.
+ * 3. a File object selected with a file upload control. (3) only works
+ * in Firefox 3.6+, Safari 6.0.2+, Chrome 7+, and IE 10+.
+ * For example:+ * var fileUploadControl = $("#profilePhotoFileUpload")[0]; + * if (fileUploadControl.files.length > 0) { + * var file = fileUploadControl.files[0]; + * var name = "photo.jpg"; + * var parseFile = new Parse.File(name, file); + * parseFile.save().then(function() { + * // The file has been saved to Parse. + * }, function(error) { + * // The file either could not be read, or could not be saved to Parse. + * }); + * }+ * @param type {String} Optional Content-Type header to use for the file. If + * this is omitted, the content type will be inferred from the name's + * extension. + */ + +var ParseFile = function () { + function ParseFile(name, data, type) { + (0, _classCallCheck3.default)(this, ParseFile); + + var specifiedType = type || ''; + + this._name = name; + + if (data !== undefined) { + if (Array.isArray(data)) { + this._source = { + format: 'base64', + base64: ParseFile.encodeBase64(data), + type: specifiedType + }; + } else if (typeof File !== 'undefined' && data instanceof File) { + this._source = { + format: 'file', + file: data, + type: specifiedType + }; + } else if (data && typeof data.base64 === 'string') { + var _base = data.base64; + var commaIndex = _base.indexOf(','); + + if (commaIndex !== -1) { + var matches = dataUriRegexp.exec(_base.slice(0, commaIndex + 1)); + // if data URI with type and charset, there will be 4 matches. + this._source = { + format: 'base64', + base64: _base.slice(commaIndex + 1), + type: matches[1] + }; + } else { + this._source = { + format: 'base64', + base64: _base, + type: specifiedType + }; + } + } else { + throw new TypeError('Cannot create a Parse.File with that data.'); + } + } + } + + /** + * Gets the name of the file. Before save is called, this is the filename + * given by the user. After save is called, that name gets prefixed with a + * unique identifier. + * @method name + * @return {String} + */ + + (0, _createClass3.default)(ParseFile, [{ + key: 'name', + value: function () { + return this._name; + } + + /** + * Gets the url of the file. It is only available after you save the file or + * after you get the file from a Parse.Object. + * @method url + * @param {Object} options An object to specify url options + * @return {String} + */ + + }, { + key: 'url', + value: function (options) { + options = options || {}; + if (!this._url) { + return; + } + if (options.forceSecure) { + return this._url.replace(/^http:\/\//i, 'https://'); + } else { + return this._url; + } + } + + /** + * Saves the file to the Parse cloud. + * @method save + * @param {Object} options A Backbone-style options object. + * @return {Parse.Promise} Promise that is resolved when the save finishes. + */ + + }, { + key: 'save', + value: function (options) { + var _this = this; + + options = options || {}; + var controller = _CoreManager2.default.getFileController(); + if (!this._previousSave) { + if (this._source.format === 'file') { + this._previousSave = controller.saveFile(this._name, this._source).then(function (res) { + _this._name = res.name; + _this._url = res.url; + return _this; + }); + } else { + this._previousSave = controller.saveBase64(this._name, this._source).then(function (res) { + _this._name = res.name; + _this._url = res.url; + return _this; + }); + } + } + if (this._previousSave) { + return this._previousSave._thenRunCallbacks(options); + } + } + }, { + key: 'toJSON', + value: function () { + return { + __type: 'File', + name: this._name, + url: this._url + }; + } + }, { + key: 'equals', + value: function (other) { + if (this === other) { + return true; + } + // Unsaved Files are never equal, since they will be saved to different URLs + return other instanceof ParseFile && this.name() === other.name() && this.url() === other.url() && typeof this.url() !== 'undefined'; + } + }], [{ + key: 'fromJSON', + value: function (obj) { + if (obj.__type !== 'File') { + throw new TypeError('JSON object does not represent a ParseFile'); + } + var file = new ParseFile(obj.name); + file._url = obj.url; + return file; + } + }, { + key: 'encodeBase64', + value: function (bytes) { + var chunks = []; + chunks.length = Math.ceil(bytes.length / 3); + for (var i = 0; i < chunks.length; i++) { + var b1 = bytes[i * 3]; + var b2 = bytes[i * 3 + 1] || 0; + var b3 = bytes[i * 3 + 2] || 0; + + var has2 = i * 3 + 1 < bytes.length; + var has3 = i * 3 + 2 < bytes.length; + + chunks[i] = [b64Digit(b1 >> 2 & 0x3F), b64Digit(b1 << 4 & 0x30 | b2 >> 4 & 0x0F), has2 ? b64Digit(b2 << 2 & 0x3C | b3 >> 6 & 0x03) : '=', has3 ? b64Digit(b3 & 0x3F) : '='].join(''); + } + + return chunks.join(''); + } + }]); + return ParseFile; +}(); + +exports.default = ParseFile; + +var DefaultController = { + saveFile: function (name, source) { + if (source.format !== 'file') { + throw new Error('saveFile can only be used with File-type sources.'); + } + // To directly upload a File, we use a REST-style AJAX request + var headers = { + 'X-Parse-Application-ID': _CoreManager2.default.get('APPLICATION_ID'), + 'X-Parse-JavaScript-Key': _CoreManager2.default.get('JAVASCRIPT_KEY'), + 'Content-Type': source.type || (source.file ? source.file.type : null) + }; + var url = _CoreManager2.default.get('SERVER_URL'); + if (url[url.length - 1] !== '/') { + url += '/'; + } + url += 'files/' + name; + return _CoreManager2.default.getRESTController().ajax('POST', url, source.file, headers); + }, + + saveBase64: function (name, source) { + if (source.format !== 'base64') { + throw new Error('saveBase64 can only be used with Base64-type sources.'); + } + var data = { + base64: source.base64 + }; + if (source.type) { + data._ContentType = source.type; + } + + return _CoreManager2.default.getRESTController().request('POST', 'files/' + name, data); + } +}; + +_CoreManager2.default.setFileController(DefaultController); \ No newline at end of file diff --git a/lib/browser/ParseGeoPoint.js b/lib/browser/ParseGeoPoint.js new file mode 100644 index 000000000..98691f85b --- /dev/null +++ b/lib/browser/ParseGeoPoint.js @@ -0,0 +1,235 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _typeof2 = require('babel-runtime/helpers/typeof'); + +var _typeof3 = _interopRequireDefault(_typeof2); + +var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); + +var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); + +var _createClass2 = require('babel-runtime/helpers/createClass'); + +var _createClass3 = _interopRequireDefault(_createClass2); + +var _ParsePromise = require('./ParsePromise'); + +var _ParsePromise2 = _interopRequireDefault(_ParsePromise); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { default: obj }; +} + +/** + * Creates a new GeoPoint with any of the following forms:
+ * new GeoPoint(otherGeoPoint) + * new GeoPoint(30, 30) + * new GeoPoint([30, 30]) + * new GeoPoint({latitude: 30, longitude: 30}) + * new GeoPoint() // defaults to (0, 0) + *+ * @class Parse.GeoPoint + * @constructor + * + *
Represents a latitude / longitude point that may be associated + * with a key in a ParseObject or used as a reference point for geo queries. + * This allows proximity-based queries on the key.
+ * + *Only one key in a class may contain a GeoPoint.
+ * + *Example:
+ * var point = new Parse.GeoPoint(30.0, -20.0); + * var object = new Parse.Object("PlaceObject"); + * object.set("location", point); + * object.save();+ */ +var ParseGeoPoint = function () { + function ParseGeoPoint(arg1, arg2) { + (0, _classCallCheck3.default)(this, ParseGeoPoint); + + if (Array.isArray(arg1)) { + ParseGeoPoint._validate(arg1[0], arg1[1]); + this._latitude = arg1[0]; + this._longitude = arg1[1]; + } else if ((typeof arg1 === 'undefined' ? 'undefined' : (0, _typeof3.default)(arg1)) === 'object') { + ParseGeoPoint._validate(arg1.latitude, arg1.longitude); + this._latitude = arg1.latitude; + this._longitude = arg1.longitude; + } else if (typeof arg1 === 'number' && typeof arg2 === 'number') { + ParseGeoPoint._validate(arg1, arg2); + this._latitude = arg1; + this._longitude = arg2; + } else { + this._latitude = 0; + this._longitude = 0; + } + } + + /** + * North-south portion of the coordinate, in range [-90, 90]. + * Throws an exception if set out of range in a modern browser. + * @property latitude + * @type Number + */ + + (0, _createClass3.default)(ParseGeoPoint, [{ + key: 'toJSON', + + /** + * Returns a JSON representation of the GeoPoint, suitable for Parse. + * @method toJSON + * @return {Object} + */ + value: function () { + ParseGeoPoint._validate(this._latitude, this._longitude); + return { + __type: 'GeoPoint', + latitude: this._latitude, + longitude: this._longitude + }; + } + }, { + key: 'equals', + value: function (other) { + return other instanceof ParseGeoPoint && this.latitude === other.latitude && this.longitude === other.longitude; + } + + /** + * Returns the distance from this GeoPoint to another in radians. + * @method radiansTo + * @param {Parse.GeoPoint} point the other Parse.GeoPoint. + * @return {Number} + */ + + }, { + key: 'radiansTo', + value: function (point) { + var d2r = Math.PI / 180.0; + var lat1rad = this.latitude * d2r; + var long1rad = this.longitude * d2r; + var lat2rad = point.latitude * d2r; + var long2rad = point.longitude * d2r; + + var sinDeltaLatDiv2 = Math.sin((lat1rad - lat2rad) / 2); + var sinDeltaLongDiv2 = Math.sin((long1rad - long2rad) / 2); + // Square of half the straight line chord distance between both points. + var a = sinDeltaLatDiv2 * sinDeltaLatDiv2 + Math.cos(lat1rad) * Math.cos(lat2rad) * sinDeltaLongDiv2 * sinDeltaLongDiv2; + a = Math.min(1.0, a); + return 2 * Math.asin(Math.sqrt(a)); + } + + /** + * Returns the distance from this GeoPoint to another in kilometers. + * @method kilometersTo + * @param {Parse.GeoPoint} point the other Parse.GeoPoint. + * @return {Number} + */ + + }, { + key: 'kilometersTo', + value: function (point) { + return this.radiansTo(point) * 6371.0; + } + + /** + * Returns the distance from this GeoPoint to another in miles. + * @method milesTo + * @param {Parse.GeoPoint} point the other Parse.GeoPoint. + * @return {Number} + */ + + }, { + key: 'milesTo', + value: function (point) { + return this.radiansTo(point) * 3958.8; + } + + /** + * Throws an exception if the given lat-long is out of bounds. + */ + + }, { + key: 'latitude', + get: function () { + return this._latitude; + }, + set: function (val) { + ParseGeoPoint._validate(val, this.longitude); + this._latitude = val; + } + + /** + * East-west portion of the coordinate, in range [-180, 180]. + * Throws if set out of range in a modern browser. + * @property longitude + * @type Number + */ + + }, { + key: 'longitude', + get: function () { + return this._longitude; + }, + set: function (val) { + ParseGeoPoint._validate(this.latitude, val); + this._longitude = val; + } + }], [{ + key: '_validate', + value: function (latitude, longitude) { + if (latitude !== latitude || longitude !== longitude) { + throw new TypeError('GeoPoint latitude and longitude must be valid numbers'); + } + if (latitude < -90.0) { + throw new TypeError('GeoPoint latitude out of bounds: ' + latitude + ' < -90.0.'); + } + if (latitude > 90.0) { + throw new TypeError('GeoPoint latitude out of bounds: ' + latitude + ' > 90.0.'); + } + if (longitude < -180.0) { + throw new TypeError('GeoPoint longitude out of bounds: ' + longitude + ' < -180.0.'); + } + if (longitude > 180.0) { + throw new TypeError('GeoPoint longitude out of bounds: ' + longitude + ' > 180.0.'); + } + } + + /** + * Creates a GeoPoint with the user's current location, if available. + * Calls options.success with a new GeoPoint instance or calls options.error. + * @method current + * @param {Object} options An object with success and error callbacks. + * @static + */ + + }, { + key: 'current', + value: function (options) { + var promise = new _ParsePromise2.default(); + navigator.geolocation.getCurrentPosition(function (location) { + promise.resolve(new ParseGeoPoint(location.coords.latitude, location.coords.longitude)); + }, function (error) { + promise.reject(error); + }); + + return promise._thenRunCallbacks(options); + } + }]); + return ParseGeoPoint; +}(); /** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + */ + +exports.default = ParseGeoPoint; \ No newline at end of file diff --git a/lib/browser/ParseHooks.js b/lib/browser/ParseHooks.js new file mode 100644 index 000000000..a2057e899 --- /dev/null +++ b/lib/browser/ParseHooks.js @@ -0,0 +1,162 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _promise = require('babel-runtime/core-js/promise'); + +var _promise2 = _interopRequireDefault(_promise); + +exports.getFunctions = getFunctions; +exports.getTriggers = getTriggers; +exports.getFunction = getFunction; +exports.getTrigger = getTrigger; +exports.createFunction = createFunction; +exports.createTrigger = createTrigger; +exports.create = create; +exports.updateFunction = updateFunction; +exports.updateTrigger = updateTrigger; +exports.update = update; +exports.removeFunction = removeFunction; +exports.removeTrigger = removeTrigger; +exports.remove = remove; + +var _CoreManager = require('./CoreManager'); + +var _CoreManager2 = _interopRequireDefault(_CoreManager); + +var _decode = require('./decode'); + +var _decode2 = _interopRequireDefault(_decode); + +var _encode = require('./encode'); + +var _encode2 = _interopRequireDefault(_encode); + +var _ParseError = require('./ParseError'); + +var _ParseError2 = _interopRequireDefault(_ParseError); + +var _ParsePromise = require('./ParsePromise'); + +var _ParsePromise2 = _interopRequireDefault(_ParsePromise); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { default: obj }; +} + +function getFunctions() { + return _CoreManager2.default.getHooksController().get("functions"); +} + +function getTriggers() { + return _CoreManager2.default.getHooksController().get("triggers"); +} + +function getFunction(name) { + return _CoreManager2.default.getHooksController().get("functions", name); +} + +function getTrigger(className, triggerName) { + return _CoreManager2.default.getHooksController().get("triggers", className, triggerName); +} + +function createFunction(functionName, url) { + return create({ functionName: functionName, url: url }); +} + +function createTrigger(className, triggerName, url) { + return create({ className: className, triggerName: triggerName, url: url }); +} + +function create(hook) { + return _CoreManager2.default.getHooksController().create(hook); +} + +function updateFunction(functionName, url) { + return update({ functionName: functionName, url: url }); +} + +function updateTrigger(className, triggerName, url) { + return update({ className: className, triggerName: triggerName, url: url }); +} + +function update(hook) { + return _CoreManager2.default.getHooksController().update(hook); +} + +function removeFunction(functionName) { + return remove({ functionName: functionName }); +} + +function removeTrigger(className, triggerName) { + return remove({ className: className, triggerName: triggerName }); +} + +function remove(hook) { + return _CoreManager2.default.getHooksController().remove(hook); +} + +var DefaultController = { + get: function (type, functionName, triggerName) { + var url = "/hooks/" + type; + if (functionName) { + url += "/" + functionName; + if (triggerName) { + url += "/" + triggerName; + } + } + return this.sendRequest("GET", url); + }, + create: function (hook) { + var url; + if (hook.functionName && hook.url) { + url = "/hooks/functions"; + } else if (hook.className && hook.triggerName && hook.url) { + url = "/hooks/triggers"; + } else { + return _promise2.default.reject({ error: 'invalid hook declaration', code: 143 }); + } + return this.sendRequest("POST", url, hook); + }, + remove: function (hook) { + var url; + if (hook.functionName) { + url = "/hooks/functions/" + hook.functionName; + delete hook.functionName; + } else if (hook.className && hook.triggerName) { + url = "/hooks/triggers/" + hook.className + "/" + hook.triggerName; + delete hook.className; + delete hook.triggerName; + } else { + return _promise2.default.reject({ error: 'invalid hook declaration', code: 143 }); + } + return this.sendRequest("PUT", url, { "__op": "Delete" }); + }, + update: function (hook) { + var url; + if (hook.functionName && hook.url) { + url = "/hooks/functions/" + hook.functionName; + delete hook.functionName; + } else if (hook.className && hook.triggerName && hook.url) { + url = "/hooks/triggers/" + hook.className + "/" + hook.triggerName; + delete hook.className; + delete hook.triggerName; + } else { + return _promise2.default.reject({ error: 'invalid hook declaration', code: 143 }); + } + return this.sendRequest('PUT', url, hook); + }, + sendRequest: function (method, url, body) { + return _CoreManager2.default.getRESTController().request(method, url, body, { useMasterKey: true }).then(function (res) { + var decoded = (0, _decode2.default)(res); + if (decoded) { + return _ParsePromise2.default.as(decoded); + } + return _ParsePromise2.default.error(new _ParseError2.default(_ParseError2.default.INVALID_JSON, 'The server returned an invalid response.')); + }); + } +}; + +_CoreManager2.default.setHooksController(DefaultController); \ No newline at end of file diff --git a/lib/browser/ParseInstallation.js b/lib/browser/ParseInstallation.js new file mode 100644 index 000000000..02d7be1d8 --- /dev/null +++ b/lib/browser/ParseInstallation.js @@ -0,0 +1,65 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _typeof2 = require('babel-runtime/helpers/typeof'); + +var _typeof3 = _interopRequireDefault(_typeof2); + +var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); + +var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); + +var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); + +var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); + +var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); + +var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); + +var _inherits2 = require('babel-runtime/helpers/inherits'); + +var _inherits3 = _interopRequireDefault(_inherits2); + +var _ParseObject2 = require('./ParseObject'); + +var _ParseObject3 = _interopRequireDefault(_ParseObject2); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { default: obj }; +} + +var Installation = function (_ParseObject) { + (0, _inherits3.default)(Installation, _ParseObject); + + function Installation(attributes) { + (0, _classCallCheck3.default)(this, Installation); + + var _this = (0, _possibleConstructorReturn3.default)(this, (Installation.__proto__ || (0, _getPrototypeOf2.default)(Installation)).call(this, '_Installation')); + + if (attributes && (typeof attributes === 'undefined' ? 'undefined' : (0, _typeof3.default)(attributes)) === 'object') { + if (!_this.set(attributes || {})) { + throw new Error('Can\'t create an invalid Session'); + } + } + return _this; + } + + return Installation; +}(_ParseObject3.default); /** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + */ + +exports.default = Installation; + +_ParseObject3.default.registerSubclass('_Installation', Installation); \ No newline at end of file diff --git a/lib/browser/ParseLiveQuery.js b/lib/browser/ParseLiveQuery.js new file mode 100644 index 000000000..a3e5fe31d --- /dev/null +++ b/lib/browser/ParseLiveQuery.js @@ -0,0 +1,241 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _EventEmitter = require('./EventEmitter'); + +var _EventEmitter2 = _interopRequireDefault(_EventEmitter); + +var _LiveQueryClient = require('./LiveQueryClient'); + +var _LiveQueryClient2 = _interopRequireDefault(_LiveQueryClient); + +var _CoreManager = require('./CoreManager'); + +var _CoreManager2 = _interopRequireDefault(_CoreManager); + +var _ParsePromise = require('./ParsePromise'); + +var _ParsePromise2 = _interopRequireDefault(_ParsePromise); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { default: obj }; +} + +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + */ + +function open() { + var LiveQueryController = _CoreManager2.default.getLiveQueryController(); + LiveQueryController.open(); +} + +function close() { + var LiveQueryController = _CoreManager2.default.getLiveQueryController(); + LiveQueryController.close(); +} + +/** + * + * We expose three events to help you monitor the status of the WebSocket connection: + * + *
Open - When we establish the WebSocket connection to the LiveQuery server, you'll get this event. + * + *
+ * Parse.LiveQuery.on('open', () => { + * + * });+ * + *
Close - When we lose the WebSocket connection to the LiveQuery server, you'll get this event. + * + *
+ * Parse.LiveQuery.on('close', () => { + * + * });+ * + *
Error - When some network error or LiveQuery server error happens, you'll get this event. + * + *
+ * Parse.LiveQuery.on('error', (error) => { + * + * });+ * + * @class Parse.LiveQuery + * @static + * + */ +var LiveQuery = new _EventEmitter2.default(); + +/** + * After open is called, the LiveQuery will try to send a connect request + * to the LiveQuery server. + * + * @method open + */ +LiveQuery.open = open; + +/** + * When you're done using LiveQuery, you can call Parse.LiveQuery.close(). + * This function will close the WebSocket connection to the LiveQuery server, + * cancel the auto reconnect, and unsubscribe all subscriptions based on it. + * If you call query.subscribe() after this, we'll create a new WebSocket + * connection to the LiveQuery server. + * + * @method close + */ + +LiveQuery.close = close; +// Register a default onError callback to make sure we do not crash on error +LiveQuery.on('error', function () {}); + +exports.default = LiveQuery; + +function getSessionToken() { + var controller = _CoreManager2.default.getUserController(); + return controller.currentUserAsync().then(function (currentUser) { + return currentUser ? currentUser.getSessionToken() : undefined; + }); +} + +function getLiveQueryClient() { + return _CoreManager2.default.getLiveQueryController().getDefaultLiveQueryClient(); +} + +var defaultLiveQueryClient = void 0; +var DefaultLiveQueryController = { + setDefaultLiveQueryClient: function (liveQueryClient) { + defaultLiveQueryClient = liveQueryClient; + }, + getDefaultLiveQueryClient: function () { + if (defaultLiveQueryClient) { + return _ParsePromise2.default.as(defaultLiveQueryClient); + } + + return getSessionToken().then(function (sessionToken) { + var liveQueryServerURL = _CoreManager2.default.get('LIVEQUERY_SERVER_URL'); + + if (liveQueryServerURL && liveQueryServerURL.indexOf('ws') !== 0) { + throw new Error('You need to set a proper Parse LiveQuery server url before using LiveQueryClient'); + } + + // If we can not find Parse.liveQueryServerURL, we try to extract it from Parse.serverURL + if (!liveQueryServerURL) { + var tempServerURL = _CoreManager2.default.get('SERVER_URL'); + var protocol = 'ws://'; + // If Parse is being served over SSL/HTTPS, ensure LiveQuery Server uses 'wss://' prefix + if (tempServerURL.indexOf('https') === 0) { + protocol = 'wss://'; + } + var host = tempServerURL.replace(/^https?:\/\//, ''); + liveQueryServerURL = protocol + host; + _CoreManager2.default.set('LIVEQUERY_SERVER_URL', liveQueryServerURL); + } + + var applicationId = _CoreManager2.default.get('APPLICATION_ID'); + var javascriptKey = _CoreManager2.default.get('JAVASCRIPT_KEY'); + var masterKey = _CoreManager2.default.get('MASTER_KEY'); + // Get currentUser sessionToken if possible + defaultLiveQueryClient = new _LiveQueryClient2.default({ + applicationId: applicationId, + serverURL: liveQueryServerURL, + javascriptKey: javascriptKey, + masterKey: masterKey, + sessionToken: sessionToken + }); + // Register a default onError callback to make sure we do not crash on error + // Cannot create these events on a nested way because of EventEmiiter from React Native + defaultLiveQueryClient.on('error', function (error) { + LiveQuery.emit('error', error); + }); + defaultLiveQueryClient.on('open', function () { + LiveQuery.emit('open'); + }); + defaultLiveQueryClient.on('close', function () { + LiveQuery.emit('close'); + }); + + return defaultLiveQueryClient; + }); + }, + open: function () { + var _this = this; + + getLiveQueryClient().then(function (liveQueryClient) { + _this.resolve(liveQueryClient.open()); + }); + }, + close: function () { + var _this2 = this; + + getLiveQueryClient().then(function (liveQueryClient) { + _this2.resolve(liveQueryClient.close()); + }); + }, + subscribe: function (query) { + var _this3 = this; + + var subscriptionWrap = new _EventEmitter2.default(); + + getLiveQueryClient().then(function (liveQueryClient) { + if (liveQueryClient.shouldOpen()) { + liveQueryClient.open(); + } + var promiseSessionToken = getSessionToken(); + // new event emitter + return promiseSessionToken.then(function (sessionToken) { + + var subscription = liveQueryClient.subscribe(query, sessionToken); + // enter, leave create, etc + + subscriptionWrap.id = subscription.id; + subscriptionWrap.query = subscription.query; + subscriptionWrap.sessionToken = subscription.sessionToken; + subscriptionWrap.unsubscribe = subscription.unsubscribe; + // Cannot create these events on a nested way because of EventEmiiter from React Native + subscription.on('open', function () { + subscriptionWrap.emit('open'); + }); + subscription.on('create', function (object) { + subscriptionWrap.emit('create', object); + }); + subscription.on('update', function (object) { + subscriptionWrap.emit('update', object); + }); + subscription.on('enter', function (object) { + subscriptionWrap.emit('enter', object); + }); + subscription.on('leave', function (object) { + subscriptionWrap.emit('leave', object); + }); + subscription.on('delete', function (object) { + subscriptionWrap.emit('delete', object); + }); + + _this3.resolve(); + }); + }); + return subscriptionWrap; + }, + unsubscribe: function (subscription) { + var _this4 = this; + + getLiveQueryClient().then(function (liveQueryClient) { + _this4.resolve(liveQueryClient.unsubscribe(subscription)); + }); + }, + _clearCachedDefaultClient: function () { + defaultLiveQueryClient = null; + } +}; + +_CoreManager2.default.setLiveQueryController(DefaultLiveQueryController); \ No newline at end of file diff --git a/lib/browser/ParseObject.js b/lib/browser/ParseObject.js new file mode 100644 index 000000000..f08795e52 --- /dev/null +++ b/lib/browser/ParseObject.js @@ -0,0 +1,2001 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _defineProperty = require('babel-runtime/core-js/object/define-property'); + +var _defineProperty2 = _interopRequireDefault(_defineProperty); + +var _create = require('babel-runtime/core-js/object/create'); + +var _create2 = _interopRequireDefault(_create); + +var _freeze = require('babel-runtime/core-js/object/freeze'); + +var _freeze2 = _interopRequireDefault(_freeze); + +var _stringify = require('babel-runtime/core-js/json/stringify'); + +var _stringify2 = _interopRequireDefault(_stringify); + +var _keys = require('babel-runtime/core-js/object/keys'); + +var _keys2 = _interopRequireDefault(_keys); + +var _typeof2 = require('babel-runtime/helpers/typeof'); + +var _typeof3 = _interopRequireDefault(_typeof2); + +var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); + +var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); + +var _createClass2 = require('babel-runtime/helpers/createClass'); + +var _createClass3 = _interopRequireDefault(_createClass2); + +var _CoreManager = require('./CoreManager'); + +var _CoreManager2 = _interopRequireDefault(_CoreManager); + +var _canBeSerialized = require('./canBeSerialized'); + +var _canBeSerialized2 = _interopRequireDefault(_canBeSerialized); + +var _decode = require('./decode'); + +var _decode2 = _interopRequireDefault(_decode); + +var _encode = require('./encode'); + +var _encode2 = _interopRequireDefault(_encode); + +var _equals = require('./equals'); + +var _equals2 = _interopRequireDefault(_equals); + +var _escape2 = require('./escape'); + +var _escape3 = _interopRequireDefault(_escape2); + +var _ParseACL = require('./ParseACL'); + +var _ParseACL2 = _interopRequireDefault(_ParseACL); + +var _parseDate = require('./parseDate'); + +var _parseDate2 = _interopRequireDefault(_parseDate); + +var _ParseError = require('./ParseError'); + +var _ParseError2 = _interopRequireDefault(_ParseError); + +var _ParseFile = require('./ParseFile'); + +var _ParseFile2 = _interopRequireDefault(_ParseFile); + +var _ParseOp = require('./ParseOp'); + +var _ParsePromise = require('./ParsePromise'); + +var _ParsePromise2 = _interopRequireDefault(_ParsePromise); + +var _ParseQuery = require('./ParseQuery'); + +var _ParseQuery2 = _interopRequireDefault(_ParseQuery); + +var _ParseRelation = require('./ParseRelation'); + +var _ParseRelation2 = _interopRequireDefault(_ParseRelation); + +var _SingleInstanceStateController = require('./SingleInstanceStateController'); + +var SingleInstanceStateController = _interopRequireWildcard(_SingleInstanceStateController); + +var _unique = require('./unique'); + +var _unique2 = _interopRequireDefault(_unique); + +var _UniqueInstanceStateController = require('./UniqueInstanceStateController'); + +var UniqueInstanceStateController = _interopRequireWildcard(_UniqueInstanceStateController); + +var _unsavedChildren = require('./unsavedChildren'); + +var _unsavedChildren2 = _interopRequireDefault(_unsavedChildren); + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } else { + var newObj = {};if (obj != null) { + for (var key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; + } + }newObj.default = obj;return newObj; + } +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { default: obj }; +} + +// Mapping of class names to constructors, so we can populate objects from the +// server with appropriate subclasses of ParseObject +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + */ + +var classMap = {}; + +// Global counter for generating unique local Ids +var localCount = 0; +// Global counter for generating unique Ids for non-single-instance objects +var objectCount = 0; +// On web clients, objects are single-instance: any two objects with the same Id +// will have the same attributes. However, this may be dangerous default +// behavior in a server scenario +var singleInstance = !_CoreManager2.default.get('IS_NODE'); +if (singleInstance) { + _CoreManager2.default.setObjectStateController(SingleInstanceStateController); +} else { + _CoreManager2.default.setObjectStateController(UniqueInstanceStateController); +} + +function getServerUrlPath() { + var serverUrl = _CoreManager2.default.get('SERVER_URL'); + if (serverUrl[serverUrl.length - 1] !== '/') { + serverUrl += '/'; + } + var url = serverUrl.replace(/https?:\/\//, ''); + return url.substr(url.indexOf('/')); +} + +/** + * Creates a new model with defined attributes. + * + *
You won't normally call this method directly. It is recommended that
+ * you use a subclass of Parse.Object
instead, created by calling
+ * extend
.
However, if you don't want to use a subclass, or aren't sure which + * subclass is appropriate, you can use this form:
+ * var object = new Parse.Object("ClassName"); + *+ * That is basically equivalent to:
+ * var MyClass = Parse.Object.extend("ClassName"); + * var object = new MyClass(); + *+ * + * @class Parse.Object + * @constructor + * @param {String} className The class name for the object + * @param {Object} attributes The initial set of data to store in the object. + * @param {Object} options The options for this object instance. + */ + +var ParseObject = function () { + /** + * The ID of this object, unique within its class. + * @property id + * @type String + */ + function ParseObject(className, attributes, options) { + (0, _classCallCheck3.default)(this, ParseObject); + + // Enable legacy initializers + if (typeof this.initialize === 'function') { + this.initialize.apply(this, arguments); + } + + var toSet = null; + this._objCount = objectCount++; + if (typeof className === 'string') { + this.className = className; + if (attributes && (typeof attributes === 'undefined' ? 'undefined' : (0, _typeof3.default)(attributes)) === 'object') { + toSet = attributes; + } + } else if (className && (typeof className === 'undefined' ? 'undefined' : (0, _typeof3.default)(className)) === 'object') { + this.className = className.className; + toSet = {}; + for (var attr in className) { + if (attr !== 'className') { + toSet[attr] = className[attr]; + } + } + if (attributes && (typeof attributes === 'undefined' ? 'undefined' : (0, _typeof3.default)(attributes)) === 'object') { + options = attributes; + } + } + if (toSet && !this.set(toSet, options)) { + throw new Error('Can\'t create an invalid Parse Object'); + } + } + + /** Prototype getters / setters **/ + + (0, _createClass3.default)(ParseObject, [{ + key: '_getId', + + /** Private methods **/ + + /** + * Returns a local or server Id used uniquely identify this object + */ + value: function () { + if (typeof this.id === 'string') { + return this.id; + } + if (typeof this._localId === 'string') { + return this._localId; + } + var localId = 'local' + String(localCount++); + this._localId = localId; + return localId; + } + + /** + * Returns a unique identifier used to pull data from the State Controller. + */ + + }, { + key: '_getStateIdentifier', + value: function () { + if (singleInstance) { + var _id = this.id; + if (!_id) { + _id = this._getId(); + } + return { + id: _id, + className: this.className + }; + } else { + return this; + } + } + }, { + key: '_getServerData', + value: function () { + var stateController = _CoreManager2.default.getObjectStateController(); + return stateController.getServerData(this._getStateIdentifier()); + } + }, { + key: '_clearServerData', + value: function () { + var serverData = this._getServerData(); + var unset = {}; + for (var attr in serverData) { + unset[attr] = undefined; + } + var stateController = _CoreManager2.default.getObjectStateController(); + stateController.setServerData(this._getStateIdentifier(), unset); + } + }, { + key: '_getPendingOps', + value: function () { + var stateController = _CoreManager2.default.getObjectStateController(); + return stateController.getPendingOps(this._getStateIdentifier()); + } + }, { + key: '_clearPendingOps', + value: function () { + var pending = this._getPendingOps(); + var latest = pending[pending.length - 1]; + var keys = (0, _keys2.default)(latest); + keys.forEach(function (key) { + delete latest[key]; + }); + } + }, { + key: '_getDirtyObjectAttributes', + value: function () { + var attributes = this.attributes; + var stateController = _CoreManager2.default.getObjectStateController(); + var objectCache = stateController.getObjectCache(this._getStateIdentifier()); + var dirty = {}; + for (var attr in attributes) { + var val = attributes[attr]; + if (val && (typeof val === 'undefined' ? 'undefined' : (0, _typeof3.default)(val)) === 'object' && !(val instanceof ParseObject) && !(val instanceof _ParseFile2.default) && !(val instanceof _ParseRelation2.default)) { + // Due to the way browsers construct maps, the key order will not change + // unless the object is changed + try { + var json = (0, _encode2.default)(val, false, true); + var stringified = (0, _stringify2.default)(json); + if (objectCache[attr] !== stringified) { + dirty[attr] = val; + } + } catch (e) { + // Error occurred, possibly by a nested unsaved pointer in a mutable container + // No matter how it happened, it indicates a change in the attribute + dirty[attr] = val; + } + } + } + return dirty; + } + }, { + key: '_toFullJSON', + value: function (seen) { + var json = this.toJSON(seen); + json.__type = 'Object'; + json.className = this.className; + return json; + } + }, { + key: '_getSaveJSON', + value: function () { + var pending = this._getPendingOps(); + var dirtyObjects = this._getDirtyObjectAttributes(); + var json = {}; + + for (var attr in dirtyObjects) { + json[attr] = new _ParseOp.SetOp(dirtyObjects[attr]).toJSON(); + } + for (attr in pending[0]) { + json[attr] = pending[0][attr].toJSON(); + } + return json; + } + }, { + key: '_getSaveParams', + value: function () { + var method = this.id ? 'PUT' : 'POST'; + var body = this._getSaveJSON(); + var path = 'classes/' + this.className; + if (this.id) { + path += '/' + this.id; + } else if (this.className === '_User') { + path = 'users'; + } + return { + method: method, + body: body, + path: path + }; + } + }, { + key: '_finishFetch', + value: function (serverData) { + if (!this.id && serverData.objectId) { + this.id = serverData.objectId; + } + var stateController = _CoreManager2.default.getObjectStateController(); + stateController.initializeState(this._getStateIdentifier()); + var decoded = {}; + for (var attr in serverData) { + if (attr === 'ACL') { + decoded[attr] = new _ParseACL2.default(serverData[attr]); + } else if (attr !== 'objectId') { + decoded[attr] = (0, _decode2.default)(serverData[attr]); + if (decoded[attr] instanceof _ParseRelation2.default) { + decoded[attr]._ensureParentAndKey(this, attr); + } + } + } + if (decoded.createdAt && typeof decoded.createdAt === 'string') { + decoded.createdAt = (0, _parseDate2.default)(decoded.createdAt); + } + if (decoded.updatedAt && typeof decoded.updatedAt === 'string') { + decoded.updatedAt = (0, _parseDate2.default)(decoded.updatedAt); + } + if (!decoded.updatedAt && decoded.createdAt) { + decoded.updatedAt = decoded.createdAt; + } + stateController.commitServerChanges(this._getStateIdentifier(), decoded); + } + }, { + key: '_setExisted', + value: function (existed) { + var stateController = _CoreManager2.default.getObjectStateController(); + var state = stateController.getState(this._getStateIdentifier()); + if (state) { + state.existed = existed; + } + } + }, { + key: '_migrateId', + value: function (serverId) { + if (this._localId && serverId) { + if (singleInstance) { + var stateController = _CoreManager2.default.getObjectStateController(); + var oldState = stateController.removeState(this._getStateIdentifier()); + this.id = serverId; + delete this._localId; + if (oldState) { + stateController.initializeState(this._getStateIdentifier(), oldState); + } + } else { + this.id = serverId; + delete this._localId; + } + } + } + }, { + key: '_handleSaveResponse', + value: function (response, status) { + var changes = {}; + + var stateController = _CoreManager2.default.getObjectStateController(); + var pending = stateController.popPendingState(this._getStateIdentifier()); + for (var attr in pending) { + if (pending[attr] instanceof _ParseOp.RelationOp) { + changes[attr] = pending[attr].applyTo(undefined, this, attr); + } else if (!(attr in response)) { + // Only SetOps and UnsetOps should not come back with results + changes[attr] = pending[attr].applyTo(undefined); + } + } + for (attr in response) { + if ((attr === 'createdAt' || attr === 'updatedAt') && typeof response[attr] === 'string') { + changes[attr] = (0, _parseDate2.default)(response[attr]); + } else if (attr === 'ACL') { + changes[attr] = new _ParseACL2.default(response[attr]); + } else if (attr !== 'objectId') { + changes[attr] = (0, _decode2.default)(response[attr]); + if (changes[attr] instanceof _ParseOp.UnsetOp) { + changes[attr] = undefined; + } + } + } + if (changes.createdAt && !changes.updatedAt) { + changes.updatedAt = changes.createdAt; + } + + this._migrateId(response.objectId); + + if (status !== 201) { + this._setExisted(true); + } + + stateController.commitServerChanges(this._getStateIdentifier(), changes); + } + }, { + key: '_handleSaveError', + value: function () { + this._getPendingOps(); + + var stateController = _CoreManager2.default.getObjectStateController(); + stateController.mergeFirstPendingState(this._getStateIdentifier()); + } + + /** Public methods **/ + + }, { + key: 'initialize', + value: function () {} + // NOOP + + + /** + * Returns a JSON version of the object suitable for saving to Parse. + * @method toJSON + * @return {Object} + */ + + }, { + key: 'toJSON', + value: function (seen) { + var seenEntry = this.id ? this.className + ':' + this.id : this; + var seen = seen || [seenEntry]; + var json = {}; + var attrs = this.attributes; + for (var attr in attrs) { + if ((attr === 'createdAt' || attr === 'updatedAt') && attrs[attr].toJSON) { + json[attr] = attrs[attr].toJSON(); + } else { + json[attr] = (0, _encode2.default)(attrs[attr], false, false, seen); + } + } + var pending = this._getPendingOps(); + for (var attr in pending[0]) { + json[attr] = pending[0][attr].toJSON(); + } + + if (this.id) { + json.objectId = this.id; + } + return json; + } + + /** + * Determines whether this ParseObject is equal to another ParseObject + * @method equals + * @return {Boolean} + */ + + }, { + key: 'equals', + value: function (other) { + if (this === other) { + return true; + } + return other instanceof ParseObject && this.className === other.className && this.id === other.id && typeof this.id !== 'undefined'; + } + + /** + * Returns true if this object has been modified since its last + * save/refresh. If an attribute is specified, it returns true only if that + * particular attribute has been modified since the last save/refresh. + * @method dirty + * @param {String} attr An attribute name (optional). + * @return {Boolean} + */ + + }, { + key: 'dirty', + value: function (attr) { + if (!this.id) { + return true; + } + var pendingOps = this._getPendingOps(); + var dirtyObjects = this._getDirtyObjectAttributes(); + if (attr) { + if (dirtyObjects.hasOwnProperty(attr)) { + return true; + } + for (var i = 0; i < pendingOps.length; i++) { + if (pendingOps[i].hasOwnProperty(attr)) { + return true; + } + } + return false; + } + if ((0, _keys2.default)(pendingOps[0]).length !== 0) { + return true; + } + if ((0, _keys2.default)(dirtyObjects).length !== 0) { + return true; + } + return false; + } + + /** + * Returns an array of keys that have been modified since last save/refresh + * @method dirtyKeys + * @return {Array of string} + */ + + }, { + key: 'dirtyKeys', + value: function () { + var pendingOps = this._getPendingOps(); + var keys = {}; + for (var i = 0; i < pendingOps.length; i++) { + for (var attr in pendingOps[i]) { + keys[attr] = true; + } + } + var dirtyObjects = this._getDirtyObjectAttributes(); + for (var attr in dirtyObjects) { + keys[attr] = true; + } + return (0, _keys2.default)(keys); + } + + /** + * Gets a Pointer referencing this Object. + * @method toPointer + * @return {Object} + */ + + }, { + key: 'toPointer', + value: function () { + if (!this.id) { + throw new Error('Cannot create a pointer to an unsaved ParseObject'); + } + return { + __type: 'Pointer', + className: this.className, + objectId: this.id + }; + } + + /** + * Gets the value of an attribute. + * @method get + * @param {String} attr The string name of an attribute. + */ + + }, { + key: 'get', + value: function (attr) { + return this.attributes[attr]; + } + + /** + * Gets a relation on the given class for the attribute. + * @method relation + * @param String attr The attribute to get the relation for. + */ + + }, { + key: 'relation', + value: function (attr) { + var value = this.get(attr); + if (value) { + if (!(value instanceof _ParseRelation2.default)) { + throw new Error('Called relation() on non-relation field ' + attr); + } + value._ensureParentAndKey(this, attr); + return value; + } + return new _ParseRelation2.default(this, attr); + } + + /** + * Gets the HTML-escaped value of an attribute. + * @method escape + * @param {String} attr The string name of an attribute. + */ + + }, { + key: 'escape', + value: function (attr) { + var val = this.attributes[attr]; + if (val == null) { + return ''; + } + + if (typeof val !== 'string') { + if (typeof val.toString !== 'function') { + return ''; + } + val = val.toString(); + } + return (0, _escape3.default)(val); + } + + /** + * Returns
true
if the attribute contains a value that is not
+ * null or undefined.
+ * @method has
+ * @param {String} attr The string name of the attribute.
+ * @return {Boolean}
+ */
+
+ }, {
+ key: 'has',
+ value: function (attr) {
+ var attributes = this.attributes;
+ if (attributes.hasOwnProperty(attr)) {
+ return attributes[attr] != null;
+ }
+ return false;
+ }
+
+ /**
+ * Sets a hash of model attributes on the object.
+ *
+ * You can call it with an object containing keys and values, or with one + * key and value. For example:
+ * gameTurn.set({ + * player: player1, + * diceRoll: 2 + * }, { + * error: function(gameTurnAgain, error) { + * // The set failed validation. + * } + * }); + * + * game.set("currentPlayer", player2, { + * error: function(gameTurnAgain, error) { + * // The set failed validation. + * } + * }); + * + * game.set("finished", true);+ * + * @method set + * @param {String} key The key to set. + * @param {} value The value to give it. + * @param {Object} options A set of options for the set. + * The only supported option is
error
.
+ * @return {Boolean} true if the set succeeded.
+ */
+
+ }, {
+ key: 'set',
+ value: function (key, value, options) {
+ var changes = {};
+ var newOps = {};
+ if (key && (typeof key === 'undefined' ? 'undefined' : (0, _typeof3.default)(key)) === 'object') {
+ changes = key;
+ options = value;
+ } else if (typeof key === 'string') {
+ changes[key] = value;
+ } else {
+ return this;
+ }
+
+ options = options || {};
+ var readonly = [];
+ if (typeof this.constructor.readOnlyAttributes === 'function') {
+ readonly = readonly.concat(this.constructor.readOnlyAttributes());
+ }
+ for (var k in changes) {
+ if (k === 'createdAt' || k === 'updatedAt') {
+ // This property is read-only, but for legacy reasons we silently
+ // ignore it
+ continue;
+ }
+ if (readonly.indexOf(k) > -1) {
+ throw new Error('Cannot modify readonly attribute: ' + k);
+ }
+ if (options.unset) {
+ newOps[k] = new _ParseOp.UnsetOp();
+ } else if (changes[k] instanceof _ParseOp.Op) {
+ newOps[k] = changes[k];
+ } else if (changes[k] && (0, _typeof3.default)(changes[k]) === 'object' && typeof changes[k].__op === 'string') {
+ newOps[k] = (0, _ParseOp.opFromJSON)(changes[k]);
+ } else if (k === 'objectId' || k === 'id') {
+ if (typeof changes[k] === 'string') {
+ this.id = changes[k];
+ }
+ } else if (k === 'ACL' && (0, _typeof3.default)(changes[k]) === 'object' && !(changes[k] instanceof _ParseACL2.default)) {
+ newOps[k] = new _ParseOp.SetOp(new _ParseACL2.default(changes[k]));
+ } else {
+ newOps[k] = new _ParseOp.SetOp(changes[k]);
+ }
+ }
+
+ // Calculate new values
+ var currentAttributes = this.attributes;
+ var newValues = {};
+ for (var attr in newOps) {
+ if (newOps[attr] instanceof _ParseOp.RelationOp) {
+ newValues[attr] = newOps[attr].applyTo(currentAttributes[attr], this, attr);
+ } else if (!(newOps[attr] instanceof _ParseOp.UnsetOp)) {
+ newValues[attr] = newOps[attr].applyTo(currentAttributes[attr]);
+ }
+ }
+
+ // Validate changes
+ if (!options.ignoreValidation) {
+ var validation = this.validate(newValues);
+ if (validation) {
+ if (typeof options.error === 'function') {
+ options.error(this, validation);
+ }
+ return false;
+ }
+ }
+
+ // Consolidate Ops
+ var pendingOps = this._getPendingOps();
+ var last = pendingOps.length - 1;
+ var stateController = _CoreManager2.default.getObjectStateController();
+ for (var attr in newOps) {
+ var nextOp = newOps[attr].mergeWith(pendingOps[last][attr]);
+ stateController.setPendingOp(this._getStateIdentifier(), attr, nextOp);
+ }
+
+ return this;
+ }
+
+ /**
+ * Remove an attribute from the model. This is a noop if the attribute doesn't
+ * exist.
+ * @method unset
+ * @param {String} attr The string name of an attribute.
+ */
+
+ }, {
+ key: 'unset',
+ value: function (attr, options) {
+ options = options || {};
+ options.unset = true;
+ return this.set(attr, null, options);
+ }
+
+ /**
+ * Atomically increments the value of the given attribute the next time the
+ * object is saved. If no amount is specified, 1 is used by default.
+ *
+ * @method increment
+ * @param attr {String} The key.
+ * @param amount {Number} The amount to increment by (optional).
+ */
+
+ }, {
+ key: 'increment',
+ value: function (attr, amount) {
+ if (typeof amount === 'undefined') {
+ amount = 1;
+ }
+ if (typeof amount !== 'number') {
+ throw new Error('Cannot increment by a non-numeric amount.');
+ }
+ return this.set(attr, new _ParseOp.IncrementOp(amount));
+ }
+
+ /**
+ * Atomically add an object to the end of the array associated with a given
+ * key.
+ * @method add
+ * @param attr {String} The key.
+ * @param item {} The item to add.
+ */
+
+ }, {
+ key: 'add',
+ value: function (attr, item) {
+ return this.set(attr, new _ParseOp.AddOp([item]));
+ }
+
+ /**
+ * Atomically add an object to the array associated with a given key, only
+ * if it is not already present in the array. The position of the insert is
+ * not guaranteed.
+ *
+ * @method addUnique
+ * @param attr {String} The key.
+ * @param item {} The object to add.
+ */
+
+ }, {
+ key: 'addUnique',
+ value: function (attr, item) {
+ return this.set(attr, new _ParseOp.AddUniqueOp([item]));
+ }
+
+ /**
+ * Atomically remove all instances of an object from the array associated
+ * with a given key.
+ *
+ * @method remove
+ * @param attr {String} The key.
+ * @param item {} The object to remove.
+ */
+
+ }, {
+ key: 'remove',
+ value: function (attr, item) {
+ return this.set(attr, new _ParseOp.RemoveOp([item]));
+ }
+
+ /**
+ * Returns an instance of a subclass of Parse.Op describing what kind of
+ * modification has been performed on this field since the last time it was
+ * saved. For example, after calling object.increment("x"), calling
+ * object.op("x") would return an instance of Parse.Op.Increment.
+ *
+ * @method op
+ * @param attr {String} The key.
+ * @returns {Parse.Op} The operation, or undefined if none.
+ */
+
+ }, {
+ key: 'op',
+ value: function (attr) {
+ var pending = this._getPendingOps();
+ for (var i = pending.length; i--;) {
+ if (pending[i][attr]) {
+ return pending[i][attr];
+ }
+ }
+ }
+
+ /**
+ * Creates a new model with identical attributes to this one, similar to Backbone.Model's clone()
+ * @method clone
+ * @return {Parse.Object}
+ */
+
+ }, {
+ key: 'clone',
+ value: function () {
+ var clone = new this.constructor();
+ if (!clone.className) {
+ clone.className = this.className;
+ }
+ var attributes = this.attributes;
+ if (typeof this.constructor.readOnlyAttributes === 'function') {
+ var readonly = this.constructor.readOnlyAttributes() || [];
+ // Attributes are frozen, so we have to rebuild an object,
+ // rather than delete readonly keys
+ var copy = {};
+ for (var a in attributes) {
+ if (readonly.indexOf(a) < 0) {
+ copy[a] = attributes[a];
+ }
+ }
+ attributes = copy;
+ }
+ if (clone.set) {
+ clone.set(attributes);
+ }
+ return clone;
+ }
+
+ /**
+ * Creates a new instance of this object. Not to be confused with clone()
+ * @method newInstance
+ * @return {Parse.Object}
+ */
+
+ }, {
+ key: 'newInstance',
+ value: function () {
+ var clone = new this.constructor();
+ if (!clone.className) {
+ clone.className = this.className;
+ }
+ clone.id = this.id;
+ if (singleInstance) {
+ // Just return an object with the right id
+ return clone;
+ }
+
+ var stateController = _CoreManager2.default.getObjectStateController();
+ if (stateController) {
+ stateController.duplicateState(this._getStateIdentifier(), clone._getStateIdentifier());
+ }
+ return clone;
+ }
+
+ /**
+ * Returns true if this object has never been saved to Parse.
+ * @method isNew
+ * @return {Boolean}
+ */
+
+ }, {
+ key: 'isNew',
+ value: function () {
+ return !this.id;
+ }
+
+ /**
+ * Returns true if this object was created by the Parse server when the
+ * object might have already been there (e.g. in the case of a Facebook
+ * login)
+ * @method existed
+ * @return {Boolean}
+ */
+
+ }, {
+ key: 'existed',
+ value: function () {
+ if (!this.id) {
+ return false;
+ }
+ var stateController = _CoreManager2.default.getObjectStateController();
+ var state = stateController.getState(this._getStateIdentifier());
+ if (state) {
+ return state.existed;
+ }
+ return false;
+ }
+
+ /**
+ * Checks if the model is currently in a valid state.
+ * @method isValid
+ * @return {Boolean}
+ */
+
+ }, {
+ key: 'isValid',
+ value: function () {
+ return !this.validate(this.attributes);
+ }
+
+ /**
+ * You should not call this function directly unless you subclass
+ * Parse.Object
, in which case you can override this method
+ * to provide additional validation on set
and
+ * save
. Your implementation should return
+ *
+ * @method validate
+ * @param {Object} attrs The current data to validate.
+ * @return {} False if the data is valid. An error object otherwise.
+ * @see Parse.Object#set
+ */
+
+ }, {
+ key: 'validate',
+ value: function (attrs) {
+ if (attrs.hasOwnProperty('ACL') && !(attrs.ACL instanceof _ParseACL2.default)) {
+ return new _ParseError2.default(_ParseError2.default.OTHER_CAUSE, 'ACL must be a Parse ACL.');
+ }
+ for (var key in attrs) {
+ if (!/^[A-Za-z][0-9A-Za-z_]*$/.test(key)) {
+ return new _ParseError2.default(_ParseError2.default.INVALID_KEY_NAME);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns the ACL for this object.
+ * @method getACL
+ * @returns {Parse.ACL} An instance of Parse.ACL.
+ * @see Parse.Object#get
+ */
+
+ }, {
+ key: 'getACL',
+ value: function () {
+ var acl = this.get('ACL');
+ if (acl instanceof _ParseACL2.default) {
+ return acl;
+ }
+ return null;
+ }
+
+ /**
+ * Sets the ACL to be used for this object.
+ * @method setACL
+ * @param {Parse.ACL} acl An instance of Parse.ACL.
+ * @param {Object} options Optional Backbone-like options object to be
+ * passed in to set.
+ * @return {Boolean} Whether the set passed validation.
+ * @see Parse.Object#set
+ */
+
+ }, {
+ key: 'setACL',
+ value: function (acl, options) {
+ return this.set('ACL', acl, options);
+ }
+
+ /**
+ * Clears any changes to this object made since the last call to save()
+ * @method revert
+ */
+
+ }, {
+ key: 'revert',
+ value: function () {
+ this._clearPendingOps();
+ }
+
+ /**
+ * Clears all attributes on a model
+ * @method clear
+ */
+
+ }, {
+ key: 'clear',
+ value: function () {
+ var attributes = this.attributes;
+ var erasable = {};
+ var readonly = ['createdAt', 'updatedAt'];
+ if (typeof this.constructor.readOnlyAttributes === 'function') {
+ readonly = readonly.concat(this.constructor.readOnlyAttributes());
+ }
+ for (var attr in attributes) {
+ if (readonly.indexOf(attr) < 0) {
+ erasable[attr] = true;
+ }
+ }
+ return this.set(erasable, { unset: true });
+ }
+
+ /**
+ * Fetch the model from the server. If the server's representation of the
+ * model differs from its current attributes, they will be overriden.
+ *
+ * @method fetch
+ * @param {Object} options A Backbone-style callback object.
+ * Valid options are:+ * object.save();+ * or
+ * object.save(null, options);+ * or
+ * object.save(attrs, options);+ * or
+ * object.save(key, value, options);+ * + * For example,
+ * gameTurn.save({ + * player: "Jake Cutter", + * diceRoll: 2 + * }, { + * success: function(gameTurnAgain) { + * // The save was successful. + * }, + * error: function(gameTurnAgain, error) { + * // The save failed. Error is an instance of Parse.Error. + * } + * });+ * or with promises:
+ * gameTurn.save({ + * player: "Jake Cutter", + * diceRoll: 2 + * }).then(function(gameTurnAgain) { + * // The save was successful. + * }, function(error) { + * // The save failed. Error is an instance of Parse.Error. + * });+ * + * @method save + * @param {Object} options A Backbone-style callback object. + * Valid options are:
+ * Parse.Object.fetchAll([object1, object2, ...], { + * success: function(list) { + * // All the objects were fetched. + * }, + * error: function(error) { + * // An error occurred while fetching one of the objects. + * }, + * }); + *+ * + * @method fetchAll + * @param {Array} list A list of
Parse.Object
.
+ * @param {Object} options A Backbone-style callback object.
+ * @static
+ * Valid options are:+ * Parse.Object.fetchAllIfNeeded([object1, ...], { + * success: function(list) { + * // Objects were fetched and updated. + * }, + * error: function(error) { + * // An error occurred while fetching one of the objects. + * }, + * }); + *+ * + * @method fetchAllIfNeeded + * @param {Array} list A list of
Parse.Object
.
+ * @param {Object} options A Backbone-style callback object.
+ * @static
+ * Valid options are:Unlike saveAll, if an error occurs while deleting an individual model, + * this method will continue trying to delete the rest of the models if + * possible, except in the case of a fatal error like a connection error. + * + *
In particular, the Parse.Error object returned in the case of error may + * be one of two types: + * + *
+ * Parse.Object.destroyAll([object1, object2, ...], { + * success: function() { + * // All the objects were deleted. + * }, + * error: function(error) { + * // An error occurred while deleting one or more of the objects. + * // If this is an aggregate error, then we can inspect each error + * // object individually to determine the reason why a particular + * // object was not deleted. + * if (error.code === Parse.Error.AGGREGATE_ERROR) { + * for (var i = 0; i < error.errors.length; i++) { + * console.log("Couldn't delete " + error.errors[i].object.id + + * "due to " + error.errors[i].message); + * } + * } else { + * console.log("Delete aborted because of " + error.message); + * } + * }, + * }); + *+ * + * @method destroyAll + * @param {Array} list A list of
Parse.Object
.
+ * @param {Object} options A Backbone-style callback object.
+ * @static
+ * Valid options are:+ * Parse.Object.saveAll([object1, object2, ...], { + * success: function(list) { + * // All the objects were saved. + * }, + * error: function(error) { + * // An error occurred while saving one of the objects. + * }, + * }); + *+ * + * @method saveAll + * @param {Array} list A list of
Parse.Object
.
+ * @param {Object} options A Backbone-style callback object.
+ * @static
+ * Valid options are:A shortcut for:
+ * var Foo = Parse.Object.extend("Foo"); + * var pointerToFoo = new Foo(); + * pointerToFoo.id = "myObjectId"; + *+ * + * @method createWithoutData + * @param {String} id The ID of the object to create a reference to. + * @static + * @return {Parse.Object} A Parse.Object reference. + */ + + }, { + key: 'createWithoutData', + value: function (id) { + var obj = new this(); + obj.id = id; + return obj; + } + + /** + * Creates a new instance of a Parse Object from a JSON representation. + * @method fromJSON + * @param {Object} json The JSON map of the Object's data + * @param {boolean} override In single instance mode, all old server data + * is overwritten if this is set to true + * @static + * @return {Parse.Object} A Parse.Object reference + */ + + }, { + key: 'fromJSON', + value: function (json, override) { + if (!json.className) { + throw new Error('Cannot create an object without a className'); + } + var constructor = classMap[json.className]; + var o = constructor ? new constructor() : new ParseObject(json.className); + var otherAttributes = {}; + for (var attr in json) { + if (attr !== 'className' && attr !== '__type') { + otherAttributes[attr] = json[attr]; + } + } + if (override) { + // id needs to be set before clearServerData can work + if (otherAttributes.objectId) { + o.id = otherAttributes.objectId; + } + var preserved = null; + if (typeof o._preserveFieldsOnFetch === 'function') { + preserved = o._preserveFieldsOnFetch(); + } + o._clearServerData(); + if (preserved) { + o._finishFetch(preserved); + } + } + o._finishFetch(otherAttributes); + if (json.objectId) { + o._setExisted(true); + } + return o; + } + + /** + * Registers a subclass of Parse.Object with a specific class name. + * When objects of that class are retrieved from a query, they will be + * instantiated with this subclass. + * This is only necessary when using ES6 subclassing. + * @method registerSubclass + * @param {String} className The class name of the subclass + * @param {Class} constructor The subclass + */ + + }, { + key: 'registerSubclass', + value: function (className, constructor) { + if (typeof className !== 'string') { + throw new TypeError('The first argument must be a valid class name.'); + } + if (typeof constructor === 'undefined') { + throw new TypeError('You must supply a subclass constructor.'); + } + if (typeof constructor !== 'function') { + throw new TypeError('You must register the subclass constructor. ' + 'Did you attempt to register an instance of the subclass?'); + } + classMap[className] = constructor; + if (!constructor.className) { + constructor.className = className; + } + } + + /** + * Creates a new subclass of Parse.Object for the given Parse class name. + * + *
Every extension of a Parse class will inherit from the most recent + * previous extension of that class. When a Parse.Object is automatically + * created by parsing JSON, it will use the most recent extension of that + * class.
+ * + *You should call either:
+ * var MyClass = Parse.Object.extend("MyClass", { + * Instance methods, + * initialize: function(attrs, options) { + * this.someInstanceProperty = [], + * Other instance properties + * } + * }, { + * Class properties + * });+ * or, for Backbone compatibility:
+ * var MyClass = Parse.Object.extend({ + * className: "MyClass", + * Instance methods, + * initialize: function(attrs, options) { + * this.someInstanceProperty = [], + * Other instance properties + * } + * }, { + * Class properties + * });+ * + * @method extend + * @param {String} className The name of the Parse class backing this model. + * @param {Object} protoProps Instance properties to add to instances of the + * class returned from this method. + * @param {Object} classProps Class properties to add the class returned from + * this method. + * @return {Class} A new subclass of Parse.Object. + */ + + }, { + key: 'extend', + value: function (className, protoProps, classProps) { + if (typeof className !== 'string') { + if (className && typeof className.className === 'string') { + return ParseObject.extend(className.className, className, protoProps); + } else { + throw new Error('Parse.Object.extend\'s first argument should be the className.'); + } + } + var adjustedClassName = className; + + if (adjustedClassName === 'User' && _CoreManager2.default.get('PERFORM_USER_REWRITE')) { + adjustedClassName = '_User'; + } + + var parentProto = ParseObject.prototype; + if (this.hasOwnProperty('__super__') && this.__super__) { + parentProto = this.prototype; + } else if (classMap[adjustedClassName]) { + parentProto = classMap[adjustedClassName].prototype; + } + var ParseObjectSubclass = function (attributes, options) { + this.className = adjustedClassName; + this._objCount = objectCount++; + // Enable legacy initializers + if (typeof this.initialize === 'function') { + this.initialize.apply(this, arguments); + } + + if (attributes && (typeof attributes === 'undefined' ? 'undefined' : (0, _typeof3.default)(attributes)) === 'object') { + if (!this.set(attributes || {}, options)) { + throw new Error('Can\'t create an invalid Parse Object'); + } + } + }; + ParseObjectSubclass.className = adjustedClassName; + ParseObjectSubclass.__super__ = parentProto; + + ParseObjectSubclass.prototype = (0, _create2.default)(parentProto, { + constructor: { + value: ParseObjectSubclass, + enumerable: false, + writable: true, + configurable: true + } + }); + + if (protoProps) { + for (var prop in protoProps) { + if (prop !== 'className') { + (0, _defineProperty2.default)(ParseObjectSubclass.prototype, prop, { + value: protoProps[prop], + enumerable: false, + writable: true, + configurable: true + }); + } + } + } + + if (classProps) { + for (var prop in classProps) { + if (prop !== 'className') { + (0, _defineProperty2.default)(ParseObjectSubclass, prop, { + value: classProps[prop], + enumerable: false, + writable: true, + configurable: true + }); + } + } + } + + ParseObjectSubclass.extend = function (name, protoProps, classProps) { + if (typeof name === 'string') { + return ParseObject.extend.call(ParseObjectSubclass, name, protoProps, classProps); + } + return ParseObject.extend.call(ParseObjectSubclass, adjustedClassName, name, protoProps); + }; + ParseObjectSubclass.createWithoutData = ParseObject.createWithoutData; + + classMap[adjustedClassName] = ParseObjectSubclass; + return ParseObjectSubclass; + } + + /** + * Enable single instance objects, where any local objects with the same Id + * share the same attributes, and stay synchronized with each other. + * This is disabled by default in server environments, since it can lead to + * security issues. + * @method enableSingleInstance + */ + + }, { + key: 'enableSingleInstance', + value: function () { + singleInstance = true; + _CoreManager2.default.setObjectStateController(SingleInstanceStateController); + } + + /** + * Disable single instance objects, where any local objects with the same Id + * share the same attributes, and stay synchronized with each other. + * When disabled, you can have two instances of the same object in memory + * without them sharing attributes. + * @method disableSingleInstance + */ + + }, { + key: 'disableSingleInstance', + value: function () { + singleInstance = false; + _CoreManager2.default.setObjectStateController(UniqueInstanceStateController); + } + }]); + return ParseObject; +}(); + +exports.default = ParseObject; + +var DefaultController = { + fetch: function (target, forceFetch, options) { + if (Array.isArray(target)) { + if (target.length < 1) { + return _ParsePromise2.default.as([]); + } + var objs = []; + var ids = []; + var className = null; + var results = []; + var error = null; + target.forEach(function (el, i) { + if (error) { + return; + } + if (!className) { + className = el.className; + } + if (className !== el.className) { + error = new _ParseError2.default(_ParseError2.default.INVALID_CLASS_NAME, 'All objects should be of the same class'); + } + if (!el.id) { + error = new _ParseError2.default(_ParseError2.default.MISSING_OBJECT_ID, 'All objects must have an ID'); + } + if (forceFetch || (0, _keys2.default)(el._getServerData()).length === 0) { + ids.push(el.id); + objs.push(el); + } + results.push(el); + }); + if (error) { + return _ParsePromise2.default.error(error); + } + var query = new _ParseQuery2.default(className); + query.containedIn('objectId', ids); + query._limit = ids.length; + return query.find(options).then(function (objects) { + var idMap = {}; + objects.forEach(function (o) { + idMap[o.id] = o; + }); + for (var i = 0; i < objs.length; i++) { + var obj = objs[i]; + if (!obj || !obj.id || !idMap[obj.id]) { + if (forceFetch) { + return _ParsePromise2.default.error(new _ParseError2.default(_ParseError2.default.OBJECT_NOT_FOUND, 'All objects must exist on the server.')); + } + } + } + if (!singleInstance) { + // If single instance objects are disabled, we need to replace the + for (var i = 0; i < results.length; i++) { + var obj = results[i]; + if (obj && obj.id && idMap[obj.id]) { + var id = obj.id; + obj._finishFetch(idMap[id].toJSON()); + results[i] = idMap[id]; + } + } + } + return _ParsePromise2.default.as(results); + }); + } else { + var RESTController = _CoreManager2.default.getRESTController(); + return RESTController.request('GET', 'classes/' + target.className + '/' + target._getId(), {}, options).then(function (response, status, xhr) { + if (target instanceof ParseObject) { + target._clearPendingOps(); + target._clearServerData(); + target._finishFetch(response); + } + return target; + }); + } + }, + destroy: function (target, options) { + var RESTController = _CoreManager2.default.getRESTController(); + if (Array.isArray(target)) { + if (target.length < 1) { + return _ParsePromise2.default.as([]); + } + var batches = [[]]; + target.forEach(function (obj) { + if (!obj.id) { + return; + } + batches[batches.length - 1].push(obj); + if (batches[batches.length - 1].length >= 20) { + batches.push([]); + } + }); + if (batches[batches.length - 1].length === 0) { + // If the last batch is empty, remove it + batches.pop(); + } + var deleteCompleted = _ParsePromise2.default.as(); + var errors = []; + batches.forEach(function (batch) { + deleteCompleted = deleteCompleted.then(function () { + return RESTController.request('POST', 'batch', { + requests: batch.map(function (obj) { + return { + method: 'DELETE', + path: getServerUrlPath() + 'classes/' + obj.className + '/' + obj._getId(), + body: {} + }; + }) + }, options).then(function (results) { + for (var i = 0; i < results.length; i++) { + if (results[i] && results[i].hasOwnProperty('error')) { + var err = new _ParseError2.default(results[i].error.code, results[i].error.error); + err.object = batch[i]; + errors.push(err); + } + } + }); + }); + }); + return deleteCompleted.then(function () { + if (errors.length) { + var aggregate = new _ParseError2.default(_ParseError2.default.AGGREGATE_ERROR); + aggregate.errors = errors; + return _ParsePromise2.default.error(aggregate); + } + return _ParsePromise2.default.as(target); + }); + } else if (target instanceof ParseObject) { + return RESTController.request('DELETE', 'classes/' + target.className + '/' + target._getId(), {}, options).then(function () { + return _ParsePromise2.default.as(target); + }); + } + return _ParsePromise2.default.as(target); + }, + save: function (target, options) { + var RESTController = _CoreManager2.default.getRESTController(); + var stateController = _CoreManager2.default.getObjectStateController(); + if (Array.isArray(target)) { + if (target.length < 1) { + return _ParsePromise2.default.as([]); + } + + var unsaved = target.concat(); + for (var i = 0; i < target.length; i++) { + if (target[i] instanceof ParseObject) { + unsaved = unsaved.concat((0, _unsavedChildren2.default)(target[i], true)); + } + } + unsaved = (0, _unique2.default)(unsaved); + + var filesSaved = _ParsePromise2.default.as(); + var pending = []; + unsaved.forEach(function (el) { + if (el instanceof _ParseFile2.default) { + filesSaved = filesSaved.then(function () { + return el.save(); + }); + } else if (el instanceof ParseObject) { + pending.push(el); + } + }); + + return filesSaved.then(function () { + var objectError = null; + return _ParsePromise2.default._continueWhile(function () { + return pending.length > 0; + }, function () { + var batch = []; + var nextPending = []; + pending.forEach(function (el) { + if (batch.length < 20 && (0, _canBeSerialized2.default)(el)) { + batch.push(el); + } else { + nextPending.push(el); + } + }); + pending = nextPending; + if (batch.length < 1) { + return _ParsePromise2.default.error(new _ParseError2.default(_ParseError2.default.OTHER_CAUSE, 'Tried to save a batch with a cycle.')); + } + + // Queue up tasks for each object in the batch. + // When every task is ready, the API request will execute + var batchReturned = new _ParsePromise2.default(); + var batchReady = []; + var batchTasks = []; + batch.forEach(function (obj, index) { + var ready = new _ParsePromise2.default(); + batchReady.push(ready); + + stateController.pushPendingState(obj._getStateIdentifier()); + batchTasks.push(stateController.enqueueTask(obj._getStateIdentifier(), function () { + ready.resolve(); + return batchReturned.then(function (responses, status) { + if (responses[index].hasOwnProperty('success')) { + obj._handleSaveResponse(responses[index].success, status); + } else { + if (!objectError && responses[index].hasOwnProperty('error')) { + var serverError = responses[index].error; + objectError = new _ParseError2.default(serverError.code, serverError.error); + // Cancel the rest of the save + pending = []; + } + obj._handleSaveError(); + } + }); + })); + }); + + _ParsePromise2.default.when(batchReady).then(function () { + // Kick off the batch request + return RESTController.request('POST', 'batch', { + requests: batch.map(function (obj) { + var params = obj._getSaveParams(); + params.path = getServerUrlPath() + params.path; + return params; + }) + }, options); + }).then(function (response, status, xhr) { + batchReturned.resolve(response, status); + }); + + return _ParsePromise2.default.when(batchTasks); + }).then(function () { + if (objectError) { + return _ParsePromise2.default.error(objectError); + } + return _ParsePromise2.default.as(target); + }); + }); + } else if (target instanceof ParseObject) { + // copying target lets Flow guarantee the pointer isn't modified elsewhere + var targetCopy = target; + var task = function () { + var params = targetCopy._getSaveParams(); + return RESTController.request(params.method, params.path, params.body, options).then(function (response, status) { + targetCopy._handleSaveResponse(response, status); + }, function (error) { + targetCopy._handleSaveError(); + return _ParsePromise2.default.error(error); + }); + }; + + stateController.pushPendingState(target._getStateIdentifier()); + return stateController.enqueueTask(target._getStateIdentifier(), task).then(function () { + return target; + }, function (error) { + return _ParsePromise2.default.error(error); + }); + } + return _ParsePromise2.default.as(); + } +}; + +_CoreManager2.default.setObjectController(DefaultController); \ No newline at end of file diff --git a/lib/browser/ParseOp.js b/lib/browser/ParseOp.js new file mode 100644 index 000000000..1eba19303 --- /dev/null +++ b/lib/browser/ParseOp.js @@ -0,0 +1,579 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.RelationOp = exports.RemoveOp = exports.AddUniqueOp = exports.AddOp = exports.IncrementOp = exports.UnsetOp = exports.SetOp = exports.Op = undefined; + +var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); + +var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); + +var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); + +var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); + +var _inherits2 = require('babel-runtime/helpers/inherits'); + +var _inherits3 = _interopRequireDefault(_inherits2); + +var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); + +var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); + +var _createClass2 = require('babel-runtime/helpers/createClass'); + +var _createClass3 = _interopRequireDefault(_createClass2); + +exports.opFromJSON = opFromJSON; + +var _arrayContainsObject = require('./arrayContainsObject'); + +var _arrayContainsObject2 = _interopRequireDefault(_arrayContainsObject); + +var _decode = require('./decode'); + +var _decode2 = _interopRequireDefault(_decode); + +var _encode = require('./encode'); + +var _encode2 = _interopRequireDefault(_encode); + +var _ParseObject = require('./ParseObject'); + +var _ParseObject2 = _interopRequireDefault(_ParseObject); + +var _ParseRelation = require('./ParseRelation'); + +var _ParseRelation2 = _interopRequireDefault(_ParseRelation); + +var _unique = require('./unique'); + +var _unique2 = _interopRequireDefault(_unique); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { default: obj }; +} + +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + */ + +function opFromJSON(json) { + if (!json || !json.__op) { + return null; + } + switch (json.__op) { + case 'Delete': + return new UnsetOp(); + case 'Increment': + return new IncrementOp(json.amount); + case 'Add': + return new AddOp((0, _decode2.default)(json.objects)); + case 'AddUnique': + return new AddUniqueOp((0, _decode2.default)(json.objects)); + case 'Remove': + return new RemoveOp((0, _decode2.default)(json.objects)); + case 'AddRelation': + var toAdd = (0, _decode2.default)(json.objects); + if (!Array.isArray(toAdd)) { + return new RelationOp([], []); + } + return new RelationOp(toAdd, []); + case 'RemoveRelation': + var toRemove = (0, _decode2.default)(json.objects); + if (!Array.isArray(toRemove)) { + return new RelationOp([], []); + } + return new RelationOp([], toRemove); + case 'Batch': + var toAdd = []; + var toRemove = []; + for (var i = 0; i < json.ops.length; i++) { + if (json.ops[i].__op === 'AddRelation') { + toAdd = toAdd.concat((0, _decode2.default)(json.ops[i].objects)); + } else if (json.ops[i].__op === 'RemoveRelation') { + toRemove = toRemove.concat((0, _decode2.default)(json.ops[i].objects)); + } + } + return new RelationOp(toAdd, toRemove); + } + return null; +} + +var Op = exports.Op = function () { + function Op() { + (0, _classCallCheck3.default)(this, Op); + } + + (0, _createClass3.default)(Op, [{ + key: 'applyTo', + + // Empty parent class + value: function (value) {} + }, { + key: 'mergeWith', + value: function (previous) {} + }, { + key: 'toJSON', + value: function () {} + }]); + return Op; +}(); + +var SetOp = exports.SetOp = function (_Op) { + (0, _inherits3.default)(SetOp, _Op); + + function SetOp(value) { + (0, _classCallCheck3.default)(this, SetOp); + + var _this = (0, _possibleConstructorReturn3.default)(this, (SetOp.__proto__ || (0, _getPrototypeOf2.default)(SetOp)).call(this)); + + _this._value = value; + return _this; + } + + (0, _createClass3.default)(SetOp, [{ + key: 'applyTo', + value: function (value) { + return this._value; + } + }, { + key: 'mergeWith', + value: function (previous) { + return new SetOp(this._value); + } + }, { + key: 'toJSON', + value: function () { + return (0, _encode2.default)(this._value, false, true); + } + }]); + return SetOp; +}(Op); + +var UnsetOp = exports.UnsetOp = function (_Op2) { + (0, _inherits3.default)(UnsetOp, _Op2); + + function UnsetOp() { + (0, _classCallCheck3.default)(this, UnsetOp); + return (0, _possibleConstructorReturn3.default)(this, (UnsetOp.__proto__ || (0, _getPrototypeOf2.default)(UnsetOp)).apply(this, arguments)); + } + + (0, _createClass3.default)(UnsetOp, [{ + key: 'applyTo', + value: function (value) { + return undefined; + } + }, { + key: 'mergeWith', + value: function (previous) { + return new UnsetOp(); + } + }, { + key: 'toJSON', + value: function () { + return { __op: 'Delete' }; + } + }]); + return UnsetOp; +}(Op); + +var IncrementOp = exports.IncrementOp = function (_Op3) { + (0, _inherits3.default)(IncrementOp, _Op3); + + function IncrementOp(amount) { + (0, _classCallCheck3.default)(this, IncrementOp); + + var _this3 = (0, _possibleConstructorReturn3.default)(this, (IncrementOp.__proto__ || (0, _getPrototypeOf2.default)(IncrementOp)).call(this)); + + if (typeof amount !== 'number') { + throw new TypeError('Increment Op must be initialized with a numeric amount.'); + } + _this3._amount = amount; + return _this3; + } + + (0, _createClass3.default)(IncrementOp, [{ + key: 'applyTo', + value: function (value) { + if (typeof value === 'undefined') { + return this._amount; + } + if (typeof value !== 'number') { + throw new TypeError('Cannot increment a non-numeric value.'); + } + return this._amount + value; + } + }, { + key: 'mergeWith', + value: function (previous) { + if (!previous) { + return this; + } + if (previous instanceof SetOp) { + return new SetOp(this.applyTo(previous._value)); + } + if (previous instanceof UnsetOp) { + return new SetOp(this._amount); + } + if (previous instanceof IncrementOp) { + return new IncrementOp(this.applyTo(previous._amount)); + } + throw new Error('Cannot merge Increment Op with the previous Op'); + } + }, { + key: 'toJSON', + value: function () { + return { __op: 'Increment', amount: this._amount }; + } + }]); + return IncrementOp; +}(Op); + +var AddOp = exports.AddOp = function (_Op4) { + (0, _inherits3.default)(AddOp, _Op4); + + function AddOp(value) { + (0, _classCallCheck3.default)(this, AddOp); + + var _this4 = (0, _possibleConstructorReturn3.default)(this, (AddOp.__proto__ || (0, _getPrototypeOf2.default)(AddOp)).call(this)); + + _this4._value = Array.isArray(value) ? value : [value]; + return _this4; + } + + (0, _createClass3.default)(AddOp, [{ + key: 'applyTo', + value: function (value) { + if (value == null) { + return this._value; + } + if (Array.isArray(value)) { + return value.concat(this._value); + } + throw new Error('Cannot add elements to a non-array value'); + } + }, { + key: 'mergeWith', + value: function (previous) { + if (!previous) { + return this; + } + if (previous instanceof SetOp) { + return new SetOp(this.applyTo(previous._value)); + } + if (previous instanceof UnsetOp) { + return new SetOp(this._value); + } + if (previous instanceof AddOp) { + return new AddOp(this.applyTo(previous._value)); + } + throw new Error('Cannot merge Add Op with the previous Op'); + } + }, { + key: 'toJSON', + value: function () { + return { __op: 'Add', objects: (0, _encode2.default)(this._value, false, true) }; + } + }]); + return AddOp; +}(Op); + +var AddUniqueOp = exports.AddUniqueOp = function (_Op5) { + (0, _inherits3.default)(AddUniqueOp, _Op5); + + function AddUniqueOp(value) { + (0, _classCallCheck3.default)(this, AddUniqueOp); + + var _this5 = (0, _possibleConstructorReturn3.default)(this, (AddUniqueOp.__proto__ || (0, _getPrototypeOf2.default)(AddUniqueOp)).call(this)); + + _this5._value = (0, _unique2.default)(Array.isArray(value) ? value : [value]); + return _this5; + } + + (0, _createClass3.default)(AddUniqueOp, [{ + key: 'applyTo', + value: function (value) { + if (value == null) { + return this._value || []; + } + if (Array.isArray(value)) { + // copying value lets Flow guarantee the pointer isn't modified elsewhere + var valueCopy = value; + var toAdd = []; + this._value.forEach(function (v) { + if (v instanceof _ParseObject2.default) { + if (!(0, _arrayContainsObject2.default)(valueCopy, v)) { + toAdd.push(v); + } + } else { + if (valueCopy.indexOf(v) < 0) { + toAdd.push(v); + } + } + }); + return value.concat(toAdd); + } + throw new Error('Cannot add elements to a non-array value'); + } + }, { + key: 'mergeWith', + value: function (previous) { + if (!previous) { + return this; + } + if (previous instanceof SetOp) { + return new SetOp(this.applyTo(previous._value)); + } + if (previous instanceof UnsetOp) { + return new SetOp(this._value); + } + if (previous instanceof AddUniqueOp) { + return new AddUniqueOp(this.applyTo(previous._value)); + } + throw new Error('Cannot merge AddUnique Op with the previous Op'); + } + }, { + key: 'toJSON', + value: function () { + return { __op: 'AddUnique', objects: (0, _encode2.default)(this._value, false, true) }; + } + }]); + return AddUniqueOp; +}(Op); + +var RemoveOp = exports.RemoveOp = function (_Op6) { + (0, _inherits3.default)(RemoveOp, _Op6); + + function RemoveOp(value) { + (0, _classCallCheck3.default)(this, RemoveOp); + + var _this6 = (0, _possibleConstructorReturn3.default)(this, (RemoveOp.__proto__ || (0, _getPrototypeOf2.default)(RemoveOp)).call(this)); + + _this6._value = (0, _unique2.default)(Array.isArray(value) ? value : [value]); + return _this6; + } + + (0, _createClass3.default)(RemoveOp, [{ + key: 'applyTo', + value: function (value) { + if (value == null) { + return []; + } + if (Array.isArray(value)) { + var i = value.indexOf(this._value); + var removed = value.concat([]); + for (var i = 0; i < this._value.length; i++) { + var index = removed.indexOf(this._value[i]); + while (index > -1) { + removed.splice(index, 1); + index = removed.indexOf(this._value[i]); + } + if (this._value[i] instanceof _ParseObject2.default && this._value[i].id) { + for (var j = 0; j < removed.length; j++) { + if (removed[j] instanceof _ParseObject2.default && this._value[i].id === removed[j].id) { + removed.splice(j, 1); + j--; + } + } + } + } + return removed; + } + throw new Error('Cannot remove elements from a non-array value'); + } + }, { + key: 'mergeWith', + value: function (previous) { + if (!previous) { + return this; + } + if (previous instanceof SetOp) { + return new SetOp(this.applyTo(previous._value)); + } + if (previous instanceof UnsetOp) { + return new UnsetOp(); + } + if (previous instanceof RemoveOp) { + var uniques = previous._value.concat([]); + for (var i = 0; i < this._value.length; i++) { + if (this._value[i] instanceof _ParseObject2.default) { + if (!(0, _arrayContainsObject2.default)(uniques, this._value[i])) { + uniques.push(this._value[i]); + } + } else { + if (uniques.indexOf(this._value[i]) < 0) { + uniques.push(this._value[i]); + } + } + } + return new RemoveOp(uniques); + } + throw new Error('Cannot merge Remove Op with the previous Op'); + } + }, { + key: 'toJSON', + value: function () { + return { __op: 'Remove', objects: (0, _encode2.default)(this._value, false, true) }; + } + }]); + return RemoveOp; +}(Op); + +var RelationOp = exports.RelationOp = function (_Op7) { + (0, _inherits3.default)(RelationOp, _Op7); + + function RelationOp(adds, removes) { + (0, _classCallCheck3.default)(this, RelationOp); + + var _this7 = (0, _possibleConstructorReturn3.default)(this, (RelationOp.__proto__ || (0, _getPrototypeOf2.default)(RelationOp)).call(this)); + + _this7._targetClassName = null; + + if (Array.isArray(adds)) { + _this7.relationsToAdd = (0, _unique2.default)(adds.map(_this7._extractId, _this7)); + } + + if (Array.isArray(removes)) { + _this7.relationsToRemove = (0, _unique2.default)(removes.map(_this7._extractId, _this7)); + } + return _this7; + } + + (0, _createClass3.default)(RelationOp, [{ + key: '_extractId', + value: function (obj) { + if (typeof obj === 'string') { + return obj; + } + if (!obj.id) { + throw new Error('You cannot add or remove an unsaved Parse Object from a relation'); + } + if (!this._targetClassName) { + this._targetClassName = obj.className; + } + if (this._targetClassName !== obj.className) { + throw new Error('Tried to create a Relation with 2 different object types: ' + this._targetClassName + ' and ' + obj.className + '.'); + } + return obj.id; + } + }, { + key: 'applyTo', + value: function (value, object, key) { + if (!value) { + if (!object || !key) { + throw new Error('Cannot apply a RelationOp without either a previous value, or an object and a key'); + } + var parent = new _ParseObject2.default(object.className); + if (object.id && object.id.indexOf('local') === 0) { + parent._localId = object.id; + } else if (object.id) { + parent.id = object.id; + } + var relation = new _ParseRelation2.default(parent, key); + relation.targetClassName = this._targetClassName; + return relation; + } + if (value instanceof _ParseRelation2.default) { + if (this._targetClassName) { + if (value.targetClassName) { + if (this._targetClassName !== value.targetClassName) { + throw new Error('Related object must be a ' + value.targetClassName + ', but a ' + this._targetClassName + ' was passed in.'); + } + } else { + value.targetClassName = this._targetClassName; + } + } + return value; + } else { + throw new Error('Relation cannot be applied to a non-relation field'); + } + } + }, { + key: 'mergeWith', + value: function (previous) { + if (!previous) { + return this; + } else if (previous instanceof UnsetOp) { + throw new Error('You cannot modify a relation after deleting it.'); + } else if (previous instanceof RelationOp) { + if (previous._targetClassName && previous._targetClassName !== this._targetClassName) { + throw new Error('Related object must be of class ' + previous._targetClassName + ', but ' + (this._targetClassName || 'null') + ' was passed in.'); + } + var newAdd = previous.relationsToAdd.concat([]); + this.relationsToRemove.forEach(function (r) { + var index = newAdd.indexOf(r); + if (index > -1) { + newAdd.splice(index, 1); + } + }); + this.relationsToAdd.forEach(function (r) { + var index = newAdd.indexOf(r); + if (index < 0) { + newAdd.push(r); + } + }); + + var newRemove = previous.relationsToRemove.concat([]); + this.relationsToAdd.forEach(function (r) { + var index = newRemove.indexOf(r); + if (index > -1) { + newRemove.splice(index, 1); + } + }); + this.relationsToRemove.forEach(function (r) { + var index = newRemove.indexOf(r); + if (index < 0) { + newRemove.push(r); + } + }); + + var newRelation = new RelationOp(newAdd, newRemove); + newRelation._targetClassName = this._targetClassName; + return newRelation; + } + throw new Error('Cannot merge Relation Op with the previous Op'); + } + }, { + key: 'toJSON', + value: function () { + var _this8 = this; + + var idToPointer = function (id) { + return { + __type: 'Pointer', + className: _this8._targetClassName, + objectId: id + }; + }; + + var adds = null; + var removes = null; + var pointers = null; + + if (this.relationsToAdd.length > 0) { + pointers = this.relationsToAdd.map(idToPointer); + adds = { __op: 'AddRelation', objects: pointers }; + } + if (this.relationsToRemove.length > 0) { + pointers = this.relationsToRemove.map(idToPointer); + removes = { __op: 'RemoveRelation', objects: pointers }; + } + + if (adds && removes) { + return { __op: 'Batch', ops: [adds, removes] }; + } + + return adds || removes || {}; + } + }]); + return RelationOp; +}(Op); \ No newline at end of file diff --git a/lib/browser/ParsePromise.js b/lib/browser/ParsePromise.js new file mode 100644 index 000000000..a79b4afee --- /dev/null +++ b/lib/browser/ParsePromise.js @@ -0,0 +1,740 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _getIterator2 = require('babel-runtime/core-js/get-iterator'); + +var _getIterator3 = _interopRequireDefault(_getIterator2); + +var _typeof2 = require('babel-runtime/helpers/typeof'); + +var _typeof3 = _interopRequireDefault(_typeof2); + +var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); + +var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); + +var _createClass2 = require('babel-runtime/helpers/createClass'); + +var _createClass3 = _interopRequireDefault(_createClass2); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { default: obj }; +} + +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +var _isPromisesAPlusCompliant = true; + +/** + * A Promise is returned by async methods as a hook to provide callbacks to be + * called when the async task is fulfilled. + * + *
Typical usage would be like:
+ * query.find().then(function(results) { + * results[0].set("foo", "bar"); + * return results[0].saveAsync(); + * }).then(function(result) { + * console.log("Updated " + result.id); + * }); + *+ * + * @class Parse.Promise + * @constructor + */ + +var ParsePromise = function () { + function ParsePromise(executor) { + (0, _classCallCheck3.default)(this, ParsePromise); + + this._resolved = false; + this._rejected = false; + this._resolvedCallbacks = []; + this._rejectedCallbacks = []; + + if (typeof executor === 'function') { + executor(this.resolve.bind(this), this.reject.bind(this)); + } + } + + /** + * Marks this promise as fulfilled, firing any callbacks waiting on it. + * @method resolve + * @param {Object} result the result to pass to the callbacks. + */ + + (0, _createClass3.default)(ParsePromise, [{ + key: 'resolve', + value: function () { + if (this._resolved || this._rejected) { + throw new Error('A promise was resolved even though it had already been ' + (this._resolved ? 'resolved' : 'rejected') + '.'); + } + this._resolved = true; + + for (var _len = arguments.length, results = Array(_len), _key = 0; _key < _len; _key++) { + results[_key] = arguments[_key]; + } + + this._result = results; + for (var i = 0; i < this._resolvedCallbacks.length; i++) { + this._resolvedCallbacks[i].apply(this, results); + } + + this._resolvedCallbacks = []; + this._rejectedCallbacks = []; + } + + /** + * Marks this promise as fulfilled, firing any callbacks waiting on it. + * @method reject + * @param {Object} error the error to pass to the callbacks. + */ + + }, { + key: 'reject', + value: function (error) { + if (this._resolved || this._rejected) { + throw new Error('A promise was rejected even though it had already been ' + (this._resolved ? 'resolved' : 'rejected') + '.'); + } + this._rejected = true; + this._error = error; + for (var i = 0; i < this._rejectedCallbacks.length; i++) { + this._rejectedCallbacks[i](error); + } + this._resolvedCallbacks = []; + this._rejectedCallbacks = []; + } + + /** + * Adds callbacks to be called when this promise is fulfilled. Returns a new + * Promise that will be fulfilled when the callback is complete. It allows + * chaining. If the callback itself returns a Promise, then the one returned + * by "then" will not be fulfilled until that one returned by the callback + * is fulfilled. + * @method then + * @param {Function} resolvedCallback Function that is called when this + * Promise is resolved. Once the callback is complete, then the Promise + * returned by "then" will also be fulfilled. + * @param {Function} rejectedCallback Function that is called when this + * Promise is rejected with an error. Once the callback is complete, then + * the promise returned by "then" with be resolved successfully. If + * rejectedCallback is null, or it returns a rejected Promise, then the + * Promise returned by "then" will be rejected with that error. + * @return {Parse.Promise} A new Promise that will be fulfilled after this + * Promise is fulfilled and either callback has completed. If the callback + * returned a Promise, then this Promise will not be fulfilled until that + * one is. + */ + + }, { + key: 'then', + value: function (resolvedCallback, rejectedCallback) { + var _this = this; + + var promise = new ParsePromise(); + + var wrappedResolvedCallback = function () { + for (var _len2 = arguments.length, results = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { + results[_key2] = arguments[_key2]; + } + + if (typeof resolvedCallback === 'function') { + if (_isPromisesAPlusCompliant) { + try { + results = [resolvedCallback.apply(this, results)]; + } catch (e) { + results = [ParsePromise.error(e)]; + } + } else { + results = [resolvedCallback.apply(this, results)]; + } + } + if (results.length === 1 && ParsePromise.is(results[0])) { + results[0].then(function () { + promise.resolve.apply(promise, arguments); + }, function (error) { + promise.reject(error); + }); + } else { + promise.resolve.apply(promise, results); + } + }; + + var wrappedRejectedCallback = function (error) { + var result = []; + if (typeof rejectedCallback === 'function') { + if (_isPromisesAPlusCompliant) { + try { + result = [rejectedCallback(error)]; + } catch (e) { + result = [ParsePromise.error(e)]; + } + } else { + result = [rejectedCallback(error)]; + } + if (result.length === 1 && ParsePromise.is(result[0])) { + result[0].then(function () { + promise.resolve.apply(promise, arguments); + }, function (error) { + promise.reject(error); + }); + } else { + if (_isPromisesAPlusCompliant) { + promise.resolve.apply(promise, result); + } else { + promise.reject(result[0]); + } + } + } else { + promise.reject(error); + } + }; + + var runLater = function (fn) { + fn.call(); + }; + if (_isPromisesAPlusCompliant) { + if (typeof process !== 'undefined' && typeof process.nextTick === 'function') { + runLater = function (fn) { + process.nextTick(fn); + }; + } else if (typeof setTimeout === 'function') { + runLater = function (fn) { + setTimeout(fn, 0); + }; + } + } + + if (this._resolved) { + runLater(function () { + wrappedResolvedCallback.apply(_this, _this._result); + }); + } else if (this._rejected) { + runLater(function () { + wrappedRejectedCallback(_this._error); + }); + } else { + this._resolvedCallbacks.push(wrappedResolvedCallback); + this._rejectedCallbacks.push(wrappedRejectedCallback); + } + + return promise; + } + + /** + * Add handlers to be called when the promise + * is either resolved or rejected + * @method always + */ + + }, { + key: 'always', + value: function (callback) { + return this.then(callback, callback); + } + + /** + * Add handlers to be called when the Promise object is resolved + * @method done + */ + + }, { + key: 'done', + value: function (callback) { + return this.then(callback); + } + + /** + * Add handlers to be called when the Promise object is rejected + * Alias for catch(). + * @method fail + */ + + }, { + key: 'fail', + value: function (callback) { + return this.then(null, callback); + } + + /** + * Add handlers to be called when the Promise object is rejected + * @method catch + */ + + }, { + key: 'catch', + value: function (callback) { + return this.then(null, callback); + } + + /** + * Run the given callbacks after this promise is fulfilled. + * @method _thenRunCallbacks + * @param optionsOrCallback {} A Backbone-style options callback, or a + * callback function. If this is an options object and contains a "model" + * attributes, that will be passed to error callbacks as the first argument. + * @param model {} If truthy, this will be passed as the first result of + * error callbacks. This is for Backbone-compatability. + * @return {Parse.Promise} A promise that will be resolved after the + * callbacks are run, with the same result as this. + */ + + }, { + key: '_thenRunCallbacks', + value: function (optionsOrCallback, model) { + var options = {}; + if (typeof optionsOrCallback === 'function') { + options.success = function (result) { + optionsOrCallback(result, null); + }; + options.error = function (error) { + optionsOrCallback(null, error); + }; + } else if ((typeof optionsOrCallback === 'undefined' ? 'undefined' : (0, _typeof3.default)(optionsOrCallback)) === 'object') { + if (typeof optionsOrCallback.success === 'function') { + options.success = optionsOrCallback.success; + } + if (typeof optionsOrCallback.error === 'function') { + options.error = optionsOrCallback.error; + } + } + + return this.then(function () { + for (var _len3 = arguments.length, results = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) { + results[_key3] = arguments[_key3]; + } + + if (options.success) { + options.success.apply(this, results); + } + return ParsePromise.as.apply(ParsePromise, arguments); + }, function (error) { + if (options.error) { + if (typeof model !== 'undefined') { + options.error(model, error); + } else { + options.error(error); + } + } + // By explicitly returning a rejected Promise, this will work with + // either jQuery or Promises/A+ semantics. + return ParsePromise.error(error); + }); + } + + /** + * Adds a callback function that should be called regardless of whether + * this promise failed or succeeded. The callback will be given either the + * array of results for its first argument, or the error as its second, + * depending on whether this Promise was rejected or resolved. Returns a + * new Promise, like "then" would. + * @method _continueWith + * @param {Function} continuation the callback. + */ + + }, { + key: '_continueWith', + value: function (continuation) { + return this.then(function () { + for (var _len4 = arguments.length, args = Array(_len4), _key4 = 0; _key4 < _len4; _key4++) { + args[_key4] = arguments[_key4]; + } + + return continuation(args, null); + }, function (error) { + return continuation(null, error); + }); + } + + /** + * Returns true iff the given object fulfils the Promise interface. + * @method is + * @param {Object} promise The object to test + * @static + * @return {Boolean} + */ + + }], [{ + key: 'is', + value: function (promise) { + return promise != null && typeof promise.then === 'function'; + } + + /** + * Returns a new promise that is resolved with a given value. + * @method as + * @param value The value to resolve the promise with + * @static + * @return {Parse.Promise} the new promise. + */ + + }, { + key: 'as', + value: function () { + var promise = new ParsePromise(); + + for (var _len5 = arguments.length, values = Array(_len5), _key5 = 0; _key5 < _len5; _key5++) { + values[_key5] = arguments[_key5]; + } + + promise.resolve.apply(promise, values); + return promise; + } + + /** + * Returns a new promise that is resolved with a given value. + * If that value is a thenable Promise (has a .then() prototype + * method), the new promise will be chained to the end of the + * value. + * @method resolve + * @param value The value to resolve the promise with + * @static + * @return {Parse.Promise} the new promise. + */ + + }, { + key: 'resolve', + value: function (value) { + return new ParsePromise(function (resolve, reject) { + if (ParsePromise.is(value)) { + value.then(resolve, reject); + } else { + resolve(value); + } + }); + } + + /** + * Returns a new promise that is rejected with a given error. + * @method error + * @param error The error to reject the promise with + * @static + * @return {Parse.Promise} the new promise. + */ + + }, { + key: 'error', + value: function () { + var promise = new ParsePromise(); + + for (var _len6 = arguments.length, errors = Array(_len6), _key6 = 0; _key6 < _len6; _key6++) { + errors[_key6] = arguments[_key6]; + } + + promise.reject.apply(promise, errors); + return promise; + } + + /** + * Returns a new promise that is rejected with a given error. + * This is an alias for Parse.Promise.error, for compliance with + * the ES6 implementation. + * @method reject + * @param error The error to reject the promise with + * @static + * @return {Parse.Promise} the new promise. + */ + + }, { + key: 'reject', + value: function () { + for (var _len7 = arguments.length, errors = Array(_len7), _key7 = 0; _key7 < _len7; _key7++) { + errors[_key7] = arguments[_key7]; + } + + return ParsePromise.error.apply(null, errors); + } + + /** + * Returns a new promise that is fulfilled when all of the input promises + * are resolved. If any promise in the list fails, then the returned promise + * will be rejected with an array containing the error from each promise. + * If they all succeed, then the returned promise will succeed, with the + * results being the results of all the input + * promises. For example:
+ * var p1 = Parse.Promise.as(1); + * var p2 = Parse.Promise.as(2); + * var p3 = Parse.Promise.as(3); + * + * Parse.Promise.when(p1, p2, p3).then(function(r1, r2, r3) { + * console.log(r1); // prints 1 + * console.log(r2); // prints 2 + * console.log(r3); // prints 3 + * });+ * + * The input promises can also be specified as an array:
+ * var promises = [p1, p2, p3]; + * Parse.Promise.when(promises).then(function(results) { + * console.log(results); // prints [1,2,3] + * }); + *+ * @method when + * @param {Array} promises a list of promises to wait for. + * @static + * @return {Parse.Promise} the new promise. + */ + + }, { + key: 'when', + value: function (promises) { + var objects; + var arrayArgument = Array.isArray(promises); + if (arrayArgument) { + objects = promises; + } else { + objects = arguments; + } + + var total = objects.length; + var hadError = false; + var results = []; + var returnValue = arrayArgument ? [results] : results; + var errors = []; + results.length = objects.length; + errors.length = objects.length; + + if (total === 0) { + return ParsePromise.as.apply(this, returnValue); + } + + var promise = new ParsePromise(); + + var resolveOne = function () { + total--; + if (total <= 0) { + if (hadError) { + promise.reject(errors); + } else { + promise.resolve.apply(promise, returnValue); + } + } + }; + + var chain = function (object, index) { + if (ParsePromise.is(object)) { + object.then(function (result) { + results[index] = result; + resolveOne(); + }, function (error) { + errors[index] = error; + hadError = true; + resolveOne(); + }); + } else { + results[i] = object; + resolveOne(); + } + }; + for (var i = 0; i < objects.length; i++) { + chain(objects[i], i); + } + + return promise; + } + + /** + * Returns a new promise that is fulfilled when all of the promises in the + * iterable argument are resolved. If any promise in the list fails, then + * the returned promise will be immediately rejected with the reason that + * single promise rejected. If they all succeed, then the returned promise + * will succeed, with the results being the results of all the input + * promises. If the iterable provided is empty, the returned promise will + * be immediately resolved. + * + * For example:
+ * var p1 = Parse.Promise.as(1); + * var p2 = Parse.Promise.as(2); + * var p3 = Parse.Promise.as(3); + * + * Parse.Promise.all([p1, p2, p3]).then(function([r1, r2, r3]) { + * console.log(r1); // prints 1 + * console.log(r2); // prints 2 + * console.log(r3); // prints 3 + * });+ * + * @method all + * @param {Iterable} promises an iterable of promises to wait for. + * @static + * @return {Parse.Promise} the new promise. + */ + + }, { + key: 'all', + value: function (promises) { + var total = 0; + var objects = []; + + var _iteratorNormalCompletion = true; + var _didIteratorError = false; + var _iteratorError = undefined; + + try { + for (var _iterator = (0, _getIterator3.default)(promises), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { + var p = _step.value; + + objects[total++] = p; + } + } catch (err) { + _didIteratorError = true; + _iteratorError = err; + } finally { + try { + if (!_iteratorNormalCompletion && _iterator.return) { + _iterator.return(); + } + } finally { + if (_didIteratorError) { + throw _iteratorError; + } + } + } + + if (total === 0) { + return ParsePromise.as([]); + } + + var hadError = false; + var promise = new ParsePromise(); + var resolved = 0; + var results = []; + objects.forEach(function (object, i) { + if (ParsePromise.is(object)) { + object.then(function (result) { + if (hadError) { + return false; + } + results[i] = result; + resolved++; + if (resolved >= total) { + promise.resolve(results); + } + }, function (error) { + // Reject immediately + promise.reject(error); + hadError = true; + }); + } else { + results[i] = object; + resolved++; + if (!hadError && resolved >= total) { + promise.resolve(results); + } + } + }); + + return promise; + } + + /** + * Returns a new promise that is immediately fulfilled when any of the + * promises in the iterable argument are resolved or rejected. If the + * first promise to complete is resolved, the returned promise will be + * resolved with the same value. Likewise, if the first promise to + * complete is rejected, the returned promise will be rejected with the + * same reason. + * + * @method race + * @param {Iterable} promises an iterable of promises to wait for. + * @static + * @return {Parse.Promise} the new promise. + */ + + }, { + key: 'race', + value: function (promises) { + var completed = false; + var promise = new ParsePromise(); + var _iteratorNormalCompletion2 = true; + var _didIteratorError2 = false; + var _iteratorError2 = undefined; + + try { + for (var _iterator2 = (0, _getIterator3.default)(promises), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { + var p = _step2.value; + + if (ParsePromise.is(p)) { + p.then(function (result) { + if (completed) { + return; + } + completed = true; + promise.resolve(result); + }, function (error) { + if (completed) { + return; + } + completed = true; + promise.reject(error); + }); + } else if (!completed) { + completed = true; + promise.resolve(p); + } + } + } catch (err) { + _didIteratorError2 = true; + _iteratorError2 = err; + } finally { + try { + if (!_iteratorNormalCompletion2 && _iterator2.return) { + _iterator2.return(); + } + } finally { + if (_didIteratorError2) { + throw _iteratorError2; + } + } + } + + return promise; + } + + /** + * Runs the given asyncFunction repeatedly, as long as the predicate + * function returns a truthy value. Stops repeating if asyncFunction returns + * a rejected promise. + * @method _continueWhile + * @param {Function} predicate should return false when ready to stop. + * @param {Function} asyncFunction should return a Promise. + * @static + */ + + }, { + key: '_continueWhile', + value: function (predicate, asyncFunction) { + if (predicate()) { + return asyncFunction().then(function () { + return ParsePromise._continueWhile(predicate, asyncFunction); + }); + } + return ParsePromise.as(); + } + }, { + key: 'isPromisesAPlusCompliant', + value: function () { + return _isPromisesAPlusCompliant; + } + }, { + key: 'enableAPlusCompliant', + value: function () { + _isPromisesAPlusCompliant = true; + } + }, { + key: 'disableAPlusCompliant', + value: function () { + _isPromisesAPlusCompliant = false; + } + }]); + return ParsePromise; +}(); + +exports.default = ParsePromise; \ No newline at end of file diff --git a/lib/browser/ParseQuery.js b/lib/browser/ParseQuery.js new file mode 100644 index 000000000..664f5a737 --- /dev/null +++ b/lib/browser/ParseQuery.js @@ -0,0 +1,1340 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _typeof2 = require('babel-runtime/helpers/typeof'); + +var _typeof3 = _interopRequireDefault(_typeof2); + +var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); + +var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); + +var _createClass2 = require('babel-runtime/helpers/createClass'); + +var _createClass3 = _interopRequireDefault(_createClass2); + +var _keys = require('babel-runtime/core-js/object/keys'); + +var _keys2 = _interopRequireDefault(_keys); + +var _CoreManager = require('./CoreManager'); + +var _CoreManager2 = _interopRequireDefault(_CoreManager); + +var _encode = require('./encode'); + +var _encode2 = _interopRequireDefault(_encode); + +var _ParseError = require('./ParseError'); + +var _ParseError2 = _interopRequireDefault(_ParseError); + +var _ParseGeoPoint = require('./ParseGeoPoint'); + +var _ParseGeoPoint2 = _interopRequireDefault(_ParseGeoPoint); + +var _ParseObject = require('./ParseObject'); + +var _ParseObject2 = _interopRequireDefault(_ParseObject); + +var _ParsePromise = require('./ParsePromise'); + +var _ParsePromise2 = _interopRequireDefault(_ParsePromise); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { default: obj }; +} + +/** + * Converts a string into a regex that matches it. + * Surrounding with \Q .. \E does this, we just need to escape any \E's in + * the text separately. + */ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + */ + +function quote(s) { + return '\\Q' + s.replace('\\E', '\\E\\\\E\\Q') + '\\E'; +} + +/** + * Handles pre-populating the result data of a query with select fields, + * making sure that the data object contains keys for all objects that have + * been requested with a select, so that our cached state updates correctly. + */ +function handleSelectResult(data, select) { + var serverDataMask = {}; + + select.forEach(function (field) { + var hasSubObjectSelect = field.indexOf(".") !== -1; + if (!hasSubObjectSelect && !data.hasOwnProperty(field)) { + // this field was selected, but is missing from the retrieved data + data[field] = undefined; + } else if (hasSubObjectSelect) { + // this field references a sub-object, + // so we need to walk down the path components + var pathComponents = field.split("."); + var obj = data; + var serverMask = serverDataMask; + + pathComponents.forEach(function (component, index, arr) { + // add keys if the expected data is missing + if (!obj[component]) { + obj[component] = index == arr.length - 1 ? undefined : {}; + } + obj = obj[component]; + + //add this path component to the server mask so we can fill it in later if needed + if (index < arr.length - 1) { + if (!serverMask[component]) { + serverMask[component] = {}; + } + } + }); + } + }); + + if ((0, _keys2.default)(serverDataMask).length > 0) { + var copyMissingDataWithMask = function copyMissingDataWithMask(src, dest, mask, copyThisLevel) { + //copy missing elements at this level + if (copyThisLevel) { + for (var key in src) { + if (src.hasOwnProperty(key) && !dest.hasOwnProperty(key)) { + dest[key] = src[key]; + } + } + } + for (var key in mask) { + //traverse into objects as needed + copyMissingDataWithMask(src[key], dest[key], mask[key], true); + } + }; + + // When selecting from sub-objects, we don't want to blow away the missing + // information that we may have retrieved before. We've already added any + // missing selected keys to sub-objects, but we still need to add in the + // data for any previously retrieved sub-objects that were not selected. + + var serverData = _CoreManager2.default.getObjectStateController().getServerData({ id: data.objectId, className: data.className }); + + copyMissingDataWithMask(serverData, data, serverDataMask, false); + } +} + +/** + * Creates a new parse Parse.Query for the given Parse.Object subclass. + * @class Parse.Query + * @constructor + * @param {} objectClass An instance of a subclass of Parse.Object, or a Parse className string. + * + *
Parse.Query defines a query that is used to fetch Parse.Objects. The
+ * most common use case is finding all objects that match a query through the
+ * find
method. For example, this sample code fetches all objects
+ * of class MyClass
. It calls a different function depending on
+ * whether the fetch succeeded or not.
+ *
+ *
+ * var query = new Parse.Query(MyClass); + * query.find({ + * success: function(results) { + * // results is an array of Parse.Object. + * }, + * + * error: function(error) { + * // error is an instance of Parse.Error. + * } + * });+ * + *
A Parse.Query can also be used to retrieve a single object whose id is
+ * known, through the get method. For example, this sample code fetches an
+ * object of class MyClass
and id myId
. It calls a
+ * different function depending on whether the fetch succeeded or not.
+ *
+ *
+ * var query = new Parse.Query(MyClass); + * query.get(myId, { + * success: function(object) { + * // object is an instance of Parse.Object. + * }, + * + * error: function(object, error) { + * // error is an instance of Parse.Error. + * } + * });+ * + *
A Parse.Query can also be used to count the number of objects that match
+ * the query without retrieving all of those objects. For example, this
+ * sample code counts the number of objects of the class MyClass
+ *
+ * var query = new Parse.Query(MyClass); + * query.count({ + * success: function(number) { + * // There are number instances of MyClass. + * }, + * + * error: function(error) { + * // error is an instance of Parse.Error. + * } + * });+ */ + +var ParseQuery = function () { + function ParseQuery(objectClass) { + (0, _classCallCheck3.default)(this, ParseQuery); + + if (typeof objectClass === 'string') { + if (objectClass === 'User' && _CoreManager2.default.get('PERFORM_USER_REWRITE')) { + this.className = '_User'; + } else { + this.className = objectClass; + } + } else if (objectClass instanceof _ParseObject2.default) { + this.className = objectClass.className; + } else if (typeof objectClass === 'function') { + if (typeof objectClass.className === 'string') { + this.className = objectClass.className; + } else { + var obj = new objectClass(); + this.className = obj.className; + } + } else { + throw new TypeError('A ParseQuery must be constructed with a ParseObject or class name.'); + } + + this._where = {}; + this._include = []; + this._limit = -1; // negative limit is not sent in the server request + this._skip = 0; + this._extraOptions = {}; + } + + /** + * Adds constraint that at least one of the passed in queries matches. + * @method _orQuery + * @param {Array} queries + * @return {Parse.Query} Returns the query, so you can chain this call. + */ + + (0, _createClass3.default)(ParseQuery, [{ + key: '_orQuery', + value: function (queries) { + var queryJSON = queries.map(function (q) { + return q.toJSON().where; + }); + + this._where.$or = queryJSON; + return this; + } + + /** + * Helper for condition queries + */ + + }, { + key: '_addCondition', + value: function (key, condition, value) { + if (!this._where[key] || typeof this._where[key] === 'string') { + this._where[key] = {}; + } + this._where[key][condition] = (0, _encode2.default)(value, false, true); + return this; + } + + /** + * Converts string for regular expression at the beginning + */ + + }, { + key: '_regexStartWith', + value: function (string) { + return '^' + quote(string); + } + + /** + * Returns a JSON representation of this query. + * @method toJSON + * @return {Object} The JSON representation of the query. + */ + + }, { + key: 'toJSON', + value: function () { + var params = { + where: this._where + }; + + if (this._include.length) { + params.include = this._include.join(','); + } + if (this._select) { + params.keys = this._select.join(','); + } + if (this._limit >= 0) { + params.limit = this._limit; + } + if (this._skip > 0) { + params.skip = this._skip; + } + if (this._order) { + params.order = this._order.join(','); + } + for (var key in this._extraOptions) { + params[key] = this._extraOptions[key]; + } + + return params; + } + + /** + * Constructs a Parse.Object whose id is already known by fetching data from + * the server. Either options.success or options.error is called when the + * find completes. + * + * @method get + * @param {String} objectId The id of the object to be fetched. + * @param {Object} options A Backbone-style options object. + * Valid options are:
var compoundQuery = Parse.Query.or(query1, query2, query3);+ * + * will create a compoundQuery that is an or of the query1, query2, and + * query3. + * @method or + * @param {...Parse.Query} var_args The list of queries to OR. + * @static + * @return {Parse.Query} The query that is the OR of the passed in queries. + */ + + }], [{ + key: 'or', + value: function () { + var className = null; + + for (var _len7 = arguments.length, queries = Array(_len7), _key7 = 0; _key7 < _len7; _key7++) { + queries[_key7] = arguments[_key7]; + } + + queries.forEach(function (q) { + if (!className) { + className = q.className; + } + + if (className !== q.className) { + throw new Error('All queries must be for the same class.'); + } + }); + + var query = new ParseQuery(className); + query._orQuery(queries); + return query; + } + }]); + return ParseQuery; +}(); + +exports.default = ParseQuery; + +var DefaultController = { + find: function (className, params, options) { + var RESTController = _CoreManager2.default.getRESTController(); + + return RESTController.request('GET', 'classes/' + className, params, options); + } +}; + +_CoreManager2.default.setQueryController(DefaultController); \ No newline at end of file diff --git a/lib/browser/ParseRelation.js b/lib/browser/ParseRelation.js new file mode 100644 index 000000000..fe9ea8769 --- /dev/null +++ b/lib/browser/ParseRelation.js @@ -0,0 +1,182 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); + +var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); + +var _createClass2 = require('babel-runtime/helpers/createClass'); + +var _createClass3 = _interopRequireDefault(_createClass2); + +var _ParseOp = require('./ParseOp'); + +var _ParseObject = require('./ParseObject'); + +var _ParseObject2 = _interopRequireDefault(_ParseObject); + +var _ParseQuery = require('./ParseQuery'); + +var _ParseQuery2 = _interopRequireDefault(_ParseQuery); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { default: obj }; +} + +/** + * Creates a new Relation for the given parent object and key. This + * constructor should rarely be used directly, but rather created by + * Parse.Object.relation. + * @class Parse.Relation + * @constructor + * @param {Parse.Object} parent The parent of this relation. + * @param {String} key The key for this relation on the parent. + * + *
+ * A class that is used to access all of the children of a many-to-many + * relationship. Each instance of Parse.Relation is associated with a + * particular parent object and key. + *
+ */ +var ParseRelation = function () { + function ParseRelation(parent, key) { + (0, _classCallCheck3.default)(this, ParseRelation); + + this.parent = parent; + this.key = key; + this.targetClassName = null; + } + + /** + * Makes sure that this relation has the right parent and key. + */ + + (0, _createClass3.default)(ParseRelation, [{ + key: '_ensureParentAndKey', + value: function (parent, key) { + this.key = this.key || key; + if (this.key !== key) { + throw new Error('Internal Error. Relation retrieved from two different keys.'); + } + if (this.parent) { + if (this.parent.className !== parent.className) { + throw new Error('Internal Error. Relation retrieved from two different Objects.'); + } + if (this.parent.id) { + if (this.parent.id !== parent.id) { + throw new Error('Internal Error. Relation retrieved from two different Objects.'); + } + } else if (parent.id) { + this.parent = parent; + } + } else { + this.parent = parent; + } + } + + /** + * Adds a Parse.Object or an array of Parse.Objects to the relation. + * @method add + * @param {} objects The item or items to add. + */ + + }, { + key: 'add', + value: function (objects) { + if (!Array.isArray(objects)) { + objects = [objects]; + } + + var change = new _ParseOp.RelationOp(objects, []); + var parent = this.parent; + if (!parent) { + throw new Error('Cannot add to a Relation without a parent'); + } + parent.set(this.key, change); + this.targetClassName = change._targetClassName; + return parent; + } + + /** + * Removes a Parse.Object or an array of Parse.Objects from this relation. + * @method remove + * @param {} objects The item or items to remove. + */ + + }, { + key: 'remove', + value: function (objects) { + if (!Array.isArray(objects)) { + objects = [objects]; + } + + var change = new _ParseOp.RelationOp([], objects); + if (!this.parent) { + throw new Error('Cannot remove from a Relation without a parent'); + } + this.parent.set(this.key, change); + this.targetClassName = change._targetClassName; + } + + /** + * Returns a JSON version of the object suitable for saving to disk. + * @method toJSON + * @return {Object} + */ + + }, { + key: 'toJSON', + value: function () { + return { + __type: 'Relation', + className: this.targetClassName + }; + } + + /** + * Returns a Parse.Query that is limited to objects in this + * relation. + * @method query + * @return {Parse.Query} + */ + + }, { + key: 'query', + value: function () { + var query; + var parent = this.parent; + if (!parent) { + throw new Error('Cannot construct a query for a Relation without a parent'); + } + if (!this.targetClassName) { + query = new _ParseQuery2.default(parent.className); + query._extraOptions.redirectClassNameForKey = this.key; + } else { + query = new _ParseQuery2.default(this.targetClassName); + } + query._addCondition('$relatedTo', 'object', { + __type: 'Pointer', + className: parent.className, + objectId: parent.id + }); + query._addCondition('$relatedTo', 'key', this.key); + + return query; + } + }]); + return ParseRelation; +}(); /** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + */ + +exports.default = ParseRelation; \ No newline at end of file diff --git a/lib/browser/ParseRole.js b/lib/browser/ParseRole.js new file mode 100644 index 000000000..567af7a54 --- /dev/null +++ b/lib/browser/ParseRole.js @@ -0,0 +1,196 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); + +var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); + +var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); + +var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); + +var _createClass2 = require('babel-runtime/helpers/createClass'); + +var _createClass3 = _interopRequireDefault(_createClass2); + +var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); + +var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); + +var _get2 = require('babel-runtime/helpers/get'); + +var _get3 = _interopRequireDefault(_get2); + +var _inherits2 = require('babel-runtime/helpers/inherits'); + +var _inherits3 = _interopRequireDefault(_inherits2); + +var _ParseACL = require('./ParseACL'); + +var _ParseACL2 = _interopRequireDefault(_ParseACL); + +var _ParseError = require('./ParseError'); + +var _ParseError2 = _interopRequireDefault(_ParseError); + +var _ParseObject2 = require('./ParseObject'); + +var _ParseObject3 = _interopRequireDefault(_ParseObject2); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { default: obj }; +} + +/** + * Represents a Role on the Parse server. Roles represent groupings of + * Users for the purposes of granting permissions (e.g. specifying an ACL + * for an Object). Roles are specified by their sets of child users and + * child roles, all of which are granted any permissions that the parent + * role has. + * + *Roles must have a name (which cannot be changed after creation of the + * role), and must specify an ACL.
+ * @class Parse.Role + * @constructor + * @param {String} name The name of the Role to create. + * @param {Parse.ACL} acl The ACL for this role. Roles must have an ACL. + * A Parse.Role is a local representation of a role persisted to the Parse + * cloud. + */ +var ParseRole = function (_ParseObject) { + (0, _inherits3.default)(ParseRole, _ParseObject); + + function ParseRole(name, acl) { + (0, _classCallCheck3.default)(this, ParseRole); + + var _this = (0, _possibleConstructorReturn3.default)(this, (ParseRole.__proto__ || (0, _getPrototypeOf2.default)(ParseRole)).call(this, '_Role')); + + if (typeof name === 'string' && acl instanceof _ParseACL2.default) { + _this.setName(name); + _this.setACL(acl); + } + return _this; + } + + /** + * Gets the name of the role. You can alternatively call role.get("name") + * + * @method getName + * @return {String} the name of the role. + */ + + (0, _createClass3.default)(ParseRole, [{ + key: 'getName', + value: function () { + var name = this.get('name'); + if (name == null || typeof name === 'string') { + return name; + } + return ''; + } + + /** + * Sets the name for a role. This value must be set before the role has + * been saved to the server, and cannot be set once the role has been + * saved. + * + *+ * A role's name can only contain alphanumeric characters, _, -, and + * spaces. + *
+ * + *This is equivalent to calling role.set("name", name)
+ * + * @method setName + * @param {String} name The name of the role. + * @param {Object} options Standard options object with success and error + * callbacks. + */ + + }, { + key: 'setName', + value: function (name, options) { + return this.set('name', name, options); + } + + /** + * Gets the Parse.Relation for the Parse.Users that are direct + * children of this role. These users are granted any privileges that this + * role has been granted (e.g. read or write access through ACLs). You can + * add or remove users from the role through this relation. + * + *This is equivalent to calling role.relation("users")
+ * + * @method getUsers + * @return {Parse.Relation} the relation for the users belonging to this + * role. + */ + + }, { + key: 'getUsers', + value: function () { + return this.relation('users'); + } + + /** + * Gets the Parse.Relation for the Parse.Roles that are direct + * children of this role. These roles' users are granted any privileges that + * this role has been granted (e.g. read or write access through ACLs). You + * can add or remove child roles from this role through this relation. + * + *This is equivalent to calling role.relation("roles")
+ * + * @method getRoles + * @return {Parse.Relation} the relation for the roles belonging to this + * role. + */ + + }, { + key: 'getRoles', + value: function () { + return this.relation('roles'); + } + }, { + key: 'validate', + value: function (attrs, options) { + var isInvalid = (0, _get3.default)(ParseRole.prototype.__proto__ || (0, _getPrototypeOf2.default)(ParseRole.prototype), 'validate', this).call(this, attrs, options); + if (isInvalid) { + return isInvalid; + } + + if ('name' in attrs && attrs.name !== this.getName()) { + var newName = attrs.name; + if (this.id && this.id !== attrs.objectId) { + // Check to see if the objectId being set matches this.id + // This happens during a fetch -- the id is set before calling fetch + // Let the name be set in this case + return new _ParseError2.default(_ParseError2.default.OTHER_CAUSE, 'A role\'s name can only be set before it has been saved.'); + } + if (typeof newName !== 'string') { + return new _ParseError2.default(_ParseError2.default.OTHER_CAUSE, 'A role\'s name must be a String.'); + } + if (!/^[0-9a-zA-Z\-_ ]+$/.test(newName)) { + return new _ParseError2.default(_ParseError2.default.OTHER_CAUSE, 'A role\'s name can be only contain alphanumeric characters, _, ' + '-, and spaces.'); + } + } + return false; + } + }]); + return ParseRole; +}(_ParseObject3.default); /** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + */ + +exports.default = ParseRole; + +_ParseObject3.default.registerSubclass('_Role', ParseRole); \ No newline at end of file diff --git a/lib/browser/ParseSession.js b/lib/browser/ParseSession.js new file mode 100644 index 000000000..7fb75c80d --- /dev/null +++ b/lib/browser/ParseSession.js @@ -0,0 +1,180 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _typeof2 = require('babel-runtime/helpers/typeof'); + +var _typeof3 = _interopRequireDefault(_typeof2); + +var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); + +var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); + +var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); + +var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); + +var _createClass2 = require('babel-runtime/helpers/createClass'); + +var _createClass3 = _interopRequireDefault(_createClass2); + +var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); + +var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); + +var _inherits2 = require('babel-runtime/helpers/inherits'); + +var _inherits3 = _interopRequireDefault(_inherits2); + +var _CoreManager = require('./CoreManager'); + +var _CoreManager2 = _interopRequireDefault(_CoreManager); + +var _isRevocableSession = require('./isRevocableSession'); + +var _isRevocableSession2 = _interopRequireDefault(_isRevocableSession); + +var _ParseObject2 = require('./ParseObject'); + +var _ParseObject3 = _interopRequireDefault(_ParseObject2); + +var _ParsePromise = require('./ParsePromise'); + +var _ParsePromise2 = _interopRequireDefault(_ParsePromise); + +var _ParseUser = require('./ParseUser'); + +var _ParseUser2 = _interopRequireDefault(_ParseUser); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { default: obj }; +} + +/** + * @class Parse.Session + * @constructor + * + *A Parse.Session object is a local representation of a revocable session. + * This class is a subclass of a Parse.Object, and retains the same + * functionality of a Parse.Object.
+ */ +var ParseSession = function (_ParseObject) { + (0, _inherits3.default)(ParseSession, _ParseObject); + + function ParseSession(attributes) { + (0, _classCallCheck3.default)(this, ParseSession); + + var _this = (0, _possibleConstructorReturn3.default)(this, (ParseSession.__proto__ || (0, _getPrototypeOf2.default)(ParseSession)).call(this, '_Session')); + + if (attributes && (typeof attributes === 'undefined' ? 'undefined' : (0, _typeof3.default)(attributes)) === 'object') { + if (!_this.set(attributes || {})) { + throw new Error('Can\'t create an invalid Session'); + } + } + return _this; + } + + /** + * Returns the session token string. + * @method getSessionToken + * @return {String} + */ + + (0, _createClass3.default)(ParseSession, [{ + key: 'getSessionToken', + value: function () { + var token = this.get('sessionToken'); + if (typeof token === 'string') { + return token; + } + return ''; + } + }], [{ + key: 'readOnlyAttributes', + value: function () { + return ['createdWith', 'expiresAt', 'installationId', 'restricted', 'sessionToken', 'user']; + } + + /** + * Retrieves the Session object for the currently logged in session. + * @method current + * @static + * @return {Parse.Promise} A promise that is resolved with the Parse.Session + * object after it has been fetched. If there is no current user, the + * promise will be rejected. + */ + + }, { + key: 'current', + value: function (options) { + options = options || {}; + var controller = _CoreManager2.default.getSessionController(); + + var sessionOptions = {}; + if (options.hasOwnProperty('useMasterKey')) { + sessionOptions.useMasterKey = options.useMasterKey; + } + return _ParseUser2.default.currentAsync().then(function (user) { + if (!user) { + return _ParsePromise2.default.error('There is no current user.'); + } + user.getSessionToken(); + + sessionOptions.sessionToken = user.getSessionToken(); + return controller.getSession(sessionOptions); + }); + } + + /** + * Determines whether the current session token is revocable. + * This method is useful for migrating Express.js or Node.js web apps to + * use revocable sessions. If you are migrating an app that uses the Parse + * SDK in the browser only, please use Parse.User.enableRevocableSession() + * instead, so that sessions can be automatically upgraded. + * @method isCurrentSessionRevocable + * @static + * @return {Boolean} + */ + + }, { + key: 'isCurrentSessionRevocable', + value: function () { + var currentUser = _ParseUser2.default.current(); + if (currentUser) { + return (0, _isRevocableSession2.default)(currentUser.getSessionToken() || ''); + } + return false; + } + }]); + return ParseSession; +}(_ParseObject3.default); /** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + */ + +exports.default = ParseSession; + +_ParseObject3.default.registerSubclass('_Session', ParseSession); + +var DefaultController = { + getSession: function (options) { + var RESTController = _CoreManager2.default.getRESTController(); + var session = new ParseSession(); + + return RESTController.request('GET', 'sessions/me', {}, options).then(function (sessionData) { + session._finishFetch(sessionData); + session._setExisted(true); + return session; + }); + } +}; + +_CoreManager2.default.setSessionController(DefaultController); \ No newline at end of file diff --git a/lib/browser/ParseUser.js b/lib/browser/ParseUser.js new file mode 100644 index 000000000..f18f42820 --- /dev/null +++ b/lib/browser/ParseUser.js @@ -0,0 +1,1150 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _stringify = require('babel-runtime/core-js/json/stringify'); + +var _stringify2 = _interopRequireDefault(_stringify); + +var _defineProperty = require('babel-runtime/core-js/object/define-property'); + +var _defineProperty2 = _interopRequireDefault(_defineProperty); + +var _typeof2 = require('babel-runtime/helpers/typeof'); + +var _typeof3 = _interopRequireDefault(_typeof2); + +var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); + +var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); + +var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); + +var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); + +var _createClass2 = require('babel-runtime/helpers/createClass'); + +var _createClass3 = _interopRequireDefault(_createClass2); + +var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); + +var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); + +var _get2 = require('babel-runtime/helpers/get'); + +var _get3 = _interopRequireDefault(_get2); + +var _inherits2 = require('babel-runtime/helpers/inherits'); + +var _inherits3 = _interopRequireDefault(_inherits2); + +var _CoreManager = require('./CoreManager'); + +var _CoreManager2 = _interopRequireDefault(_CoreManager); + +var _isRevocableSession = require('./isRevocableSession'); + +var _isRevocableSession2 = _interopRequireDefault(_isRevocableSession); + +var _ParseError = require('./ParseError'); + +var _ParseError2 = _interopRequireDefault(_ParseError); + +var _ParseObject2 = require('./ParseObject'); + +var _ParseObject3 = _interopRequireDefault(_ParseObject2); + +var _ParsePromise = require('./ParsePromise'); + +var _ParsePromise2 = _interopRequireDefault(_ParsePromise); + +var _ParseSession = require('./ParseSession'); + +var _ParseSession2 = _interopRequireDefault(_ParseSession); + +var _Storage = require('./Storage'); + +var _Storage2 = _interopRequireDefault(_Storage); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { default: obj }; +} + +var CURRENT_USER_KEY = 'currentUser'; /** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + */ + +var canUseCurrentUser = !_CoreManager2.default.get('IS_NODE'); +var currentUserCacheMatchesDisk = false; +var currentUserCache = null; + +var authProviders = {}; + +/** + * @class Parse.User + * @constructor + * + *A Parse.User object is a local representation of a user persisted to the + * Parse cloud. This class is a subclass of a Parse.Object, and retains the + * same functionality of a Parse.Object, but also extends it with various + * user specific methods, like authentication, signing up, and validation of + * uniqueness.
+ */ + +var ParseUser = function (_ParseObject) { + (0, _inherits3.default)(ParseUser, _ParseObject); + + function ParseUser(attributes) { + (0, _classCallCheck3.default)(this, ParseUser); + + var _this = (0, _possibleConstructorReturn3.default)(this, (ParseUser.__proto__ || (0, _getPrototypeOf2.default)(ParseUser)).call(this, '_User')); + + if (attributes && (typeof attributes === 'undefined' ? 'undefined' : (0, _typeof3.default)(attributes)) === 'object') { + if (!_this.set(attributes || {})) { + throw new Error('Can\'t create an invalid Parse User'); + } + } + return _this; + } + + /** + * Request a revocable session token to replace the older style of token. + * @method _upgradeToRevocableSession + * @param {Object} options A Backbone-style options object. + * @return {Parse.Promise} A promise that is resolved when the replacement + * token has been fetched. + */ + + (0, _createClass3.default)(ParseUser, [{ + key: '_upgradeToRevocableSession', + value: function (options) { + options = options || {}; + + var upgradeOptions = {}; + if (options.hasOwnProperty('useMasterKey')) { + upgradeOptions.useMasterKey = options.useMasterKey; + } + + var controller = _CoreManager2.default.getUserController(); + return controller.upgradeToRevocableSession(this, upgradeOptions)._thenRunCallbacks(options); + } + + /** + * Unlike in the Android/iOS SDKs, logInWith is unnecessary, since you can + * call linkWith on the user (even if it doesn't exist yet on the server). + * @method _linkWith + */ + + }, { + key: '_linkWith', + value: function (provider, options) { + var _this2 = this; + + var authType; + if (typeof provider === 'string') { + authType = provider; + provider = authProviders[provider]; + } else { + authType = provider.getAuthType(); + } + if (options && options.hasOwnProperty('authData')) { + var authData = this.get('authData') || {}; + if ((typeof authData === 'undefined' ? 'undefined' : (0, _typeof3.default)(authData)) !== 'object') { + throw new Error('Invalid type: authData field should be an object'); + } + authData[authType] = options.authData; + + var controller = _CoreManager2.default.getUserController(); + return controller.linkWith(this, authData)._thenRunCallbacks(options, this); + } else { + var promise = new _ParsePromise2.default(); + provider.authenticate({ + success: function (provider, result) { + var opts = {}; + opts.authData = result; + if (options.success) { + opts.success = options.success; + } + if (options.error) { + opts.error = options.error; + } + _this2._linkWith(provider, opts).then(function () { + promise.resolve(_this2); + }, function (error) { + promise.reject(error); + }); + }, + error: function (provider, _error) { + if (typeof options.error === 'function') { + options.error(_this2, _error); + } + promise.reject(_error); + } + }); + return promise; + } + } + + /** + * Synchronizes auth data for a provider (e.g. puts the access token in the + * right place to be used by the Facebook SDK). + * @method _synchronizeAuthData + */ + + }, { + key: '_synchronizeAuthData', + value: function (provider) { + if (!this.isCurrent() || !provider) { + return; + } + var authType; + if (typeof provider === 'string') { + authType = provider; + provider = authProviders[authType]; + } else { + authType = provider.getAuthType(); + } + var authData = this.get('authData'); + if (!provider || !authData || (typeof authData === 'undefined' ? 'undefined' : (0, _typeof3.default)(authData)) !== 'object') { + return; + } + var success = provider.restoreAuthentication(authData[authType]); + if (!success) { + this._unlinkFrom(provider); + } + } + + /** + * Synchronizes authData for all providers. + * @method _synchronizeAllAuthData + */ + + }, { + key: '_synchronizeAllAuthData', + value: function () { + var authData = this.get('authData'); + if ((typeof authData === 'undefined' ? 'undefined' : (0, _typeof3.default)(authData)) !== 'object') { + return; + } + + for (var key in authData) { + this._synchronizeAuthData(key); + } + } + + /** + * Removes null values from authData (which exist temporarily for + * unlinking) + * @method _cleanupAuthData + */ + + }, { + key: '_cleanupAuthData', + value: function () { + if (!this.isCurrent()) { + return; + } + var authData = this.get('authData'); + if ((typeof authData === 'undefined' ? 'undefined' : (0, _typeof3.default)(authData)) !== 'object') { + return; + } + + for (var key in authData) { + if (!authData[key]) { + delete authData[key]; + } + } + } + + /** + * Unlinks a user from a service. + * @method _unlinkFrom + */ + + }, { + key: '_unlinkFrom', + value: function (provider, options) { + var _this3 = this; + + if (typeof provider === 'string') { + provider = authProviders[provider]; + } else { + provider.getAuthType(); + } + return this._linkWith(provider, { authData: null }).then(function () { + _this3._synchronizeAuthData(provider); + return _ParsePromise2.default.as(_this3); + })._thenRunCallbacks(options); + } + + /** + * Checks whether a user is linked to a service. + * @method _isLinked + */ + + }, { + key: '_isLinked', + value: function (provider) { + var authType; + if (typeof provider === 'string') { + authType = provider; + } else { + authType = provider.getAuthType(); + } + var authData = this.get('authData') || {}; + if ((typeof authData === 'undefined' ? 'undefined' : (0, _typeof3.default)(authData)) !== 'object') { + return false; + } + return !!authData[authType]; + } + + /** + * Deauthenticates all providers. + * @method _logOutWithAll + */ + + }, { + key: '_logOutWithAll', + value: function () { + var authData = this.get('authData'); + if ((typeof authData === 'undefined' ? 'undefined' : (0, _typeof3.default)(authData)) !== 'object') { + return; + } + + for (var key in authData) { + this._logOutWith(key); + } + } + + /** + * Deauthenticates a single provider (e.g. removing access tokens from the + * Facebook SDK). + * @method _logOutWith + */ + + }, { + key: '_logOutWith', + value: function (provider) { + if (!this.isCurrent()) { + return; + } + if (typeof provider === 'string') { + provider = authProviders[provider]; + } + if (provider && provider.deauthenticate) { + provider.deauthenticate(); + } + } + + /** + * Class instance method used to maintain specific keys when a fetch occurs. + * Used to ensure that the session token is not lost. + */ + + }, { + key: '_preserveFieldsOnFetch', + value: function () { + return { + sessionToken: this.get('sessionToken') + }; + } + + /** + * Returns true ifcurrent
would return this user.
+ * @method isCurrent
+ * @return {Boolean}
+ */
+
+ }, {
+ key: 'isCurrent',
+ value: function () {
+ var current = ParseUser.current();
+ return !!current && current.id === this.id;
+ }
+
+ /**
+ * Returns get("username").
+ * @method getUsername
+ * @return {String}
+ */
+
+ }, {
+ key: 'getUsername',
+ value: function () {
+ var username = this.get('username');
+ if (username == null || typeof username === 'string') {
+ return username;
+ }
+ return '';
+ }
+
+ /**
+ * Calls set("username", username, options) and returns the result.
+ * @method setUsername
+ * @param {String} username
+ * @param {Object} options A Backbone-style options object.
+ * @return {Boolean}
+ */
+
+ }, {
+ key: 'setUsername',
+ value: function (username) {
+ // Strip anonymity, even we do not support anonymous user in js SDK, we may
+ // encounter anonymous user created by android/iOS in cloud code.
+ var authData = this.get('authData');
+ if (authData && (typeof authData === 'undefined' ? 'undefined' : (0, _typeof3.default)(authData)) === 'object' && authData.hasOwnProperty('anonymous')) {
+ // We need to set anonymous to null instead of deleting it in order to remove it from Parse.
+ authData.anonymous = null;
+ }
+ this.set('username', username);
+ }
+
+ /**
+ * Calls set("password", password, options) and returns the result.
+ * @method setPassword
+ * @param {String} password
+ * @param {Object} options A Backbone-style options object.
+ * @return {Boolean}
+ */
+
+ }, {
+ key: 'setPassword',
+ value: function (password) {
+ this.set('password', password);
+ }
+
+ /**
+ * Returns get("email").
+ * @method getEmail
+ * @return {String}
+ */
+
+ }, {
+ key: 'getEmail',
+ value: function () {
+ var email = this.get('email');
+ if (email == null || typeof email === 'string') {
+ return email;
+ }
+ return '';
+ }
+
+ /**
+ * Calls set("email", email, options) and returns the result.
+ * @method setEmail
+ * @param {String} email
+ * @param {Object} options A Backbone-style options object.
+ * @return {Boolean}
+ */
+
+ }, {
+ key: 'setEmail',
+ value: function (email) {
+ this.set('email', email);
+ }
+
+ /**
+ * Returns the session token for this user, if the user has been logged in,
+ * or if it is the result of a query with the master key. Otherwise, returns
+ * undefined.
+ * @method getSessionToken
+ * @return {String} the session token, or undefined
+ */
+
+ }, {
+ key: 'getSessionToken',
+ value: function () {
+ var token = this.get('sessionToken');
+ if (token == null || typeof token === 'string') {
+ return token;
+ }
+ return '';
+ }
+
+ /**
+ * Checks whether this user is the current user and has been authenticated.
+ * @method authenticated
+ * @return (Boolean) whether this user is the current user and is logged in.
+ */
+
+ }, {
+ key: 'authenticated',
+ value: function () {
+ var current = ParseUser.current();
+ return !!this.get('sessionToken') && !!current && current.id === this.id;
+ }
+
+ /**
+ * Signs up a new user. You should call this instead of save for
+ * new Parse.Users. This will create a new Parse.User on the server, and
+ * also persist the session on disk so that you can access the user using
+ * current
.
+ *
+ * A username and password must be set before calling signUp.
+ * + *Calls options.success or options.error on completion.
+ * + * @method signUp + * @param {Object} attrs Extra fields to set on the new user, or null. + * @param {Object} options A Backbone-style options object. + * @return {Parse.Promise} A promise that is fulfilled when the signup + * finishes. + */ + + }, { + key: 'signUp', + value: function (attrs, options) { + options = options || {}; + + var signupOptions = {}; + if (options.hasOwnProperty('useMasterKey')) { + signupOptions.useMasterKey = options.useMasterKey; + } + if (options.hasOwnProperty('installationId')) { + signupOptions.installationId = options.installationId; + } + + var controller = _CoreManager2.default.getUserController(); + return controller.signUp(this, attrs, signupOptions)._thenRunCallbacks(options, this); + } + + /** + * Logs in a Parse.User. On success, this saves the session to disk, + * so you can retrieve the currently logged in user using + *current
.
+ *
+ * A username and password must be set before calling logIn.
+ * + *Calls options.success or options.error on completion.
+ * + * @method logIn + * @param {Object} options A Backbone-style options object. + * @return {Parse.Promise} A promise that is fulfilled with the user when + * the login is complete. + */ + + }, { + key: 'logIn', + value: function (options) { + options = options || {}; + + var loginOptions = {}; + if (options.hasOwnProperty('useMasterKey')) { + loginOptions.useMasterKey = options.useMasterKey; + } + if (options.hasOwnProperty('installationId')) { + loginOptions.installationId = options.installationId; + } + + var controller = _CoreManager2.default.getUserController(); + return controller.logIn(this, loginOptions)._thenRunCallbacks(options, this); + } + + /** + * Wrap the default save behavior with functionality to save to local + * storage if this is current user. + */ + + }, { + key: 'save', + value: function () { + var _this4 = this; + + for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + return (0, _get3.default)(ParseUser.prototype.__proto__ || (0, _getPrototypeOf2.default)(ParseUser.prototype), 'save', this).apply(this, args).then(function () { + if (_this4.isCurrent()) { + return _CoreManager2.default.getUserController().updateUserOnDisk(_this4); + } + return _this4; + }); + } + + /** + * Wrap the default destroy behavior with functionality that logs out + * the current user when it is destroyed + */ + + }, { + key: 'destroy', + value: function () { + var _this5 = this; + + for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { + args[_key2] = arguments[_key2]; + } + + return (0, _get3.default)(ParseUser.prototype.__proto__ || (0, _getPrototypeOf2.default)(ParseUser.prototype), 'destroy', this).apply(this, args).then(function () { + if (_this5.isCurrent()) { + return _CoreManager2.default.getUserController().removeUserFromDisk(); + } + return _this5; + }); + } + + /** + * Wrap the default fetch behavior with functionality to save to local + * storage if this is current user. + */ + + }, { + key: 'fetch', + value: function () { + var _this6 = this; + + for (var _len3 = arguments.length, args = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) { + args[_key3] = arguments[_key3]; + } + + return (0, _get3.default)(ParseUser.prototype.__proto__ || (0, _getPrototypeOf2.default)(ParseUser.prototype), 'fetch', this).apply(this, args).then(function () { + if (_this6.isCurrent()) { + return _CoreManager2.default.getUserController().updateUserOnDisk(_this6); + } + return _this6; + }); + } + }], [{ + key: 'readOnlyAttributes', + value: function () { + return ['sessionToken']; + } + + /** + * Adds functionality to the existing Parse.User class + * @method extend + * @param {Object} protoProps A set of properties to add to the prototype + * @param {Object} classProps A set of static properties to add to the class + * @static + * @return {Class} The newly extended Parse.User class + */ + + }, { + key: 'extend', + value: function (protoProps, classProps) { + if (protoProps) { + for (var prop in protoProps) { + if (prop !== 'className') { + (0, _defineProperty2.default)(ParseUser.prototype, prop, { + value: protoProps[prop], + enumerable: false, + writable: true, + configurable: true + }); + } + } + } + + if (classProps) { + for (var prop in classProps) { + if (prop !== 'className') { + (0, _defineProperty2.default)(ParseUser, prop, { + value: classProps[prop], + enumerable: false, + writable: true, + configurable: true + }); + } + } + } + + return ParseUser; + } + + /** + * Retrieves the currently logged in ParseUser with a valid session, + * either from memory or localStorage, if necessary. + * @method current + * @static + * @return {Parse.Object} The currently logged in Parse.User. + */ + + }, { + key: 'current', + value: function () { + if (!canUseCurrentUser) { + return null; + } + var controller = _CoreManager2.default.getUserController(); + return controller.currentUser(); + } + + /** + * Retrieves the currently logged in ParseUser from asynchronous Storage. + * @method currentAsync + * @static + * @return {Parse.Promise} A Promise that is resolved with the currently + * logged in Parse User + */ + + }, { + key: 'currentAsync', + value: function () { + if (!canUseCurrentUser) { + return _ParsePromise2.default.as(null); + } + var controller = _CoreManager2.default.getUserController(); + return controller.currentUserAsync(); + } + + /** + * Signs up a new user with a username (or email) and password. + * This will create a new Parse.User on the server, and also persist the + * session in localStorage so that you can access the user using + * {@link #current}. + * + *Calls options.success or options.error on completion.
+ * + * @method signUp + * @param {String} username The username (or email) to sign up with. + * @param {String} password The password to sign up with. + * @param {Object} attrs Extra fields to set on the new user. + * @param {Object} options A Backbone-style options object. + * @static + * @return {Parse.Promise} A promise that is fulfilled with the user when + * the signup completes. + */ + + }, { + key: 'signUp', + value: function (username, password, attrs, options) { + attrs = attrs || {}; + attrs.username = username; + attrs.password = password; + var user = new ParseUser(attrs); + return user.signUp({}, options); + } + + /** + * Logs in a user with a username (or email) and password. On success, this + * saves the session to disk, so you can retrieve the currently logged in + * user usingcurrent
.
+ *
+ * Calls options.success or options.error on completion.
+ * + * @method logIn + * @param {String} username The username (or email) to log in with. + * @param {String} password The password to log in with. + * @param {Object} options A Backbone-style options object. + * @static + * @return {Parse.Promise} A promise that is fulfilled with the user when + * the login completes. + */ + + }, { + key: 'logIn', + value: function (username, password, options) { + if (typeof username !== 'string') { + return _ParsePromise2.default.error(new _ParseError2.default(_ParseError2.default.OTHER_CAUSE, 'Username must be a string.')); + } else if (typeof password !== 'string') { + return _ParsePromise2.default.error(new _ParseError2.default(_ParseError2.default.OTHER_CAUSE, 'Password must be a string.')); + } + var user = new ParseUser(); + user._finishFetch({ username: username, password: password }); + return user.logIn(options); + } + + /** + * Logs in a user with a session token. On success, this saves the session + * to disk, so you can retrieve the currently logged in user using + *current
.
+ *
+ * Calls options.success or options.error on completion.
+ * + * @method become + * @param {String} sessionToken The sessionToken to log in with. + * @param {Object} options A Backbone-style options object. + * @static + * @return {Parse.Promise} A promise that is fulfilled with the user when + * the login completes. + */ + + }, { + key: 'become', + value: function (sessionToken, options) { + if (!canUseCurrentUser) { + throw new Error('It is not memory-safe to become a user in a server environment'); + } + options = options || {}; + + var becomeOptions = { + sessionToken: sessionToken + }; + if (options.hasOwnProperty('useMasterKey')) { + becomeOptions.useMasterKey = options.useMasterKey; + } + + var controller = _CoreManager2.default.getUserController(); + return controller.become(becomeOptions)._thenRunCallbacks(options); + } + }, { + key: 'logInWith', + value: function (provider, options) { + return ParseUser._logInWith(provider, options); + } + + /** + * Logs out the currently logged in user session. This will remove the + * session from disk, log out of linked services, and future calls to + *current
will return null
.
+ * @method logOut
+ * @static
+ * @return {Parse.Promise} A promise that is resolved when the session is
+ * destroyed on the server.
+ */
+
+ }, {
+ key: 'logOut',
+ value: function () {
+ if (!canUseCurrentUser) {
+ throw new Error('There is no current user user on a node.js server environment.');
+ }
+
+ var controller = _CoreManager2.default.getUserController();
+ return controller.logOut();
+ }
+
+ /**
+ * Requests a password reset email to be sent to the specified email address
+ * associated with the user account. This email allows the user to securely
+ * reset their password on the Parse site.
+ *
+ * Calls options.success or options.error on completion.
+ * + * @method requestPasswordReset + * @param {String} email The email address associated with the user that + * forgot their password. + * @param {Object} options A Backbone-style options object. + * @static + */ + + }, { + key: 'requestPasswordReset', + value: function (email, options) { + options = options || {}; + + var requestOptions = {}; + if (options.hasOwnProperty('useMasterKey')) { + requestOptions.useMasterKey = options.useMasterKey; + } + + var controller = _CoreManager2.default.getUserController(); + return controller.requestPasswordReset(email, requestOptions)._thenRunCallbacks(options); + } + + /** + * Allow someone to define a custom User class without className + * being rewritten to _User. The default behavior is to rewrite + * User to _User for legacy reasons. This allows developers to + * override that behavior. + * + * @method allowCustomUserClass + * @param {Boolean} isAllowed Whether or not to allow custom User class + * @static + */ + + }, { + key: 'allowCustomUserClass', + value: function (isAllowed) { + _CoreManager2.default.set('PERFORM_USER_REWRITE', !isAllowed); + } + + /** + * Allows a legacy application to start using revocable sessions. If the + * current session token is not revocable, a request will be made for a new, + * revocable session. + * It is not necessary to call this method from cloud code unless you are + * handling user signup or login from the server side. In a cloud code call, + * this function will not attempt to upgrade the current token. + * @method enableRevocableSession + * @param {Object} options A Backbone-style options object. + * @static + * @return {Parse.Promise} A promise that is resolved when the process has + * completed. If a replacement session token is requested, the promise + * will be resolved after a new token has been fetched. + */ + + }, { + key: 'enableRevocableSession', + value: function (options) { + options = options || {}; + _CoreManager2.default.set('FORCE_REVOCABLE_SESSION', true); + if (canUseCurrentUser) { + var current = ParseUser.current(); + if (current) { + return current._upgradeToRevocableSession(options); + } + } + return _ParsePromise2.default.as()._thenRunCallbacks(options); + } + + /** + * Enables the use of become or the current user in a server + * environment. These features are disabled by default, since they depend on + * global objects that are not memory-safe for most servers. + * @method enableUnsafeCurrentUser + * @static + */ + + }, { + key: 'enableUnsafeCurrentUser', + value: function () { + canUseCurrentUser = true; + } + + /** + * Disables the use of become or the current user in any environment. + * These features are disabled on servers by default, since they depend on + * global objects that are not memory-safe for most servers. + * @method disableUnsafeCurrentUser + * @static + */ + + }, { + key: 'disableUnsafeCurrentUser', + value: function () { + canUseCurrentUser = false; + } + }, { + key: '_registerAuthenticationProvider', + value: function (provider) { + authProviders[provider.getAuthType()] = provider; + // Synchronize the current user with the auth provider. + ParseUser.currentAsync().then(function (current) { + if (current) { + current._synchronizeAuthData(provider.getAuthType()); + } + }); + } + }, { + key: '_logInWith', + value: function (provider, options) { + var user = new ParseUser(); + return user._linkWith(provider, options); + } + }, { + key: '_clearCache', + value: function () { + currentUserCache = null; + currentUserCacheMatchesDisk = false; + } + }, { + key: '_setCurrentUserCache', + value: function (user) { + currentUserCache = user; + } + }]); + return ParseUser; +}(_ParseObject3.default); + +exports.default = ParseUser; + +_ParseObject3.default.registerSubclass('_User', ParseUser); + +var DefaultController = { + updateUserOnDisk: function (user) { + var path = _Storage2.default.generatePath(CURRENT_USER_KEY); + var json = user.toJSON(); + json.className = '_User'; + return _Storage2.default.setItemAsync(path, (0, _stringify2.default)(json)).then(function () { + return user; + }); + }, + removeUserFromDisk: function () { + var path = _Storage2.default.generatePath(CURRENT_USER_KEY); + currentUserCacheMatchesDisk = true; + currentUserCache = null; + return _Storage2.default.removeItemAsync(path); + }, + setCurrentUser: function (user) { + currentUserCache = user; + user._cleanupAuthData(); + user._synchronizeAllAuthData(); + return DefaultController.updateUserOnDisk(user); + }, + currentUser: function () { + if (currentUserCache) { + return currentUserCache; + } + if (currentUserCacheMatchesDisk) { + return null; + } + if (_Storage2.default.async()) { + throw new Error('Cannot call currentUser() when using a platform with an async ' + 'storage system. Call currentUserAsync() instead.'); + } + var path = _Storage2.default.generatePath(CURRENT_USER_KEY); + var userData = _Storage2.default.getItem(path); + currentUserCacheMatchesDisk = true; + if (!userData) { + currentUserCache = null; + return null; + } + userData = JSON.parse(userData); + if (!userData.className) { + userData.className = '_User'; + } + if (userData._id) { + if (userData.objectId !== userData._id) { + userData.objectId = userData._id; + } + delete userData._id; + } + if (userData._sessionToken) { + userData.sessionToken = userData._sessionToken; + delete userData._sessionToken; + } + var current = _ParseObject3.default.fromJSON(userData); + currentUserCache = current; + current._synchronizeAllAuthData(); + return current; + }, + currentUserAsync: function () { + if (currentUserCache) { + return _ParsePromise2.default.as(currentUserCache); + } + if (currentUserCacheMatchesDisk) { + return _ParsePromise2.default.as(null); + } + var path = _Storage2.default.generatePath(CURRENT_USER_KEY); + return _Storage2.default.getItemAsync(path).then(function (userData) { + currentUserCacheMatchesDisk = true; + if (!userData) { + currentUserCache = null; + return _ParsePromise2.default.as(null); + } + userData = JSON.parse(userData); + if (!userData.className) { + userData.className = '_User'; + } + if (userData._id) { + if (userData.objectId !== userData._id) { + userData.objectId = userData._id; + } + delete userData._id; + } + if (userData._sessionToken) { + userData.sessionToken = userData._sessionToken; + delete userData._sessionToken; + } + var current = _ParseObject3.default.fromJSON(userData); + currentUserCache = current; + current._synchronizeAllAuthData(); + return _ParsePromise2.default.as(current); + }); + }, + signUp: function (user, attrs, options) { + var username = attrs && attrs.username || user.get('username'); + var password = attrs && attrs.password || user.get('password'); + + if (!username || !username.length) { + return _ParsePromise2.default.error(new _ParseError2.default(_ParseError2.default.OTHER_CAUSE, 'Cannot sign up user with an empty name.')); + } + if (!password || !password.length) { + return _ParsePromise2.default.error(new _ParseError2.default(_ParseError2.default.OTHER_CAUSE, 'Cannot sign up user with an empty password.')); + } + + return user.save(attrs, options).then(function () { + // Clear the password field + user._finishFetch({ password: undefined }); + + if (canUseCurrentUser) { + return DefaultController.setCurrentUser(user); + } + return user; + }); + }, + logIn: function (user, options) { + var RESTController = _CoreManager2.default.getRESTController(); + var stateController = _CoreManager2.default.getObjectStateController(); + var auth = { + username: user.get('username'), + password: user.get('password') + }; + return RESTController.request('GET', 'login', auth, options).then(function (response, status) { + user._migrateId(response.objectId); + user._setExisted(true); + stateController.setPendingOp(user._getStateIdentifier(), 'username', undefined); + stateController.setPendingOp(user._getStateIdentifier(), 'password', undefined); + response.password = undefined; + user._finishFetch(response); + if (!canUseCurrentUser) { + // We can't set the current user, so just return the one we logged in + return _ParsePromise2.default.as(user); + } + return DefaultController.setCurrentUser(user); + }); + }, + become: function (options) { + var user = new ParseUser(); + var RESTController = _CoreManager2.default.getRESTController(); + return RESTController.request('GET', 'users/me', {}, options).then(function (response, status) { + user._finishFetch(response); + user._setExisted(true); + return DefaultController.setCurrentUser(user); + }); + }, + logOut: function () { + return DefaultController.currentUserAsync().then(function (currentUser) { + var path = _Storage2.default.generatePath(CURRENT_USER_KEY); + var promise = _Storage2.default.removeItemAsync(path); + var RESTController = _CoreManager2.default.getRESTController(); + if (currentUser !== null) { + var currentSession = currentUser.getSessionToken(); + if (currentSession && (0, _isRevocableSession2.default)(currentSession)) { + promise = promise.then(function () { + return RESTController.request('POST', 'logout', {}, { sessionToken: currentSession }); + }); + } + currentUser._logOutWithAll(); + currentUser._finishFetch({ sessionToken: undefined }); + } + currentUserCacheMatchesDisk = true; + currentUserCache = null; + + return promise; + }); + }, + requestPasswordReset: function (email, options) { + var RESTController = _CoreManager2.default.getRESTController(); + return RESTController.request('POST', 'requestPasswordReset', { email: email }, options); + }, + upgradeToRevocableSession: function (user, options) { + var token = user.getSessionToken(); + if (!token) { + return _ParsePromise2.default.error(new _ParseError2.default(_ParseError2.default.SESSION_MISSING, 'Cannot upgrade a user with no session token')); + } + + options.sessionToken = token; + + var RESTController = _CoreManager2.default.getRESTController(); + return RESTController.request('POST', 'upgradeToRevocableSession', {}, options).then(function (result) { + var session = new _ParseSession2.default(); + session._finishFetch(result); + user._finishFetch({ sessionToken: session.getSessionToken() }); + if (user.isCurrent()) { + return DefaultController.setCurrentUser(user); + } + return _ParsePromise2.default.as(user); + }); + }, + linkWith: function (user, authData) { + return user.save({ authData: authData }).then(function () { + if (canUseCurrentUser) { + return DefaultController.setCurrentUser(user); + } + return user; + }); + } +}; + +_CoreManager2.default.setUserController(DefaultController); \ No newline at end of file diff --git a/lib/browser/Push.js b/lib/browser/Push.js new file mode 100644 index 000000000..cfb740767 --- /dev/null +++ b/lib/browser/Push.js @@ -0,0 +1,98 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _typeof2 = require('babel-runtime/helpers/typeof'); + +var _typeof3 = _interopRequireDefault(_typeof2); + +exports.send = send; + +var _CoreManager = require('./CoreManager'); + +var _CoreManager2 = _interopRequireDefault(_CoreManager); + +var _ParseQuery = require('./ParseQuery'); + +var _ParseQuery2 = _interopRequireDefault(_ParseQuery); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { default: obj }; +} + +/** + * Contains functions to deal with Push in Parse. + * @class Parse.Push + * @static + */ + +/** + * Sends a push notification. + * @method send + * @param {Object} data - The data of the push notification. Valid fields + * are: + *+ * var dimensions = { + * gender: 'm', + * source: 'web', + * dayType: 'weekend' + * }; + * Parse.Analytics.track('signup', dimensions); + *+ * + * There is a default limit of 8 dimensions per event tracked. + * + * @method track + * @param {String} name The name of the custom event to report to Parse as + * having happened. + * @param {Object} dimensions The dictionary of information by which to + * segment this event. + * @param {Object} options A Backbone-style callback object. + * @return {Parse.Promise} A promise that is resolved when the round-trip + * to the server completes. + */ +function track(name, dimensions, options) { + name = name || ''; + name = name.replace(/^\s*/, ''); + name = name.replace(/\s*$/, ''); + if (name.length === 0) { + throw new TypeError('A name for the custom event must be provided'); + } + + for (var key in dimensions) { + if (typeof key !== 'string' || typeof dimensions[key] !== 'string') { + throw new TypeError('track() dimensions expects keys and values of type "string".'); + } + } + + options = options || {}; + return _CoreManager2.default.getAnalyticsController().track(name, dimensions)._thenRunCallbacks(options); +} /** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + */ + +var DefaultController = { + track: function (name, dimensions) { + var RESTController = _CoreManager2.default.getRESTController(); + return RESTController.request('POST', 'events/' + name, { dimensions: dimensions }); + } +}; + +_CoreManager2.default.setAnalyticsController(DefaultController); \ No newline at end of file diff --git a/lib/node/Cloud.js b/lib/node/Cloud.js new file mode 100644 index 000000000..72c333750 --- /dev/null +++ b/lib/node/Cloud.js @@ -0,0 +1,109 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.run = run; + +var _CoreManager = require('./CoreManager'); + +var _CoreManager2 = _interopRequireDefault(_CoreManager); + +var _decode = require('./decode'); + +var _decode2 = _interopRequireDefault(_decode); + +var _encode = require('./encode'); + +var _encode2 = _interopRequireDefault(_encode); + +var _ParseError = require('./ParseError'); + +var _ParseError2 = _interopRequireDefault(_ParseError); + +var _ParsePromise = require('./ParsePromise'); + +var _ParsePromise2 = _interopRequireDefault(_ParsePromise); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { default: obj }; +} + +/** + * Contains functions for calling and declaring + * cloud functions. + *
+ * Some functions are only available from Cloud Code. + *
+ * + * @class Parse.Cloud + * @static + */ + +/** + * Makes a call to a cloud function. + * @method run + * @param {String} name The function name. + * @param {Object} data The parameters to send to the cloud function. + * @param {Object} options A Backbone-style options object + * options.success, if set, should be a function to handle a successful + * call to a cloud function. options.error should be a function that + * handles an error running the cloud function. Both functions are + * optional. Both functions take a single argument. + * @return {Parse.Promise} A promise that will be resolved with the result + * of the function. + */ +function run(name, data, options) { + options = options || {}; + + if (typeof name !== 'string' || name.length === 0) { + throw new TypeError('Cloud function name must be a string.'); + } + + var requestOptions = {}; + if (options.useMasterKey) { + requestOptions.useMasterKey = options.useMasterKey; + } + if (options.sessionToken) { + requestOptions.sessionToken = options.sessionToken; + } + + return _CoreManager2.default.getCloudController().run(name, data, requestOptions)._thenRunCallbacks(options); +} /** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + */ + +var DefaultController = { + run: function (name, data, options) { + var RESTController = _CoreManager2.default.getRESTController(); + + var payload = (0, _encode2.default)(data, true); + + var requestOptions = {}; + if (options.hasOwnProperty('useMasterKey')) { + requestOptions.useMasterKey = options.useMasterKey; + } + if (options.hasOwnProperty('sessionToken')) { + requestOptions.sessionToken = options.sessionToken; + } + + var request = RESTController.request('POST', 'functions/' + name, payload, requestOptions); + + return request.then(function (res) { + var decoded = (0, _decode2.default)(res); + if (decoded && decoded.hasOwnProperty('result')) { + return _ParsePromise2.default.as(decoded.result); + } + return _ParsePromise2.default.error(new _ParseError2.default(_ParseError2.default.INVALID_JSON, 'The server returned an invalid response.')); + })._thenRunCallbacks(options); + } +}; + +_CoreManager2.default.setCloudController(DefaultController); \ No newline at end of file diff --git a/lib/node/CoreManager.js b/lib/node/CoreManager.js new file mode 100644 index 000000000..20d66a5f1 --- /dev/null +++ b/lib/node/CoreManager.js @@ -0,0 +1,161 @@ +'use strict'; + +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + */ + +var config = { + // Defaults + IS_NODE: typeof process !== 'undefined' && !!process.versions && !!process.versions.node && !process.versions.electron, + REQUEST_ATTEMPT_LIMIT: 5, + SERVER_URL: 'https://api.parse.com/1', + LIVEQUERY_SERVER_URL: null, + VERSION: 'js' + '1.9.2', + APPLICATION_ID: null, + JAVASCRIPT_KEY: null, + MASTER_KEY: null, + USE_MASTER_KEY: false, + PERFORM_USER_REWRITE: true, + FORCE_REVOCABLE_SESSION: false +}; + +function requireMethods(name, methods, controller) { + methods.forEach(function (func) { + if (typeof controller[func] !== 'function') { + throw new Error(name + ' must implement ' + func + '()'); + } + }); +} + +module.exports = { + get: function (key) { + if (config.hasOwnProperty(key)) { + return config[key]; + } + throw new Error('Configuration key not found: ' + key); + }, + + set: function (key, value) { + config[key] = value; + }, + + /* Specialized Controller Setters/Getters */ + + setAnalyticsController: function (controller) { + requireMethods('AnalyticsController', ['track'], controller); + config['AnalyticsController'] = controller; + }, + getAnalyticsController: function () { + return config['AnalyticsController']; + }, + setCloudController: function (controller) { + requireMethods('CloudController', ['run'], controller); + config['CloudController'] = controller; + }, + getCloudController: function () { + return config['CloudController']; + }, + setConfigController: function (controller) { + requireMethods('ConfigController', ['current', 'get'], controller); + config['ConfigController'] = controller; + }, + getConfigController: function () { + return config['ConfigController']; + }, + setFileController: function (controller) { + requireMethods('FileController', ['saveFile', 'saveBase64'], controller); + config['FileController'] = controller; + }, + getFileController: function () { + return config['FileController']; + }, + setInstallationController: function (controller) { + requireMethods('InstallationController', ['currentInstallationId'], controller); + config['InstallationController'] = controller; + }, + getInstallationController: function () { + return config['InstallationController']; + }, + setObjectController: function (controller) { + requireMethods('ObjectController', ['save', 'fetch', 'destroy'], controller); + config['ObjectController'] = controller; + }, + getObjectController: function () { + return config['ObjectController']; + }, + setObjectStateController: function (controller) { + requireMethods('ObjectStateController', ['getState', 'initializeState', 'removeState', 'getServerData', 'setServerData', 'getPendingOps', 'setPendingOp', 'pushPendingState', 'popPendingState', 'mergeFirstPendingState', 'getObjectCache', 'estimateAttribute', 'estimateAttributes', 'commitServerChanges', 'enqueueTask', 'clearAllState'], controller); + + config['ObjectStateController'] = controller; + }, + getObjectStateController: function () { + return config['ObjectStateController']; + }, + setPushController: function (controller) { + requireMethods('PushController', ['send'], controller); + config['PushController'] = controller; + }, + getPushController: function () { + return config['PushController']; + }, + setQueryController: function (controller) { + requireMethods('QueryController', ['find'], controller); + config['QueryController'] = controller; + }, + getQueryController: function () { + return config['QueryController']; + }, + setRESTController: function (controller) { + requireMethods('RESTController', ['request', 'ajax'], controller); + config['RESTController'] = controller; + }, + getRESTController: function () { + return config['RESTController']; + }, + setSessionController: function (controller) { + requireMethods('SessionController', ['getSession'], controller); + config['SessionController'] = controller; + }, + getSessionController: function () { + return config['SessionController']; + }, + setStorageController: function (controller) { + if (controller.async) { + requireMethods('An async StorageController', ['getItemAsync', 'setItemAsync', 'removeItemAsync'], controller); + } else { + requireMethods('A synchronous StorageController', ['getItem', 'setItem', 'removeItem'], controller); + } + config['StorageController'] = controller; + }, + getStorageController: function () { + return config['StorageController']; + }, + setUserController: function (controller) { + requireMethods('UserController', ['setCurrentUser', 'currentUser', 'currentUserAsync', 'signUp', 'logIn', 'become', 'logOut', 'requestPasswordReset', 'upgradeToRevocableSession', 'linkWith'], controller); + config['UserController'] = controller; + }, + getUserController: function () { + return config['UserController']; + }, + setLiveQueryController: function (controller) { + requireMethods('LiveQueryController', ['subscribe', 'unsubscribe', 'open', 'close'], controller); + config['LiveQueryController'] = controller; + }, + getLiveQueryController: function () { + return config['LiveQueryController']; + }, + setHooksController: function (controller) { + requireMethods('HooksController', ['create', 'get', 'update', 'remove'], controller); + config['HooksController'] = controller; + }, + getHooksController: function () { + return config['HooksController']; + } +}; \ No newline at end of file diff --git a/lib/node/EventEmitter.js b/lib/node/EventEmitter.js new file mode 100644 index 000000000..e9414f0bf --- /dev/null +++ b/lib/node/EventEmitter.js @@ -0,0 +1,15 @@ +'use strict'; + +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * This is a simple wrapper to unify EventEmitter implementations across platforms. + */ + +module.exports = require('events').EventEmitter; +var EventEmitter; \ No newline at end of file diff --git a/lib/node/FacebookUtils.js b/lib/node/FacebookUtils.js new file mode 100644 index 000000000..bb8b7ef51 --- /dev/null +++ b/lib/node/FacebookUtils.js @@ -0,0 +1,243 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _parseDate = require('./parseDate'); + +var _parseDate2 = _interopRequireDefault(_parseDate); + +var _ParseUser = require('./ParseUser'); + +var _ParseUser2 = _interopRequireDefault(_ParseUser); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { default: obj }; +} + +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * -weak + */ + +var PUBLIC_KEY = "*"; + +var initialized = false; +var requestedPermissions; +var initOptions; +var provider = { + authenticate: function (options) { + var _this = this; + + if (typeof FB === 'undefined') { + options.error(this, 'Facebook SDK not found.'); + } + FB.login(function (response) { + if (response.authResponse) { + if (options.success) { + options.success(_this, { + id: response.authResponse.userID, + access_token: response.authResponse.accessToken, + expiration_date: new Date(response.authResponse.expiresIn * 1000 + new Date().getTime()).toJSON() + }); + } + } else { + if (options.error) { + options.error(_this, response); + } + } + }, { + scope: requestedPermissions + }); + }, + restoreAuthentication: function (authData) { + if (authData) { + var expiration = (0, _parseDate2.default)(authData.expiration_date); + var expiresIn = expiration ? (expiration.getTime() - new Date().getTime()) / 1000 : 0; + + var authResponse = { + userID: authData.id, + accessToken: authData.access_token, + expiresIn: expiresIn + }; + var newOptions = {}; + if (initOptions) { + for (var key in initOptions) { + newOptions[key] = initOptions[key]; + } + } + newOptions.authResponse = authResponse; + + // Suppress checks for login status from the browser. + newOptions.status = false; + + // If the user doesn't match the one known by the FB SDK, log out. + // Most of the time, the users will match -- it's only in cases where + // the FB SDK knows of a different user than the one being restored + // from a Parse User that logged in with username/password. + var existingResponse = FB.getAuthResponse(); + if (existingResponse && existingResponse.userID !== authResponse.userID) { + FB.logout(); + } + + FB.init(newOptions); + } + return true; + }, + getAuthType: function () { + return 'facebook'; + }, + deauthenticate: function () { + this.restoreAuthentication(null); + } +}; + +/** + * Provides a set of utilities for using Parse with Facebook. + * @class Parse.FacebookUtils + * @static + */ +var FacebookUtils = { + /** + * Initializes Parse Facebook integration. Call this function after you + * have loaded the Facebook Javascript SDK with the same parameters + * as you would pass to
+ *
+ * FB.init()
. Parse.FacebookUtils will invoke FB.init() for you
+ * with these arguments.
+ *
+ * @method init
+ * @param {Object} options Facebook options argument as described here:
+ *
+ * FB.init(). The status flag will be coerced to 'false' because it
+ * interferes with Parse Facebook integration. Call FB.getLoginStatus()
+ * explicitly if this behavior is required by your application.
+ */
+ init: function (options) {
+ if (typeof FB === 'undefined') {
+ throw new Error('The Facebook JavaScript SDK must be loaded before calling init.');
+ }
+ initOptions = {};
+ if (options) {
+ for (var key in options) {
+ initOptions[key] = options[key];
+ }
+ }
+ if (initOptions.status && typeof console !== 'undefined') {
+ var warn = console.warn || console.log || function () {};
+ warn.call(console, 'The "status" flag passed into' + ' FB.init, when set to true, can interfere with Parse Facebook' + ' integration, so it has been suppressed. Please call' + ' FB.getLoginStatus() explicitly if you require this behavior.');
+ }
+ initOptions.status = false;
+ FB.init(initOptions);
+ _ParseUser2.default._registerAuthenticationProvider(provider);
+ initialized = true;
+ },
+
+ /**
+ * Gets whether the user has their account linked to Facebook.
+ *
+ * @method isLinked
+ * @param {Parse.User} user User to check for a facebook link.
+ * The user must be logged in on this device.
+ * @return {Boolean} true
if the user has their account
+ * linked to Facebook.
+ */
+ isLinked: function (user) {
+ return user._isLinked('facebook');
+ },
+
+ /**
+ * Logs in a user using Facebook. This method delegates to the Facebook
+ * SDK to authenticate the user, and then automatically logs in (or
+ * creates, in the case where it is a new user) a Parse.User.
+ *
+ * @method logIn
+ * @param {String, Object} permissions The permissions required for Facebook
+ * log in. This is a comma-separated string of permissions.
+ * Alternatively, supply a Facebook authData object as described in our
+ * REST API docs if you want to handle getting facebook auth tokens
+ * yourself.
+ * @param {Object} options Standard options object with success and error
+ * callbacks.
+ */
+ logIn: function (permissions, options) {
+ if (!permissions || typeof permissions === 'string') {
+ if (!initialized) {
+ throw new Error('You must initialize FacebookUtils before calling logIn.');
+ }
+ requestedPermissions = permissions;
+ return _ParseUser2.default._logInWith('facebook', options);
+ } else {
+ var newOptions = {};
+ if (options) {
+ for (var key in options) {
+ newOptions[key] = options[key];
+ }
+ }
+ newOptions.authData = permissions;
+ return _ParseUser2.default._logInWith('facebook', newOptions);
+ }
+ },
+
+ /**
+ * Links Facebook to an existing PFUser. This method delegates to the
+ * Facebook SDK to authenticate the user, and then automatically links
+ * the account to the Parse.User.
+ *
+ * @method link
+ * @param {Parse.User} user User to link to Facebook. This must be the
+ * current user.
+ * @param {String, Object} permissions The permissions required for Facebook
+ * log in. This is a comma-separated string of permissions.
+ * Alternatively, supply a Facebook authData object as described in our
+ * REST API docs if you want to handle getting facebook auth tokens
+ * yourself.
+ * @param {Object} options Standard options object with success and error
+ * callbacks.
+ */
+ link: function (user, permissions, options) {
+ if (!permissions || typeof permissions === 'string') {
+ if (!initialized) {
+ throw new Error('You must initialize FacebookUtils before calling link.');
+ }
+ requestedPermissions = permissions;
+ return user._linkWith('facebook', options);
+ } else {
+ var newOptions = {};
+ if (options) {
+ for (var key in options) {
+ newOptions[key] = options[key];
+ }
+ }
+ newOptions.authData = permissions;
+ return user._linkWith('facebook', newOptions);
+ }
+ },
+
+ /**
+ * Unlinks the Parse.User from a Facebook account.
+ *
+ * @method unlink
+ * @param {Parse.User} user User to unlink from Facebook. This must be the
+ * current user.
+ * @param {Object} options Standard options object with success and error
+ * callbacks.
+ */
+ unlink: function (user, options) {
+ if (!initialized) {
+ throw new Error('You must initialize FacebookUtils before calling unlink.');
+ }
+ return user._unlinkFrom('facebook', options);
+ }
+};
+
+exports.default = FacebookUtils;
\ No newline at end of file
diff --git a/lib/node/InstallationController.js b/lib/node/InstallationController.js
new file mode 100644
index 000000000..7b01e3cad
--- /dev/null
+++ b/lib/node/InstallationController.js
@@ -0,0 +1,64 @@
+'use strict';
+
+var _CoreManager = require('./CoreManager');
+
+var _CoreManager2 = _interopRequireDefault(_CoreManager);
+
+var _ParsePromise = require('./ParsePromise');
+
+var _ParsePromise2 = _interopRequireDefault(_ParsePromise);
+
+var _Storage = require('./Storage');
+
+var _Storage2 = _interopRequireDefault(_Storage);
+
+function _interopRequireDefault(obj) {
+ return obj && obj.__esModule ? obj : { default: obj };
+}
+
+var iidCache = null; /**
+ * Copyright (c) 2015-present, Parse, LLC.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ *
+ *
+ */
+
+function hexOctet() {
+ return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
+}
+
+function generateId() {
+ return hexOctet() + hexOctet() + '-' + hexOctet() + '-' + hexOctet() + '-' + hexOctet() + '-' + hexOctet() + hexOctet() + hexOctet();
+}
+
+var InstallationController = {
+ currentInstallationId: function () {
+ if (typeof iidCache === 'string') {
+ return _ParsePromise2.default.as(iidCache);
+ }
+ var path = _Storage2.default.generatePath('installationId');
+ return _Storage2.default.getItemAsync(path).then(function (iid) {
+ if (!iid) {
+ iid = generateId();
+ return _Storage2.default.setItemAsync(path, iid).then(function () {
+ iidCache = iid;
+ return iid;
+ });
+ }
+ iidCache = iid;
+ return iid;
+ });
+ },
+ _clearCache: function () {
+ iidCache = null;
+ },
+ _setInstallationIdCache: function (iid) {
+ iidCache = iid;
+ }
+};
+
+module.exports = InstallationController;
\ No newline at end of file
diff --git a/lib/node/LiveQueryClient.js b/lib/node/LiveQueryClient.js
new file mode 100644
index 000000000..e0420323f
--- /dev/null
+++ b/lib/node/LiveQueryClient.js
@@ -0,0 +1,595 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+
+var _typeof2 = require('babel-runtime/helpers/typeof');
+
+var _typeof3 = _interopRequireDefault(_typeof2);
+
+var _getIterator2 = require('babel-runtime/core-js/get-iterator');
+
+var _getIterator3 = _interopRequireDefault(_getIterator2);
+
+var _stringify = require('babel-runtime/core-js/json/stringify');
+
+var _stringify2 = _interopRequireDefault(_stringify);
+
+var _map = require('babel-runtime/core-js/map');
+
+var _map2 = _interopRequireDefault(_map);
+
+var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of');
+
+var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
+
+var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
+
+var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
+
+var _createClass2 = require('babel-runtime/helpers/createClass');
+
+var _createClass3 = _interopRequireDefault(_createClass2);
+
+var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn');
+
+var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
+
+var _inherits2 = require('babel-runtime/helpers/inherits');
+
+var _inherits3 = _interopRequireDefault(_inherits2);
+
+var _EventEmitter2 = require('./EventEmitter');
+
+var _EventEmitter3 = _interopRequireDefault(_EventEmitter2);
+
+var _ParsePromise = require('./ParsePromise');
+
+var _ParsePromise2 = _interopRequireDefault(_ParsePromise);
+
+var _ParseObject = require('./ParseObject');
+
+var _ParseObject2 = _interopRequireDefault(_ParseObject);
+
+var _LiveQuerySubscription = require('./LiveQuerySubscription');
+
+var _LiveQuerySubscription2 = _interopRequireDefault(_LiveQuerySubscription);
+
+function _interopRequireDefault(obj) {
+ return obj && obj.__esModule ? obj : { default: obj };
+}
+
+// The LiveQuery client inner state
+/**
+ * Copyright (c) 2015-present, Parse, LLC.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+var CLIENT_STATE = {
+ INITIALIZED: 'initialized',
+ CONNECTING: 'connecting',
+ CONNECTED: 'connected',
+ CLOSED: 'closed',
+ RECONNECTING: 'reconnecting',
+ DISCONNECTED: 'disconnected'
+};
+
+// The event type the LiveQuery client should sent to server
+var OP_TYPES = {
+ CONNECT: 'connect',
+ SUBSCRIBE: 'subscribe',
+ UNSUBSCRIBE: 'unsubscribe',
+ ERROR: 'error'
+};
+
+// The event we get back from LiveQuery server
+var OP_EVENTS = {
+ CONNECTED: 'connected',
+ SUBSCRIBED: 'subscribed',
+ UNSUBSCRIBED: 'unsubscribed',
+ ERROR: 'error',
+ CREATE: 'create',
+ UPDATE: 'update',
+ ENTER: 'enter',
+ LEAVE: 'leave',
+ DELETE: 'delete'
+};
+
+// The event the LiveQuery client should emit
+var CLIENT_EMMITER_TYPES = {
+ CLOSE: 'close',
+ ERROR: 'error',
+ OPEN: 'open'
+};
+
+// The event the LiveQuery subscription should emit
+var SUBSCRIPTION_EMMITER_TYPES = {
+ OPEN: 'open',
+ CLOSE: 'close',
+ ERROR: 'error',
+ CREATE: 'create',
+ UPDATE: 'update',
+ ENTER: 'enter',
+ LEAVE: 'leave',
+ DELETE: 'delete'
+};
+
+var generateInterval = function (k) {
+ return Math.random() * Math.min(30, Math.pow(2, k) - 1) * 1000;
+};
+
+/**
+ * Creates a new LiveQueryClient.
+ * Extends events.EventEmitter
+ * cloud functions.
+ *
+ * A wrapper of a standard WebSocket client. We add several useful methods to
+ * help you connect/disconnect to LiveQueryServer, subscribe/unsubscribe a ParseQuery easily.
+ *
+ * javascriptKey and masterKey are used for verifying the LiveQueryClient when it tries
+ * to connect to the LiveQuery server
+ *
+ * @class Parse.LiveQueryClient
+ * @constructor
+ * @param {Object} options
+ * @param {string} options.applicationId - applicationId of your Parse app
+ * @param {string} options.serverURL - the URL of your LiveQuery server
+ * @param {string} options.javascriptKey (optional)
+ * @param {string} options.masterKey (optional) Your Parse Master Key. (Node.js only!)
+ * @param {string} options.sessionToken (optional)
+ *
+ *
+ * We expose three events to help you monitor the status of the LiveQueryClient.
+ *
+ * + * let Parse = require('parse/node'); + * let LiveQueryClient = Parse.LiveQueryClient; + * let client = new LiveQueryClient({ + * applicationId: '', + * serverURL: '', + * javascriptKey: '', + * masterKey: '' + * }); + *+ * + * Open - When we establish the WebSocket connection to the LiveQuery server, you'll get this event. + *
+ * client.on('open', () => { + * + * });+ * + * Close - When we lose the WebSocket connection to the LiveQuery server, you'll get this event. + *
+ * client.on('close', () => { + * + * });+ * + * Error - When some network error or LiveQuery server error happens, you'll get this event. + *
+ * client.on('error', (error) => { + * + * });+ * + * + */ + +var LiveQueryClient = function (_EventEmitter) { + (0, _inherits3.default)(LiveQueryClient, _EventEmitter); + + function LiveQueryClient(_ref) { + var applicationId = _ref.applicationId, + serverURL = _ref.serverURL, + javascriptKey = _ref.javascriptKey, + masterKey = _ref.masterKey, + sessionToken = _ref.sessionToken; + (0, _classCallCheck3.default)(this, LiveQueryClient); + + var _this = (0, _possibleConstructorReturn3.default)(this, (LiveQueryClient.__proto__ || (0, _getPrototypeOf2.default)(LiveQueryClient)).call(this)); + + if (!serverURL || serverURL.indexOf('ws') !== 0) { + throw new Error('You need to set a proper Parse LiveQuery server url before using LiveQueryClient'); + } + + _this.reconnectHandle = null; + _this.attempts = 1;; + _this.id = 0; + _this.requestId = 1; + _this.serverURL = serverURL; + _this.applicationId = applicationId; + _this.javascriptKey = javascriptKey; + _this.masterKey = masterKey; + _this.sessionToken = sessionToken; + _this.connectPromise = new _ParsePromise2.default(); + _this.subscriptions = new _map2.default(); + _this.state = CLIENT_STATE.INITIALIZED; + return _this; + } + + (0, _createClass3.default)(LiveQueryClient, [{ + key: 'shouldOpen', + value: function () { + return this.state === CLIENT_STATE.INITIALIZED || this.state === CLIENT_STATE.DISCONNECTED; + } + + /** + * Subscribes to a ParseQuery + * + * If you provide the sessionToken, when the LiveQuery server gets ParseObject's + * updates from parse server, it'll try to check whether the sessionToken fulfills + * the ParseObject's ACL. The LiveQuery server will only send updates to clients whose + * sessionToken is fit for the ParseObject's ACL. You can check the LiveQuery protocol + * here for more details. The subscription you get is the same subscription you get + * from our Standard API. + * + * @method subscribe + * @param {Object} query - the ParseQuery you want to subscribe to + * @param {string} sessionToken (optional) + * @return {Object} subscription + */ + + }, { + key: 'subscribe', + value: function (query, sessionToken) { + var _this2 = this; + + if (!query) { + return; + } + var where = query.toJSON().where; + var className = query.className; + var subscribeRequest = { + op: OP_TYPES.SUBSCRIBE, + requestId: this.requestId, + query: { + className: className, + where: where + } + }; + + if (sessionToken) { + subscribeRequest.sessionToken = sessionToken; + } + + var subscription = new _LiveQuerySubscription2.default(this.requestId, query, sessionToken); + this.subscriptions.set(this.requestId, subscription); + this.requestId += 1; + this.connectPromise.then(function () { + _this2.socket.send((0, _stringify2.default)(subscribeRequest)); + }); + + // adding listener so process does not crash + // best practice is for developer to register their own listener + subscription.on('error', function () {}); + + return subscription; + } + + /** + * After calling unsubscribe you'll stop receiving events from the subscription object. + * + * @method unsubscribe + * @param {Object} subscription - subscription you would like to unsubscribe from. + */ + + }, { + key: 'unsubscribe', + value: function (subscription) { + var _this3 = this; + + if (!subscription) { + return; + } + + this.subscriptions.delete(subscription.id); + var unsubscribeRequest = { + op: OP_TYPES.UNSUBSCRIBE, + requestId: subscription.id + }; + this.connectPromise.then(function () { + _this3.socket.send((0, _stringify2.default)(unsubscribeRequest)); + }); + } + + /** + * After open is called, the LiveQueryClient will try to send a connect request + * to the LiveQuery server. + * + * @method open + */ + + }, { + key: 'open', + value: function () { + var _this4 = this; + + var WebSocketImplementation = this._getWebSocketImplementation(); + if (!WebSocketImplementation) { + this.emit(CLIENT_EMMITER_TYPES.ERROR, 'Can not find WebSocket implementation'); + return; + } + + if (this.state !== CLIENT_STATE.RECONNECTING) { + this.state = CLIENT_STATE.CONNECTING; + } + + // Get WebSocket implementation + this.socket = new WebSocketImplementation(this.serverURL); + + // Bind WebSocket callbacks + this.socket.onopen = function () { + _this4._handleWebSocketOpen(); + }; + + this.socket.onmessage = function (event) { + _this4._handleWebSocketMessage(event); + }; + + this.socket.onclose = function () { + _this4._handleWebSocketClose(); + }; + + this.socket.onerror = function (error) { + _this4._handleWebSocketError(error); + }; + } + }, { + key: 'resubscribe', + value: function () { + var _this5 = this; + + this.subscriptions.forEach(function (subscription, requestId) { + var query = subscription.query; + var where = query.toJSON().where; + var className = query.className; + var sessionToken = subscription.sessionToken; + var subscribeRequest = { + op: OP_TYPES.SUBSCRIBE, + requestId: requestId, + query: { + className: className, + where: where + } + }; + + if (sessionToken) { + subscribeRequest.sessionToken = sessionToken; + } + + _this5.connectPromise.then(function () { + _this5.socket.send((0, _stringify2.default)(subscribeRequest)); + }); + }); + } + + /** + * This method will close the WebSocket connection to this LiveQueryClient, + * cancel the auto reconnect and unsubscribe all subscriptions based on it. + * + * @method close + */ + + }, { + key: 'close', + value: function () { + if (this.state === CLIENT_STATE.INITIALIZED || this.state === CLIENT_STATE.DISCONNECTED) { + return; + } + this.state = CLIENT_STATE.DISCONNECTED; + this.socket.close(); + // Notify each subscription about the close + var _iteratorNormalCompletion = true; + var _didIteratorError = false; + var _iteratorError = undefined; + + try { + for (var _iterator = (0, _getIterator3.default)(this.subscriptions.values()), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { + var subscription = _step.value; + + subscription.emit(SUBSCRIPTION_EMMITER_TYPES.CLOSE); + } + } catch (err) { + _didIteratorError = true; + _iteratorError = err; + } finally { + try { + if (!_iteratorNormalCompletion && _iterator.return) { + _iterator.return(); + } + } finally { + if (_didIteratorError) { + throw _iteratorError; + } + } + } + + this._handleReset(); + this.emit(CLIENT_EMMITER_TYPES.CLOSE); + } + }, { + key: '_getWebSocketImplementation', + value: function () { + return require('ws'); + } + + // ensure we start with valid state if connect is called again after close + + }, { + key: '_handleReset', + value: function () { + this.attempts = 1;; + this.id = 0; + this.requestId = 1; + this.connectPromise = new _ParsePromise2.default(); + this.subscriptions = new _map2.default(); + } + }, { + key: '_handleWebSocketOpen', + value: function () { + this.attempts = 1; + var connectRequest = { + op: OP_TYPES.CONNECT, + applicationId: this.applicationId, + javascriptKey: this.javascriptKey, + masterKey: this.masterKey, + sessionToken: this.sessionToken + }; + this.socket.send((0, _stringify2.default)(connectRequest)); + } + }, { + key: '_handleWebSocketMessage', + value: function (event) { + var data = event.data; + if (typeof data === 'string') { + data = JSON.parse(data); + } + var subscription = null; + if (data.requestId) { + subscription = this.subscriptions.get(data.requestId); + } + switch (data.op) { + case OP_EVENTS.CONNECTED: + if (this.state === CLIENT_STATE.RECONNECTING) { + this.resubscribe(); + } + this.emit(CLIENT_EMMITER_TYPES.OPEN); + this.id = data.clientId; + this.connectPromise.resolve(); + this.state = CLIENT_STATE.CONNECTED; + break; + case OP_EVENTS.SUBSCRIBED: + if (subscription) { + subscription.emit(SUBSCRIPTION_EMMITER_TYPES.OPEN); + } + break; + case OP_EVENTS.ERROR: + if (data.requestId) { + if (subscription) { + subscription.emit(SUBSCRIPTION_EMMITER_TYPES.ERROR, data.error); + } + } else { + this.emit(CLIENT_EMMITER_TYPES.ERROR, data.error); + } + break; + case OP_EVENTS.UNSUBSCRIBED: + // We have already deleted subscription in unsubscribe(), do nothing here + break; + default: + // create, update, enter, leave, delete cases + var className = data.object.className; + // Delete the extrea __type and className fields during transfer to full JSON + delete data.object.__type; + delete data.object.className; + var parseObject = new _ParseObject2.default(className); + parseObject._finishFetch(data.object); + if (!subscription) { + break; + } + subscription.emit(data.op, parseObject); + } + } + }, { + key: '_handleWebSocketClose', + value: function () { + if (this.state === CLIENT_STATE.DISCONNECTED) { + return; + } + this.state = CLIENT_STATE.CLOSED; + this.emit(CLIENT_EMMITER_TYPES.CLOSE); + // Notify each subscription about the close + var _iteratorNormalCompletion2 = true; + var _didIteratorError2 = false; + var _iteratorError2 = undefined; + + try { + for (var _iterator2 = (0, _getIterator3.default)(this.subscriptions.values()), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { + var subscription = _step2.value; + + subscription.emit(SUBSCRIPTION_EMMITER_TYPES.CLOSE); + } + } catch (err) { + _didIteratorError2 = true; + _iteratorError2 = err; + } finally { + try { + if (!_iteratorNormalCompletion2 && _iterator2.return) { + _iterator2.return(); + } + } finally { + if (_didIteratorError2) { + throw _iteratorError2; + } + } + } + + this._handleReconnect(); + } + }, { + key: '_handleWebSocketError', + value: function (error) { + this.emit(CLIENT_EMMITER_TYPES.ERROR, error); + var _iteratorNormalCompletion3 = true; + var _didIteratorError3 = false; + var _iteratorError3 = undefined; + + try { + for (var _iterator3 = (0, _getIterator3.default)(this.subscriptions.values()), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) { + var subscription = _step3.value; + + subscription.emit(SUBSCRIPTION_EMMITER_TYPES.ERROR); + } + } catch (err) { + _didIteratorError3 = true; + _iteratorError3 = err; + } finally { + try { + if (!_iteratorNormalCompletion3 && _iterator3.return) { + _iterator3.return(); + } + } finally { + if (_didIteratorError3) { + throw _iteratorError3; + } + } + } + + this._handleReconnect(); + } + }, { + key: '_handleReconnect', + value: function () { + var _this6 = this; + + // if closed or currently reconnecting we stop attempting to reconnect + if (this.state === CLIENT_STATE.DISCONNECTED) { + return; + } + + this.state = CLIENT_STATE.RECONNECTING; + var time = generateInterval(this.attempts); + + // handle case when both close/error occur at frequent rates we ensure we do not reconnect unnecessarily. + // we're unable to distinguish different between close/error when we're unable to reconnect therefore + // we try to reonnect in both cases + // server side ws and browser WebSocket behave differently in when close/error get triggered + + if (this.reconnectHandle) { + clearTimeout(this.reconnectHandle); + } + + this.reconnectHandle = setTimeout(function () { + _this6.attempts++; + _this6.connectPromise = new _ParsePromise2.default(); + _this6.open(); + }.bind(this), time); + } + }]); + return LiveQueryClient; +}(_EventEmitter3.default); + +exports.default = LiveQueryClient; \ No newline at end of file diff --git a/lib/node/LiveQuerySubscription.js b/lib/node/LiveQuerySubscription.js new file mode 100644 index 000000000..4078b65d5 --- /dev/null +++ b/lib/node/LiveQuerySubscription.js @@ -0,0 +1,162 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); + +var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); + +var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); + +var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); + +var _createClass2 = require('babel-runtime/helpers/createClass'); + +var _createClass3 = _interopRequireDefault(_createClass2); + +var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); + +var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); + +var _inherits2 = require('babel-runtime/helpers/inherits'); + +var _inherits3 = _interopRequireDefault(_inherits2); + +var _EventEmitter2 = require('./EventEmitter'); + +var _EventEmitter3 = _interopRequireDefault(_EventEmitter2); + +var _CoreManager = require('./CoreManager'); + +var _CoreManager2 = _interopRequireDefault(_CoreManager); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { default: obj }; +} + +/** + * Creates a new LiveQuery Subscription. + * Extends events.EventEmitter + * cloud functions. + * + * @constructor + * @param {string} id - subscription id + * @param {string} query - query to subscribe to + * @param {string} sessionToken - optional session token + * + *
Open Event - When you call query.subscribe(), we send a subscribe request to + * the LiveQuery server, when we get the confirmation from the LiveQuery server, + * this event will be emitted. When the client loses WebSocket connection to the + * LiveQuery server, we will try to auto reconnect the LiveQuery server. If we + * reconnect the LiveQuery server and successfully resubscribe the ParseQuery, + * you'll also get this event. + * + *
+ * subscription.on('open', () => { + * + * });+ * + *
Create Event - When a new ParseObject is created and it fulfills the ParseQuery you subscribe, + * you'll get this event. The object is the ParseObject which is created. + * + *
+ * subscription.on('create', (object) => { + * + * });+ * + *
Update Event - When an existing ParseObject which fulfills the ParseQuery you subscribe + * is updated (The ParseObject fulfills the ParseQuery before and after changes), + * you'll get this event. The object is the ParseObject which is updated. + * Its content is the latest value of the ParseObject. + * + *
+ * subscription.on('update', (object) => { + * + * });+ * + *
Enter Event - When an existing ParseObject's old value doesn't fulfill the ParseQuery + * but its new value fulfills the ParseQuery, you'll get this event. The object is the + * ParseObject which enters the ParseQuery. Its content is the latest value of the ParseObject. + * + *
+ * subscription.on('enter', (object) => { + * + * });+ * + * + *
Update Event - When an existing ParseObject's old value fulfills the ParseQuery but its new value + * doesn't fulfill the ParseQuery, you'll get this event. The object is the ParseObject + * which leaves the ParseQuery. Its content is the latest value of the ParseObject. + * + *
+ * subscription.on('leave', (object) => { + * + * });+ * + * + *
Delete Event - When an existing ParseObject which fulfills the ParseQuery is deleted, you'll + * get this event. The object is the ParseObject which is deleted. + * + *
+ * subscription.on('delete', (object) => { + * + * });+ * + * + *
Close Event - When the client loses the WebSocket connection to the LiveQuery + * server and we stop receiving events, you'll get this event. + * + *
+ * subscription.on('close', () => { + * + * });+ * + * + */ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ + +var Subscription = function (_EventEmitter) { + (0, _inherits3.default)(Subscription, _EventEmitter); + + function Subscription(id, query, sessionToken) { + (0, _classCallCheck3.default)(this, Subscription); + + var _this2 = (0, _possibleConstructorReturn3.default)(this, (Subscription.__proto__ || (0, _getPrototypeOf2.default)(Subscription)).call(this)); + + _this2.id = id; + _this2.query = query; + _this2.sessionToken = sessionToken; + return _this2; + } + + /** + * @method unsubscribe + */ + + (0, _createClass3.default)(Subscription, [{ + key: 'unsubscribe', + value: function () { + var _this3 = this; + + var _this = this; + _CoreManager2.default.getLiveQueryController().getDefaultLiveQueryClient().then(function (liveQueryClient) { + liveQueryClient.unsubscribe(_this); + _this.emit('close'); + _this3.resolve(); + }); + } + }]); + return Subscription; +}(_EventEmitter3.default); + +exports.default = Subscription; \ No newline at end of file diff --git a/lib/node/ObjectStateMutations.js b/lib/node/ObjectStateMutations.js new file mode 100644 index 000000000..b476a0e2f --- /dev/null +++ b/lib/node/ObjectStateMutations.js @@ -0,0 +1,165 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _stringify = require('babel-runtime/core-js/json/stringify'); + +var _stringify2 = _interopRequireDefault(_stringify); + +var _typeof2 = require('babel-runtime/helpers/typeof'); + +var _typeof3 = _interopRequireDefault(_typeof2); + +exports.defaultState = defaultState; +exports.setServerData = setServerData; +exports.setPendingOp = setPendingOp; +exports.pushPendingState = pushPendingState; +exports.popPendingState = popPendingState; +exports.mergeFirstPendingState = mergeFirstPendingState; +exports.estimateAttribute = estimateAttribute; +exports.estimateAttributes = estimateAttributes; +exports.commitServerChanges = commitServerChanges; + +var _encode = require('./encode'); + +var _encode2 = _interopRequireDefault(_encode); + +var _ParseFile = require('./ParseFile'); + +var _ParseFile2 = _interopRequireDefault(_ParseFile); + +var _ParseObject = require('./ParseObject'); + +var _ParseObject2 = _interopRequireDefault(_ParseObject); + +var _ParsePromise = require('./ParsePromise'); + +var _ParsePromise2 = _interopRequireDefault(_ParsePromise); + +var _ParseRelation = require('./ParseRelation'); + +var _ParseRelation2 = _interopRequireDefault(_ParseRelation); + +var _TaskQueue = require('./TaskQueue'); + +var _TaskQueue2 = _interopRequireDefault(_TaskQueue); + +var _ParseOp = require('./ParseOp'); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { default: obj }; +} + +function defaultState() { + return { + serverData: {}, + pendingOps: [{}], + objectCache: {}, + tasks: new _TaskQueue2.default(), + existed: false + }; +} /** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + */ + +function setServerData(serverData, attributes) { + for (var _attr in attributes) { + if (typeof attributes[_attr] !== 'undefined') { + serverData[_attr] = attributes[_attr]; + } else { + delete serverData[_attr]; + } + } +} + +function setPendingOp(pendingOps, attr, op) { + var last = pendingOps.length - 1; + if (op) { + pendingOps[last][attr] = op; + } else { + delete pendingOps[last][attr]; + } +} + +function pushPendingState(pendingOps) { + pendingOps.push({}); +} + +function popPendingState(pendingOps) { + var first = pendingOps.shift(); + if (!pendingOps.length) { + pendingOps[0] = {}; + } + return first; +} + +function mergeFirstPendingState(pendingOps) { + var first = popPendingState(pendingOps); + var next = pendingOps[0]; + for (var _attr2 in first) { + if (next[_attr2] && first[_attr2]) { + var merged = next[_attr2].mergeWith(first[_attr2]); + if (merged) { + next[_attr2] = merged; + } + } else { + next[_attr2] = first[_attr2]; + } + } +} + +function estimateAttribute(serverData, pendingOps, className, id, attr) { + var value = serverData[attr]; + for (var i = 0; i < pendingOps.length; i++) { + if (pendingOps[i][attr]) { + if (pendingOps[i][attr] instanceof _ParseOp.RelationOp) { + if (id) { + value = pendingOps[i][attr].applyTo(value, { className: className, id: id }, attr); + } + } else { + value = pendingOps[i][attr].applyTo(value); + } + } + } + return value; +} + +function estimateAttributes(serverData, pendingOps, className, id) { + var data = {}; + var attr = void 0; + for (attr in serverData) { + data[attr] = serverData[attr]; + } + for (var i = 0; i < pendingOps.length; i++) { + for (attr in pendingOps[i]) { + if (pendingOps[i][attr] instanceof _ParseOp.RelationOp) { + if (id) { + data[attr] = pendingOps[i][attr].applyTo(data[attr], { className: className, id: id }, attr); + } + } else { + data[attr] = pendingOps[i][attr].applyTo(data[attr]); + } + } + } + return data; +} + +function commitServerChanges(serverData, objectCache, changes) { + for (var _attr3 in changes) { + var val = changes[_attr3]; + serverData[_attr3] = val; + if (val && (typeof val === 'undefined' ? 'undefined' : (0, _typeof3.default)(val)) === 'object' && !(val instanceof _ParseObject2.default) && !(val instanceof _ParseFile2.default) && !(val instanceof _ParseRelation2.default)) { + var json = (0, _encode2.default)(val, false, true); + objectCache[_attr3] = (0, _stringify2.default)(json); + } + } +} \ No newline at end of file diff --git a/lib/node/Parse.js b/lib/node/Parse.js new file mode 100644 index 000000000..280cd4015 --- /dev/null +++ b/lib/node/Parse.js @@ -0,0 +1,190 @@ +'use strict'; + +var _decode = require('./decode'); + +var _decode2 = _interopRequireDefault(_decode); + +var _encode = require('./encode'); + +var _encode2 = _interopRequireDefault(_encode); + +var _CoreManager = require('./CoreManager'); + +var _CoreManager2 = _interopRequireDefault(_CoreManager); + +var _InstallationController = require('./InstallationController'); + +var _InstallationController2 = _interopRequireDefault(_InstallationController); + +var _ParseOp = require('./ParseOp'); + +var ParseOp = _interopRequireWildcard(_ParseOp); + +var _RESTController = require('./RESTController'); + +var _RESTController2 = _interopRequireDefault(_RESTController); + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } else { + var newObj = {};if (obj != null) { + for (var key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; + } + }newObj.default = obj;return newObj; + } +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { default: obj }; +} + +/** + * Contains all Parse API classes and functions. + * @class Parse + * @static + */ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +var Parse = { + /** + * Call this method first to set up your authentication tokens for Parse. + * You can get your keys from the Data Browser on parse.com. + * @method initialize + * @param {String} applicationId Your Parse Application ID. + * @param {String} javaScriptKey (optional) Your Parse JavaScript Key (Not needed for parse-server) + * @param {String} masterKey (optional) Your Parse Master Key. (Node.js only!) + * @static + */ + initialize: function (applicationId, javaScriptKey) { + Parse._initialize(applicationId, javaScriptKey); + }, + _initialize: function (applicationId, javaScriptKey, masterKey) { + _CoreManager2.default.set('APPLICATION_ID', applicationId); + _CoreManager2.default.set('JAVASCRIPT_KEY', javaScriptKey); + _CoreManager2.default.set('MASTER_KEY', masterKey); + _CoreManager2.default.set('USE_MASTER_KEY', false); + } +}; + +/** These legacy setters may eventually be deprecated **/ +Object.defineProperty(Parse, 'applicationId', { + get: function () { + return _CoreManager2.default.get('APPLICATION_ID'); + }, + set: function (value) { + _CoreManager2.default.set('APPLICATION_ID', value); + } +}); +Object.defineProperty(Parse, 'javaScriptKey', { + get: function () { + return _CoreManager2.default.get('JAVASCRIPT_KEY'); + }, + set: function (value) { + _CoreManager2.default.set('JAVASCRIPT_KEY', value); + } +}); +Object.defineProperty(Parse, 'masterKey', { + get: function () { + return _CoreManager2.default.get('MASTER_KEY'); + }, + set: function (value) { + _CoreManager2.default.set('MASTER_KEY', value); + } +}); +Object.defineProperty(Parse, 'serverURL', { + get: function () { + return _CoreManager2.default.get('SERVER_URL'); + }, + set: function (value) { + _CoreManager2.default.set('SERVER_URL', value); + } +}); +Object.defineProperty(Parse, 'liveQueryServerURL', { + get: function () { + return _CoreManager2.default.get('LIVEQUERY_SERVER_URL'); + }, + set: function (value) { + _CoreManager2.default.set('LIVEQUERY_SERVER_URL', value); + } +}); +/** End setters **/ + +Parse.ACL = require('./ParseACL').default; +Parse.Analytics = require('./Analytics'); +Parse.Cloud = require('./Cloud'); +Parse.CoreManager = require('./CoreManager'); +Parse.Config = require('./ParseConfig').default; +Parse.Error = require('./ParseError').default; +Parse.FacebookUtils = require('./FacebookUtils').default; +Parse.File = require('./ParseFile').default; +Parse.GeoPoint = require('./ParseGeoPoint').default; +Parse.Installation = require('./ParseInstallation').default; +Parse.Object = require('./ParseObject').default; +Parse.Op = { + Set: ParseOp.SetOp, + Unset: ParseOp.UnsetOp, + Increment: ParseOp.IncrementOp, + Add: ParseOp.AddOp, + Remove: ParseOp.RemoveOp, + AddUnique: ParseOp.AddUniqueOp, + Relation: ParseOp.RelationOp +}; +Parse.Promise = require('./ParsePromise').default; +Parse.Push = require('./Push'); +Parse.Query = require('./ParseQuery').default; +Parse.Relation = require('./ParseRelation').default; +Parse.Role = require('./ParseRole').default; +Parse.Session = require('./ParseSession').default; +Parse.Storage = require('./Storage'); +Parse.User = require('./ParseUser').default; +Parse.LiveQuery = require('./ParseLiveQuery').default; +Parse.LiveQueryClient = require('./LiveQueryClient').default; + +Parse._request = function () { + for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + return _CoreManager2.default.getRESTController().request.apply(null, args); +}; +Parse._ajax = function () { + for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { + args[_key2] = arguments[_key2]; + } + + return _CoreManager2.default.getRESTController().ajax.apply(null, args); +}; +// We attempt to match the signatures of the legacy versions of these methods +Parse._decode = function (_, value) { + return (0, _decode2.default)(value); +}; +Parse._encode = function (value, _, disallowObjects) { + return (0, _encode2.default)(value, disallowObjects); +}; +Parse._getInstallationId = function () { + return _CoreManager2.default.getInstallationController().currentInstallationId(); +}; + +_CoreManager2.default.setInstallationController(_InstallationController2.default); +_CoreManager2.default.setRESTController(_RESTController2.default); + +Parse.initialize = Parse._initialize; +Parse.Cloud = Parse.Cloud || {}; +Parse.Cloud.useMasterKey = function () { + _CoreManager2.default.set('USE_MASTER_KEY', true); +}; +Parse.Hooks = require('./ParseHooks'); + +// For legacy requires, of the form `var Parse = require('parse').Parse` +Parse.Parse = Parse; + +module.exports = Parse; \ No newline at end of file diff --git a/lib/node/ParseACL.js b/lib/node/ParseACL.js new file mode 100644 index 000000000..2aa232c18 --- /dev/null +++ b/lib/node/ParseACL.js @@ -0,0 +1,406 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _keys = require('babel-runtime/core-js/object/keys'); + +var _keys2 = _interopRequireDefault(_keys); + +var _typeof2 = require('babel-runtime/helpers/typeof'); + +var _typeof3 = _interopRequireDefault(_typeof2); + +var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); + +var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); + +var _createClass2 = require('babel-runtime/helpers/createClass'); + +var _createClass3 = _interopRequireDefault(_createClass2); + +var _ParseRole = require('./ParseRole'); + +var _ParseRole2 = _interopRequireDefault(_ParseRole); + +var _ParseUser = require('./ParseUser'); + +var _ParseUser2 = _interopRequireDefault(_ParseUser); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { default: obj }; +} + +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + */ + +var PUBLIC_KEY = '*'; + +/** + * Creates a new ACL. + * If no argument is given, the ACL has no permissions for anyone. + * If the argument is a Parse.User, the ACL will have read and write + * permission for only that user. + * If the argument is any other JSON object, that object will be interpretted + * as a serialized ACL created with toJSON(). + * @class Parse.ACL + * @constructor + * + *
An ACL, or Access Control List can be added to any
+ * Parse.Object
to restrict access to only a subset of users
+ * of your application.
Parse.Error
.
+ * @param {String} message A detailed description of the error.
+ */
+var ParseError = function ParseError(code, message) {
+ (0, _classCallCheck3.default)(this, ParseError);
+
+ this.code = code;
+ this.message = message;
+};
+
+/**
+ * Error code indicating some error other than those enumerated here.
+ * @property OTHER_CAUSE
+ * @static
+ * @final
+ */
+
+exports.default = ParseError;
+ParseError.OTHER_CAUSE = -1;
+
+/**
+ * Error code indicating that something has gone wrong with the server.
+ * If you get this error code, it is Parse's fault. Contact us at
+ * https://parse.com/help
+ * @property INTERNAL_SERVER_ERROR
+ * @static
+ * @final
+ */
+ParseError.INTERNAL_SERVER_ERROR = 1;
+
+/**
+ * Error code indicating the connection to the Parse servers failed.
+ * @property CONNECTION_FAILED
+ * @static
+ * @final
+ */
+ParseError.CONNECTION_FAILED = 100;
+
+/**
+ * Error code indicating the specified object doesn't exist.
+ * @property OBJECT_NOT_FOUND
+ * @static
+ * @final
+ */
+ParseError.OBJECT_NOT_FOUND = 101;
+
+/**
+ * Error code indicating you tried to query with a datatype that doesn't
+ * support it, like exact matching an array or object.
+ * @property INVALID_QUERY
+ * @static
+ * @final
+ */
+ParseError.INVALID_QUERY = 102;
+
+/**
+ * Error code indicating a missing or invalid classname. Classnames are
+ * case-sensitive. They must start with a letter, and a-zA-Z0-9_ are the
+ * only valid characters.
+ * @property INVALID_CLASS_NAME
+ * @static
+ * @final
+ */
+ParseError.INVALID_CLASS_NAME = 103;
+
+/**
+ * Error code indicating an unspecified object id.
+ * @property MISSING_OBJECT_ID
+ * @static
+ * @final
+ */
+ParseError.MISSING_OBJECT_ID = 104;
+
+/**
+ * Error code indicating an invalid key name. Keys are case-sensitive. They
+ * must start with a letter, and a-zA-Z0-9_ are the only valid characters.
+ * @property INVALID_KEY_NAME
+ * @static
+ * @final
+ */
+ParseError.INVALID_KEY_NAME = 105;
+
+/**
+ * Error code indicating a malformed pointer. You should not see this unless
+ * you have been mucking about changing internal Parse code.
+ * @property INVALID_POINTER
+ * @static
+ * @final
+ */
+ParseError.INVALID_POINTER = 106;
+
+/**
+ * Error code indicating that badly formed JSON was received upstream. This
+ * either indicates you have done something unusual with modifying how
+ * things encode to JSON, or the network is failing badly.
+ * @property INVALID_JSON
+ * @static
+ * @final
+ */
+ParseError.INVALID_JSON = 107;
+
+/**
+ * Error code indicating that the feature you tried to access is only
+ * available internally for testing purposes.
+ * @property COMMAND_UNAVAILABLE
+ * @static
+ * @final
+ */
+ParseError.COMMAND_UNAVAILABLE = 108;
+
+/**
+ * You must call Parse.initialize before using the Parse library.
+ * @property NOT_INITIALIZED
+ * @static
+ * @final
+ */
+ParseError.NOT_INITIALIZED = 109;
+
+/**
+ * Error code indicating that a field was set to an inconsistent type.
+ * @property INCORRECT_TYPE
+ * @static
+ * @final
+ */
+ParseError.INCORRECT_TYPE = 111;
+
+/**
+ * Error code indicating an invalid channel name. A channel name is either
+ * an empty string (the broadcast channel) or contains only a-zA-Z0-9_
+ * characters and starts with a letter.
+ * @property INVALID_CHANNEL_NAME
+ * @static
+ * @final
+ */
+ParseError.INVALID_CHANNEL_NAME = 112;
+
+/**
+ * Error code indicating that push is misconfigured.
+ * @property PUSH_MISCONFIGURED
+ * @static
+ * @final
+ */
+ParseError.PUSH_MISCONFIGURED = 115;
+
+/**
+ * Error code indicating that the object is too large.
+ * @property OBJECT_TOO_LARGE
+ * @static
+ * @final
+ */
+ParseError.OBJECT_TOO_LARGE = 116;
+
+/**
+ * Error code indicating that the operation isn't allowed for clients.
+ * @property OPERATION_FORBIDDEN
+ * @static
+ * @final
+ */
+ParseError.OPERATION_FORBIDDEN = 119;
+
+/**
+ * Error code indicating the result was not found in the cache.
+ * @property CACHE_MISS
+ * @static
+ * @final
+ */
+ParseError.CACHE_MISS = 120;
+
+/**
+ * Error code indicating that an invalid key was used in a nested
+ * JSONObject.
+ * @property INVALID_NESTED_KEY
+ * @static
+ * @final
+ */
+ParseError.INVALID_NESTED_KEY = 121;
+
+/**
+ * Error code indicating that an invalid filename was used for ParseFile.
+ * A valid file name contains only a-zA-Z0-9_. characters and is between 1
+ * and 128 characters.
+ * @property INVALID_FILE_NAME
+ * @static
+ * @final
+ */
+ParseError.INVALID_FILE_NAME = 122;
+
+/**
+ * Error code indicating an invalid ACL was provided.
+ * @property INVALID_ACL
+ * @static
+ * @final
+ */
+ParseError.INVALID_ACL = 123;
+
+/**
+ * Error code indicating that the request timed out on the server. Typically
+ * this indicates that the request is too expensive to run.
+ * @property TIMEOUT
+ * @static
+ * @final
+ */
+ParseError.TIMEOUT = 124;
+
+/**
+ * Error code indicating that the email address was invalid.
+ * @property INVALID_EMAIL_ADDRESS
+ * @static
+ * @final
+ */
+ParseError.INVALID_EMAIL_ADDRESS = 125;
+
+/**
+ * Error code indicating a missing content type.
+ * @property MISSING_CONTENT_TYPE
+ * @static
+ * @final
+ */
+ParseError.MISSING_CONTENT_TYPE = 126;
+
+/**
+ * Error code indicating a missing content length.
+ * @property MISSING_CONTENT_LENGTH
+ * @static
+ * @final
+ */
+ParseError.MISSING_CONTENT_LENGTH = 127;
+
+/**
+ * Error code indicating an invalid content length.
+ * @property INVALID_CONTENT_LENGTH
+ * @static
+ * @final
+ */
+ParseError.INVALID_CONTENT_LENGTH = 128;
+
+/**
+ * Error code indicating a file that was too large.
+ * @property FILE_TOO_LARGE
+ * @static
+ * @final
+ */
+ParseError.FILE_TOO_LARGE = 129;
+
+/**
+ * Error code indicating an error saving a file.
+ * @property FILE_SAVE_ERROR
+ * @static
+ * @final
+ */
+ParseError.FILE_SAVE_ERROR = 130;
+
+/**
+ * Error code indicating that a unique field was given a value that is
+ * already taken.
+ * @property DUPLICATE_VALUE
+ * @static
+ * @final
+ */
+ParseError.DUPLICATE_VALUE = 137;
+
+/**
+ * Error code indicating that a role's name is invalid.
+ * @property INVALID_ROLE_NAME
+ * @static
+ * @final
+ */
+ParseError.INVALID_ROLE_NAME = 139;
+
+/**
+ * Error code indicating that an application quota was exceeded. Upgrade to
+ * resolve.
+ * @property EXCEEDED_QUOTA
+ * @static
+ * @final
+ */
+ParseError.EXCEEDED_QUOTA = 140;
+
+/**
+ * Error code indicating that a Cloud Code script failed.
+ * @property SCRIPT_FAILED
+ * @static
+ * @final
+ */
+ParseError.SCRIPT_FAILED = 141;
+
+/**
+ * Error code indicating that a Cloud Code validation failed.
+ * @property VALIDATION_ERROR
+ * @static
+ * @final
+ */
+ParseError.VALIDATION_ERROR = 142;
+
+/**
+ * Error code indicating that invalid image data was provided.
+ * @property INVALID_IMAGE_DATA
+ * @static
+ * @final
+ */
+ParseError.INVALID_IMAGE_DATA = 143;
+
+/**
+ * Error code indicating an unsaved file.
+ * @property UNSAVED_FILE_ERROR
+ * @static
+ * @final
+ */
+ParseError.UNSAVED_FILE_ERROR = 151;
+
+/**
+ * Error code indicating an invalid push time.
+ * @property INVALID_PUSH_TIME_ERROR
+ * @static
+ * @final
+ */
+ParseError.INVALID_PUSH_TIME_ERROR = 152;
+
+/**
+ * Error code indicating an error deleting a file.
+ * @property FILE_DELETE_ERROR
+ * @static
+ * @final
+ */
+ParseError.FILE_DELETE_ERROR = 153;
+
+/**
+ * Error code indicating that the application has exceeded its request
+ * limit.
+ * @property REQUEST_LIMIT_EXCEEDED
+ * @static
+ * @final
+ */
+ParseError.REQUEST_LIMIT_EXCEEDED = 155;
+
+/**
+ * Error code indicating an invalid event name.
+ * @property INVALID_EVENT_NAME
+ * @static
+ * @final
+ */
+ParseError.INVALID_EVENT_NAME = 160;
+
+/**
+ * Error code indicating that the username is missing or empty.
+ * @property USERNAME_MISSING
+ * @static
+ * @final
+ */
+ParseError.USERNAME_MISSING = 200;
+
+/**
+ * Error code indicating that the password is missing or empty.
+ * @property PASSWORD_MISSING
+ * @static
+ * @final
+ */
+ParseError.PASSWORD_MISSING = 201;
+
+/**
+ * Error code indicating that the username has already been taken.
+ * @property USERNAME_TAKEN
+ * @static
+ * @final
+ */
+ParseError.USERNAME_TAKEN = 202;
+
+/**
+ * Error code indicating that the email has already been taken.
+ * @property EMAIL_TAKEN
+ * @static
+ * @final
+ */
+ParseError.EMAIL_TAKEN = 203;
+
+/**
+ * Error code indicating that the email is missing, but must be specified.
+ * @property EMAIL_MISSING
+ * @static
+ * @final
+ */
+ParseError.EMAIL_MISSING = 204;
+
+/**
+ * Error code indicating that a user with the specified email was not found.
+ * @property EMAIL_NOT_FOUND
+ * @static
+ * @final
+ */
+ParseError.EMAIL_NOT_FOUND = 205;
+
+/**
+ * Error code indicating that a user object without a valid session could
+ * not be altered.
+ * @property SESSION_MISSING
+ * @static
+ * @final
+ */
+ParseError.SESSION_MISSING = 206;
+
+/**
+ * Error code indicating that a user can only be created through signup.
+ * @property MUST_CREATE_USER_THROUGH_SIGNUP
+ * @static
+ * @final
+ */
+ParseError.MUST_CREATE_USER_THROUGH_SIGNUP = 207;
+
+/**
+ * Error code indicating that an an account being linked is already linked
+ * to another user.
+ * @property ACCOUNT_ALREADY_LINKED
+ * @static
+ * @final
+ */
+ParseError.ACCOUNT_ALREADY_LINKED = 208;
+
+/**
+ * Error code indicating that the current session token is invalid.
+ * @property INVALID_SESSION_TOKEN
+ * @static
+ * @final
+ */
+ParseError.INVALID_SESSION_TOKEN = 209;
+
+/**
+ * Error code indicating that a user cannot be linked to an account because
+ * that account's id could not be found.
+ * @property LINKED_ID_MISSING
+ * @static
+ * @final
+ */
+ParseError.LINKED_ID_MISSING = 250;
+
+/**
+ * Error code indicating that a user with a linked (e.g. Facebook) account
+ * has an invalid session.
+ * @property INVALID_LINKED_SESSION
+ * @static
+ * @final
+ */
+ParseError.INVALID_LINKED_SESSION = 251;
+
+/**
+ * Error code indicating that a service being linked (e.g. Facebook or
+ * Twitter) is unsupported.
+ * @property UNSUPPORTED_SERVICE
+ * @static
+ * @final
+ */
+ParseError.UNSUPPORTED_SERVICE = 252;
+
+/**
+ * Error code indicating that there were multiple errors. Aggregate errors
+ * have an "errors" property, which is an array of error objects with more
+ * detail about each error that occurred.
+ * @property AGGREGATE_ERROR
+ * @static
+ * @final
+ */
+ParseError.AGGREGATE_ERROR = 600;
+
+/**
+ * Error code indicating the client was unable to read an input file.
+ * @property FILE_READ_ERROR
+ * @static
+ * @final
+ */
+ParseError.FILE_READ_ERROR = 601;
+
+/**
+ * Error code indicating a real error code is unavailable because
+ * we had to use an XDomainRequest object to allow CORS requests in
+ * Internet Explorer, which strips the body from HTTP responses that have
+ * a non-2XX status code.
+ * @property X_DOMAIN_REQUEST
+ * @static
+ * @final
+ */
+ParseError.X_DOMAIN_REQUEST = 602;
\ No newline at end of file
diff --git a/lib/node/ParseFile.js b/lib/node/ParseFile.js
new file mode 100644
index 000000000..48efdb9d8
--- /dev/null
+++ b/lib/node/ParseFile.js
@@ -0,0 +1,291 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+
+var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
+
+var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
+
+var _createClass2 = require('babel-runtime/helpers/createClass');
+
+var _createClass3 = _interopRequireDefault(_createClass2);
+
+var _CoreManager = require('./CoreManager');
+
+var _CoreManager2 = _interopRequireDefault(_CoreManager);
+
+var _ParsePromise = require('./ParsePromise');
+
+var _ParsePromise2 = _interopRequireDefault(_ParsePromise);
+
+function _interopRequireDefault(obj) {
+ return obj && obj.__esModule ? obj : { default: obj };
+}
+
+/**
+ * Copyright (c) 2015-present, Parse, LLC.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ *
+ *
+ */
+
+var dataUriRegexp = /^data:([a-zA-Z]*\/[a-zA-Z+.-]*);(charset=[a-zA-Z0-9\-\/\s]*,)?base64,/;
+
+function b64Digit(number) {
+ if (number < 26) {
+ return String.fromCharCode(65 + number);
+ }
+ if (number < 52) {
+ return String.fromCharCode(97 + (number - 26));
+ }
+ if (number < 62) {
+ return String.fromCharCode(48 + (number - 52));
+ }
+ if (number === 62) {
+ return '+';
+ }
+ if (number === 63) {
+ return '/';
+ }
+ throw new TypeError('Tried to encode large digit ' + number + ' in base64.');
+}
+
+/**
+ * A Parse.File is a local representation of a file that is saved to the Parse
+ * cloud.
+ * @class Parse.File
+ * @constructor
+ * @param name {String} The file's name. This will be prefixed by a unique
+ * value once the file has finished saving. The file name must begin with
+ * an alphanumeric character, and consist of alphanumeric characters,
+ * periods, spaces, underscores, or dashes.
+ * @param data {Array} The data for the file, as either:
+ * 1. an Array of byte value Numbers, or
+ * 2. an Object like { base64: "..." } with a base64-encoded String.
+ * 3. a File object selected with a file upload control. (3) only works
+ * in Firefox 3.6+, Safari 6.0.2+, Chrome 7+, and IE 10+.
+ * For example:+ * var fileUploadControl = $("#profilePhotoFileUpload")[0]; + * if (fileUploadControl.files.length > 0) { + * var file = fileUploadControl.files[0]; + * var name = "photo.jpg"; + * var parseFile = new Parse.File(name, file); + * parseFile.save().then(function() { + * // The file has been saved to Parse. + * }, function(error) { + * // The file either could not be read, or could not be saved to Parse. + * }); + * }+ * @param type {String} Optional Content-Type header to use for the file. If + * this is omitted, the content type will be inferred from the name's + * extension. + */ + +var ParseFile = function () { + function ParseFile(name, data, type) { + (0, _classCallCheck3.default)(this, ParseFile); + + var specifiedType = type || ''; + + this._name = name; + + if (data !== undefined) { + if (Array.isArray(data)) { + this._source = { + format: 'base64', + base64: ParseFile.encodeBase64(data), + type: specifiedType + }; + } else if (typeof File !== 'undefined' && data instanceof File) { + this._source = { + format: 'file', + file: data, + type: specifiedType + }; + } else if (data && typeof data.base64 === 'string') { + var _base = data.base64; + var commaIndex = _base.indexOf(','); + + if (commaIndex !== -1) { + var matches = dataUriRegexp.exec(_base.slice(0, commaIndex + 1)); + // if data URI with type and charset, there will be 4 matches. + this._source = { + format: 'base64', + base64: _base.slice(commaIndex + 1), + type: matches[1] + }; + } else { + this._source = { + format: 'base64', + base64: _base, + type: specifiedType + }; + } + } else { + throw new TypeError('Cannot create a Parse.File with that data.'); + } + } + } + + /** + * Gets the name of the file. Before save is called, this is the filename + * given by the user. After save is called, that name gets prefixed with a + * unique identifier. + * @method name + * @return {String} + */ + + (0, _createClass3.default)(ParseFile, [{ + key: 'name', + value: function () { + return this._name; + } + + /** + * Gets the url of the file. It is only available after you save the file or + * after you get the file from a Parse.Object. + * @method url + * @param {Object} options An object to specify url options + * @return {String} + */ + + }, { + key: 'url', + value: function (options) { + options = options || {}; + if (!this._url) { + return; + } + if (options.forceSecure) { + return this._url.replace(/^http:\/\//i, 'https://'); + } else { + return this._url; + } + } + + /** + * Saves the file to the Parse cloud. + * @method save + * @param {Object} options A Backbone-style options object. + * @return {Parse.Promise} Promise that is resolved when the save finishes. + */ + + }, { + key: 'save', + value: function (options) { + var _this = this; + + options = options || {}; + var controller = _CoreManager2.default.getFileController(); + if (!this._previousSave) { + if (this._source.format === 'file') { + this._previousSave = controller.saveFile(this._name, this._source).then(function (res) { + _this._name = res.name; + _this._url = res.url; + return _this; + }); + } else { + this._previousSave = controller.saveBase64(this._name, this._source).then(function (res) { + _this._name = res.name; + _this._url = res.url; + return _this; + }); + } + } + if (this._previousSave) { + return this._previousSave._thenRunCallbacks(options); + } + } + }, { + key: 'toJSON', + value: function () { + return { + __type: 'File', + name: this._name, + url: this._url + }; + } + }, { + key: 'equals', + value: function (other) { + if (this === other) { + return true; + } + // Unsaved Files are never equal, since they will be saved to different URLs + return other instanceof ParseFile && this.name() === other.name() && this.url() === other.url() && typeof this.url() !== 'undefined'; + } + }], [{ + key: 'fromJSON', + value: function (obj) { + if (obj.__type !== 'File') { + throw new TypeError('JSON object does not represent a ParseFile'); + } + var file = new ParseFile(obj.name); + file._url = obj.url; + return file; + } + }, { + key: 'encodeBase64', + value: function (bytes) { + var chunks = []; + chunks.length = Math.ceil(bytes.length / 3); + for (var i = 0; i < chunks.length; i++) { + var b1 = bytes[i * 3]; + var b2 = bytes[i * 3 + 1] || 0; + var b3 = bytes[i * 3 + 2] || 0; + + var has2 = i * 3 + 1 < bytes.length; + var has3 = i * 3 + 2 < bytes.length; + + chunks[i] = [b64Digit(b1 >> 2 & 0x3F), b64Digit(b1 << 4 & 0x30 | b2 >> 4 & 0x0F), has2 ? b64Digit(b2 << 2 & 0x3C | b3 >> 6 & 0x03) : '=', has3 ? b64Digit(b3 & 0x3F) : '='].join(''); + } + + return chunks.join(''); + } + }]); + return ParseFile; +}(); + +exports.default = ParseFile; + +var DefaultController = { + saveFile: function (name, source) { + if (source.format !== 'file') { + throw new Error('saveFile can only be used with File-type sources.'); + } + // To directly upload a File, we use a REST-style AJAX request + var headers = { + 'X-Parse-Application-ID': _CoreManager2.default.get('APPLICATION_ID'), + 'X-Parse-JavaScript-Key': _CoreManager2.default.get('JAVASCRIPT_KEY'), + 'Content-Type': source.type || (source.file ? source.file.type : null) + }; + var url = _CoreManager2.default.get('SERVER_URL'); + if (url[url.length - 1] !== '/') { + url += '/'; + } + url += 'files/' + name; + return _CoreManager2.default.getRESTController().ajax('POST', url, source.file, headers); + }, + + saveBase64: function (name, source) { + if (source.format !== 'base64') { + throw new Error('saveBase64 can only be used with Base64-type sources.'); + } + var data = { + base64: source.base64 + }; + if (source.type) { + data._ContentType = source.type; + } + + return _CoreManager2.default.getRESTController().request('POST', 'files/' + name, data); + } +}; + +_CoreManager2.default.setFileController(DefaultController); \ No newline at end of file diff --git a/lib/node/ParseGeoPoint.js b/lib/node/ParseGeoPoint.js new file mode 100644 index 000000000..98691f85b --- /dev/null +++ b/lib/node/ParseGeoPoint.js @@ -0,0 +1,235 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _typeof2 = require('babel-runtime/helpers/typeof'); + +var _typeof3 = _interopRequireDefault(_typeof2); + +var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); + +var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); + +var _createClass2 = require('babel-runtime/helpers/createClass'); + +var _createClass3 = _interopRequireDefault(_createClass2); + +var _ParsePromise = require('./ParsePromise'); + +var _ParsePromise2 = _interopRequireDefault(_ParsePromise); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { default: obj }; +} + +/** + * Creates a new GeoPoint with any of the following forms:
+ * new GeoPoint(otherGeoPoint) + * new GeoPoint(30, 30) + * new GeoPoint([30, 30]) + * new GeoPoint({latitude: 30, longitude: 30}) + * new GeoPoint() // defaults to (0, 0) + *+ * @class Parse.GeoPoint + * @constructor + * + *
Represents a latitude / longitude point that may be associated + * with a key in a ParseObject or used as a reference point for geo queries. + * This allows proximity-based queries on the key.
+ * + *Only one key in a class may contain a GeoPoint.
+ * + *Example:
+ * var point = new Parse.GeoPoint(30.0, -20.0); + * var object = new Parse.Object("PlaceObject"); + * object.set("location", point); + * object.save();+ */ +var ParseGeoPoint = function () { + function ParseGeoPoint(arg1, arg2) { + (0, _classCallCheck3.default)(this, ParseGeoPoint); + + if (Array.isArray(arg1)) { + ParseGeoPoint._validate(arg1[0], arg1[1]); + this._latitude = arg1[0]; + this._longitude = arg1[1]; + } else if ((typeof arg1 === 'undefined' ? 'undefined' : (0, _typeof3.default)(arg1)) === 'object') { + ParseGeoPoint._validate(arg1.latitude, arg1.longitude); + this._latitude = arg1.latitude; + this._longitude = arg1.longitude; + } else if (typeof arg1 === 'number' && typeof arg2 === 'number') { + ParseGeoPoint._validate(arg1, arg2); + this._latitude = arg1; + this._longitude = arg2; + } else { + this._latitude = 0; + this._longitude = 0; + } + } + + /** + * North-south portion of the coordinate, in range [-90, 90]. + * Throws an exception if set out of range in a modern browser. + * @property latitude + * @type Number + */ + + (0, _createClass3.default)(ParseGeoPoint, [{ + key: 'toJSON', + + /** + * Returns a JSON representation of the GeoPoint, suitable for Parse. + * @method toJSON + * @return {Object} + */ + value: function () { + ParseGeoPoint._validate(this._latitude, this._longitude); + return { + __type: 'GeoPoint', + latitude: this._latitude, + longitude: this._longitude + }; + } + }, { + key: 'equals', + value: function (other) { + return other instanceof ParseGeoPoint && this.latitude === other.latitude && this.longitude === other.longitude; + } + + /** + * Returns the distance from this GeoPoint to another in radians. + * @method radiansTo + * @param {Parse.GeoPoint} point the other Parse.GeoPoint. + * @return {Number} + */ + + }, { + key: 'radiansTo', + value: function (point) { + var d2r = Math.PI / 180.0; + var lat1rad = this.latitude * d2r; + var long1rad = this.longitude * d2r; + var lat2rad = point.latitude * d2r; + var long2rad = point.longitude * d2r; + + var sinDeltaLatDiv2 = Math.sin((lat1rad - lat2rad) / 2); + var sinDeltaLongDiv2 = Math.sin((long1rad - long2rad) / 2); + // Square of half the straight line chord distance between both points. + var a = sinDeltaLatDiv2 * sinDeltaLatDiv2 + Math.cos(lat1rad) * Math.cos(lat2rad) * sinDeltaLongDiv2 * sinDeltaLongDiv2; + a = Math.min(1.0, a); + return 2 * Math.asin(Math.sqrt(a)); + } + + /** + * Returns the distance from this GeoPoint to another in kilometers. + * @method kilometersTo + * @param {Parse.GeoPoint} point the other Parse.GeoPoint. + * @return {Number} + */ + + }, { + key: 'kilometersTo', + value: function (point) { + return this.radiansTo(point) * 6371.0; + } + + /** + * Returns the distance from this GeoPoint to another in miles. + * @method milesTo + * @param {Parse.GeoPoint} point the other Parse.GeoPoint. + * @return {Number} + */ + + }, { + key: 'milesTo', + value: function (point) { + return this.radiansTo(point) * 3958.8; + } + + /** + * Throws an exception if the given lat-long is out of bounds. + */ + + }, { + key: 'latitude', + get: function () { + return this._latitude; + }, + set: function (val) { + ParseGeoPoint._validate(val, this.longitude); + this._latitude = val; + } + + /** + * East-west portion of the coordinate, in range [-180, 180]. + * Throws if set out of range in a modern browser. + * @property longitude + * @type Number + */ + + }, { + key: 'longitude', + get: function () { + return this._longitude; + }, + set: function (val) { + ParseGeoPoint._validate(this.latitude, val); + this._longitude = val; + } + }], [{ + key: '_validate', + value: function (latitude, longitude) { + if (latitude !== latitude || longitude !== longitude) { + throw new TypeError('GeoPoint latitude and longitude must be valid numbers'); + } + if (latitude < -90.0) { + throw new TypeError('GeoPoint latitude out of bounds: ' + latitude + ' < -90.0.'); + } + if (latitude > 90.0) { + throw new TypeError('GeoPoint latitude out of bounds: ' + latitude + ' > 90.0.'); + } + if (longitude < -180.0) { + throw new TypeError('GeoPoint longitude out of bounds: ' + longitude + ' < -180.0.'); + } + if (longitude > 180.0) { + throw new TypeError('GeoPoint longitude out of bounds: ' + longitude + ' > 180.0.'); + } + } + + /** + * Creates a GeoPoint with the user's current location, if available. + * Calls options.success with a new GeoPoint instance or calls options.error. + * @method current + * @param {Object} options An object with success and error callbacks. + * @static + */ + + }, { + key: 'current', + value: function (options) { + var promise = new _ParsePromise2.default(); + navigator.geolocation.getCurrentPosition(function (location) { + promise.resolve(new ParseGeoPoint(location.coords.latitude, location.coords.longitude)); + }, function (error) { + promise.reject(error); + }); + + return promise._thenRunCallbacks(options); + } + }]); + return ParseGeoPoint; +}(); /** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + */ + +exports.default = ParseGeoPoint; \ No newline at end of file diff --git a/lib/node/ParseHooks.js b/lib/node/ParseHooks.js new file mode 100644 index 000000000..a2057e899 --- /dev/null +++ b/lib/node/ParseHooks.js @@ -0,0 +1,162 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _promise = require('babel-runtime/core-js/promise'); + +var _promise2 = _interopRequireDefault(_promise); + +exports.getFunctions = getFunctions; +exports.getTriggers = getTriggers; +exports.getFunction = getFunction; +exports.getTrigger = getTrigger; +exports.createFunction = createFunction; +exports.createTrigger = createTrigger; +exports.create = create; +exports.updateFunction = updateFunction; +exports.updateTrigger = updateTrigger; +exports.update = update; +exports.removeFunction = removeFunction; +exports.removeTrigger = removeTrigger; +exports.remove = remove; + +var _CoreManager = require('./CoreManager'); + +var _CoreManager2 = _interopRequireDefault(_CoreManager); + +var _decode = require('./decode'); + +var _decode2 = _interopRequireDefault(_decode); + +var _encode = require('./encode'); + +var _encode2 = _interopRequireDefault(_encode); + +var _ParseError = require('./ParseError'); + +var _ParseError2 = _interopRequireDefault(_ParseError); + +var _ParsePromise = require('./ParsePromise'); + +var _ParsePromise2 = _interopRequireDefault(_ParsePromise); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { default: obj }; +} + +function getFunctions() { + return _CoreManager2.default.getHooksController().get("functions"); +} + +function getTriggers() { + return _CoreManager2.default.getHooksController().get("triggers"); +} + +function getFunction(name) { + return _CoreManager2.default.getHooksController().get("functions", name); +} + +function getTrigger(className, triggerName) { + return _CoreManager2.default.getHooksController().get("triggers", className, triggerName); +} + +function createFunction(functionName, url) { + return create({ functionName: functionName, url: url }); +} + +function createTrigger(className, triggerName, url) { + return create({ className: className, triggerName: triggerName, url: url }); +} + +function create(hook) { + return _CoreManager2.default.getHooksController().create(hook); +} + +function updateFunction(functionName, url) { + return update({ functionName: functionName, url: url }); +} + +function updateTrigger(className, triggerName, url) { + return update({ className: className, triggerName: triggerName, url: url }); +} + +function update(hook) { + return _CoreManager2.default.getHooksController().update(hook); +} + +function removeFunction(functionName) { + return remove({ functionName: functionName }); +} + +function removeTrigger(className, triggerName) { + return remove({ className: className, triggerName: triggerName }); +} + +function remove(hook) { + return _CoreManager2.default.getHooksController().remove(hook); +} + +var DefaultController = { + get: function (type, functionName, triggerName) { + var url = "/hooks/" + type; + if (functionName) { + url += "/" + functionName; + if (triggerName) { + url += "/" + triggerName; + } + } + return this.sendRequest("GET", url); + }, + create: function (hook) { + var url; + if (hook.functionName && hook.url) { + url = "/hooks/functions"; + } else if (hook.className && hook.triggerName && hook.url) { + url = "/hooks/triggers"; + } else { + return _promise2.default.reject({ error: 'invalid hook declaration', code: 143 }); + } + return this.sendRequest("POST", url, hook); + }, + remove: function (hook) { + var url; + if (hook.functionName) { + url = "/hooks/functions/" + hook.functionName; + delete hook.functionName; + } else if (hook.className && hook.triggerName) { + url = "/hooks/triggers/" + hook.className + "/" + hook.triggerName; + delete hook.className; + delete hook.triggerName; + } else { + return _promise2.default.reject({ error: 'invalid hook declaration', code: 143 }); + } + return this.sendRequest("PUT", url, { "__op": "Delete" }); + }, + update: function (hook) { + var url; + if (hook.functionName && hook.url) { + url = "/hooks/functions/" + hook.functionName; + delete hook.functionName; + } else if (hook.className && hook.triggerName && hook.url) { + url = "/hooks/triggers/" + hook.className + "/" + hook.triggerName; + delete hook.className; + delete hook.triggerName; + } else { + return _promise2.default.reject({ error: 'invalid hook declaration', code: 143 }); + } + return this.sendRequest('PUT', url, hook); + }, + sendRequest: function (method, url, body) { + return _CoreManager2.default.getRESTController().request(method, url, body, { useMasterKey: true }).then(function (res) { + var decoded = (0, _decode2.default)(res); + if (decoded) { + return _ParsePromise2.default.as(decoded); + } + return _ParsePromise2.default.error(new _ParseError2.default(_ParseError2.default.INVALID_JSON, 'The server returned an invalid response.')); + }); + } +}; + +_CoreManager2.default.setHooksController(DefaultController); \ No newline at end of file diff --git a/lib/node/ParseInstallation.js b/lib/node/ParseInstallation.js new file mode 100644 index 000000000..02d7be1d8 --- /dev/null +++ b/lib/node/ParseInstallation.js @@ -0,0 +1,65 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _typeof2 = require('babel-runtime/helpers/typeof'); + +var _typeof3 = _interopRequireDefault(_typeof2); + +var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); + +var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); + +var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); + +var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); + +var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); + +var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); + +var _inherits2 = require('babel-runtime/helpers/inherits'); + +var _inherits3 = _interopRequireDefault(_inherits2); + +var _ParseObject2 = require('./ParseObject'); + +var _ParseObject3 = _interopRequireDefault(_ParseObject2); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { default: obj }; +} + +var Installation = function (_ParseObject) { + (0, _inherits3.default)(Installation, _ParseObject); + + function Installation(attributes) { + (0, _classCallCheck3.default)(this, Installation); + + var _this = (0, _possibleConstructorReturn3.default)(this, (Installation.__proto__ || (0, _getPrototypeOf2.default)(Installation)).call(this, '_Installation')); + + if (attributes && (typeof attributes === 'undefined' ? 'undefined' : (0, _typeof3.default)(attributes)) === 'object') { + if (!_this.set(attributes || {})) { + throw new Error('Can\'t create an invalid Session'); + } + } + return _this; + } + + return Installation; +}(_ParseObject3.default); /** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + */ + +exports.default = Installation; + +_ParseObject3.default.registerSubclass('_Installation', Installation); \ No newline at end of file diff --git a/lib/node/ParseLiveQuery.js b/lib/node/ParseLiveQuery.js new file mode 100644 index 000000000..a3e5fe31d --- /dev/null +++ b/lib/node/ParseLiveQuery.js @@ -0,0 +1,241 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _EventEmitter = require('./EventEmitter'); + +var _EventEmitter2 = _interopRequireDefault(_EventEmitter); + +var _LiveQueryClient = require('./LiveQueryClient'); + +var _LiveQueryClient2 = _interopRequireDefault(_LiveQueryClient); + +var _CoreManager = require('./CoreManager'); + +var _CoreManager2 = _interopRequireDefault(_CoreManager); + +var _ParsePromise = require('./ParsePromise'); + +var _ParsePromise2 = _interopRequireDefault(_ParsePromise); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { default: obj }; +} + +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + */ + +function open() { + var LiveQueryController = _CoreManager2.default.getLiveQueryController(); + LiveQueryController.open(); +} + +function close() { + var LiveQueryController = _CoreManager2.default.getLiveQueryController(); + LiveQueryController.close(); +} + +/** + * + * We expose three events to help you monitor the status of the WebSocket connection: + * + *
Open - When we establish the WebSocket connection to the LiveQuery server, you'll get this event. + * + *
+ * Parse.LiveQuery.on('open', () => { + * + * });+ * + *
Close - When we lose the WebSocket connection to the LiveQuery server, you'll get this event. + * + *
+ * Parse.LiveQuery.on('close', () => { + * + * });+ * + *
Error - When some network error or LiveQuery server error happens, you'll get this event. + * + *
+ * Parse.LiveQuery.on('error', (error) => { + * + * });+ * + * @class Parse.LiveQuery + * @static + * + */ +var LiveQuery = new _EventEmitter2.default(); + +/** + * After open is called, the LiveQuery will try to send a connect request + * to the LiveQuery server. + * + * @method open + */ +LiveQuery.open = open; + +/** + * When you're done using LiveQuery, you can call Parse.LiveQuery.close(). + * This function will close the WebSocket connection to the LiveQuery server, + * cancel the auto reconnect, and unsubscribe all subscriptions based on it. + * If you call query.subscribe() after this, we'll create a new WebSocket + * connection to the LiveQuery server. + * + * @method close + */ + +LiveQuery.close = close; +// Register a default onError callback to make sure we do not crash on error +LiveQuery.on('error', function () {}); + +exports.default = LiveQuery; + +function getSessionToken() { + var controller = _CoreManager2.default.getUserController(); + return controller.currentUserAsync().then(function (currentUser) { + return currentUser ? currentUser.getSessionToken() : undefined; + }); +} + +function getLiveQueryClient() { + return _CoreManager2.default.getLiveQueryController().getDefaultLiveQueryClient(); +} + +var defaultLiveQueryClient = void 0; +var DefaultLiveQueryController = { + setDefaultLiveQueryClient: function (liveQueryClient) { + defaultLiveQueryClient = liveQueryClient; + }, + getDefaultLiveQueryClient: function () { + if (defaultLiveQueryClient) { + return _ParsePromise2.default.as(defaultLiveQueryClient); + } + + return getSessionToken().then(function (sessionToken) { + var liveQueryServerURL = _CoreManager2.default.get('LIVEQUERY_SERVER_URL'); + + if (liveQueryServerURL && liveQueryServerURL.indexOf('ws') !== 0) { + throw new Error('You need to set a proper Parse LiveQuery server url before using LiveQueryClient'); + } + + // If we can not find Parse.liveQueryServerURL, we try to extract it from Parse.serverURL + if (!liveQueryServerURL) { + var tempServerURL = _CoreManager2.default.get('SERVER_URL'); + var protocol = 'ws://'; + // If Parse is being served over SSL/HTTPS, ensure LiveQuery Server uses 'wss://' prefix + if (tempServerURL.indexOf('https') === 0) { + protocol = 'wss://'; + } + var host = tempServerURL.replace(/^https?:\/\//, ''); + liveQueryServerURL = protocol + host; + _CoreManager2.default.set('LIVEQUERY_SERVER_URL', liveQueryServerURL); + } + + var applicationId = _CoreManager2.default.get('APPLICATION_ID'); + var javascriptKey = _CoreManager2.default.get('JAVASCRIPT_KEY'); + var masterKey = _CoreManager2.default.get('MASTER_KEY'); + // Get currentUser sessionToken if possible + defaultLiveQueryClient = new _LiveQueryClient2.default({ + applicationId: applicationId, + serverURL: liveQueryServerURL, + javascriptKey: javascriptKey, + masterKey: masterKey, + sessionToken: sessionToken + }); + // Register a default onError callback to make sure we do not crash on error + // Cannot create these events on a nested way because of EventEmiiter from React Native + defaultLiveQueryClient.on('error', function (error) { + LiveQuery.emit('error', error); + }); + defaultLiveQueryClient.on('open', function () { + LiveQuery.emit('open'); + }); + defaultLiveQueryClient.on('close', function () { + LiveQuery.emit('close'); + }); + + return defaultLiveQueryClient; + }); + }, + open: function () { + var _this = this; + + getLiveQueryClient().then(function (liveQueryClient) { + _this.resolve(liveQueryClient.open()); + }); + }, + close: function () { + var _this2 = this; + + getLiveQueryClient().then(function (liveQueryClient) { + _this2.resolve(liveQueryClient.close()); + }); + }, + subscribe: function (query) { + var _this3 = this; + + var subscriptionWrap = new _EventEmitter2.default(); + + getLiveQueryClient().then(function (liveQueryClient) { + if (liveQueryClient.shouldOpen()) { + liveQueryClient.open(); + } + var promiseSessionToken = getSessionToken(); + // new event emitter + return promiseSessionToken.then(function (sessionToken) { + + var subscription = liveQueryClient.subscribe(query, sessionToken); + // enter, leave create, etc + + subscriptionWrap.id = subscription.id; + subscriptionWrap.query = subscription.query; + subscriptionWrap.sessionToken = subscription.sessionToken; + subscriptionWrap.unsubscribe = subscription.unsubscribe; + // Cannot create these events on a nested way because of EventEmiiter from React Native + subscription.on('open', function () { + subscriptionWrap.emit('open'); + }); + subscription.on('create', function (object) { + subscriptionWrap.emit('create', object); + }); + subscription.on('update', function (object) { + subscriptionWrap.emit('update', object); + }); + subscription.on('enter', function (object) { + subscriptionWrap.emit('enter', object); + }); + subscription.on('leave', function (object) { + subscriptionWrap.emit('leave', object); + }); + subscription.on('delete', function (object) { + subscriptionWrap.emit('delete', object); + }); + + _this3.resolve(); + }); + }); + return subscriptionWrap; + }, + unsubscribe: function (subscription) { + var _this4 = this; + + getLiveQueryClient().then(function (liveQueryClient) { + _this4.resolve(liveQueryClient.unsubscribe(subscription)); + }); + }, + _clearCachedDefaultClient: function () { + defaultLiveQueryClient = null; + } +}; + +_CoreManager2.default.setLiveQueryController(DefaultLiveQueryController); \ No newline at end of file diff --git a/lib/node/ParseObject.js b/lib/node/ParseObject.js new file mode 100644 index 000000000..f08795e52 --- /dev/null +++ b/lib/node/ParseObject.js @@ -0,0 +1,2001 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _defineProperty = require('babel-runtime/core-js/object/define-property'); + +var _defineProperty2 = _interopRequireDefault(_defineProperty); + +var _create = require('babel-runtime/core-js/object/create'); + +var _create2 = _interopRequireDefault(_create); + +var _freeze = require('babel-runtime/core-js/object/freeze'); + +var _freeze2 = _interopRequireDefault(_freeze); + +var _stringify = require('babel-runtime/core-js/json/stringify'); + +var _stringify2 = _interopRequireDefault(_stringify); + +var _keys = require('babel-runtime/core-js/object/keys'); + +var _keys2 = _interopRequireDefault(_keys); + +var _typeof2 = require('babel-runtime/helpers/typeof'); + +var _typeof3 = _interopRequireDefault(_typeof2); + +var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); + +var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); + +var _createClass2 = require('babel-runtime/helpers/createClass'); + +var _createClass3 = _interopRequireDefault(_createClass2); + +var _CoreManager = require('./CoreManager'); + +var _CoreManager2 = _interopRequireDefault(_CoreManager); + +var _canBeSerialized = require('./canBeSerialized'); + +var _canBeSerialized2 = _interopRequireDefault(_canBeSerialized); + +var _decode = require('./decode'); + +var _decode2 = _interopRequireDefault(_decode); + +var _encode = require('./encode'); + +var _encode2 = _interopRequireDefault(_encode); + +var _equals = require('./equals'); + +var _equals2 = _interopRequireDefault(_equals); + +var _escape2 = require('./escape'); + +var _escape3 = _interopRequireDefault(_escape2); + +var _ParseACL = require('./ParseACL'); + +var _ParseACL2 = _interopRequireDefault(_ParseACL); + +var _parseDate = require('./parseDate'); + +var _parseDate2 = _interopRequireDefault(_parseDate); + +var _ParseError = require('./ParseError'); + +var _ParseError2 = _interopRequireDefault(_ParseError); + +var _ParseFile = require('./ParseFile'); + +var _ParseFile2 = _interopRequireDefault(_ParseFile); + +var _ParseOp = require('./ParseOp'); + +var _ParsePromise = require('./ParsePromise'); + +var _ParsePromise2 = _interopRequireDefault(_ParsePromise); + +var _ParseQuery = require('./ParseQuery'); + +var _ParseQuery2 = _interopRequireDefault(_ParseQuery); + +var _ParseRelation = require('./ParseRelation'); + +var _ParseRelation2 = _interopRequireDefault(_ParseRelation); + +var _SingleInstanceStateController = require('./SingleInstanceStateController'); + +var SingleInstanceStateController = _interopRequireWildcard(_SingleInstanceStateController); + +var _unique = require('./unique'); + +var _unique2 = _interopRequireDefault(_unique); + +var _UniqueInstanceStateController = require('./UniqueInstanceStateController'); + +var UniqueInstanceStateController = _interopRequireWildcard(_UniqueInstanceStateController); + +var _unsavedChildren = require('./unsavedChildren'); + +var _unsavedChildren2 = _interopRequireDefault(_unsavedChildren); + +function _interopRequireWildcard(obj) { + if (obj && obj.__esModule) { + return obj; + } else { + var newObj = {};if (obj != null) { + for (var key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; + } + }newObj.default = obj;return newObj; + } +} + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { default: obj }; +} + +// Mapping of class names to constructors, so we can populate objects from the +// server with appropriate subclasses of ParseObject +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + */ + +var classMap = {}; + +// Global counter for generating unique local Ids +var localCount = 0; +// Global counter for generating unique Ids for non-single-instance objects +var objectCount = 0; +// On web clients, objects are single-instance: any two objects with the same Id +// will have the same attributes. However, this may be dangerous default +// behavior in a server scenario +var singleInstance = !_CoreManager2.default.get('IS_NODE'); +if (singleInstance) { + _CoreManager2.default.setObjectStateController(SingleInstanceStateController); +} else { + _CoreManager2.default.setObjectStateController(UniqueInstanceStateController); +} + +function getServerUrlPath() { + var serverUrl = _CoreManager2.default.get('SERVER_URL'); + if (serverUrl[serverUrl.length - 1] !== '/') { + serverUrl += '/'; + } + var url = serverUrl.replace(/https?:\/\//, ''); + return url.substr(url.indexOf('/')); +} + +/** + * Creates a new model with defined attributes. + * + *
You won't normally call this method directly. It is recommended that
+ * you use a subclass of Parse.Object
instead, created by calling
+ * extend
.
However, if you don't want to use a subclass, or aren't sure which + * subclass is appropriate, you can use this form:
+ * var object = new Parse.Object("ClassName"); + *+ * That is basically equivalent to:
+ * var MyClass = Parse.Object.extend("ClassName"); + * var object = new MyClass(); + *+ * + * @class Parse.Object + * @constructor + * @param {String} className The class name for the object + * @param {Object} attributes The initial set of data to store in the object. + * @param {Object} options The options for this object instance. + */ + +var ParseObject = function () { + /** + * The ID of this object, unique within its class. + * @property id + * @type String + */ + function ParseObject(className, attributes, options) { + (0, _classCallCheck3.default)(this, ParseObject); + + // Enable legacy initializers + if (typeof this.initialize === 'function') { + this.initialize.apply(this, arguments); + } + + var toSet = null; + this._objCount = objectCount++; + if (typeof className === 'string') { + this.className = className; + if (attributes && (typeof attributes === 'undefined' ? 'undefined' : (0, _typeof3.default)(attributes)) === 'object') { + toSet = attributes; + } + } else if (className && (typeof className === 'undefined' ? 'undefined' : (0, _typeof3.default)(className)) === 'object') { + this.className = className.className; + toSet = {}; + for (var attr in className) { + if (attr !== 'className') { + toSet[attr] = className[attr]; + } + } + if (attributes && (typeof attributes === 'undefined' ? 'undefined' : (0, _typeof3.default)(attributes)) === 'object') { + options = attributes; + } + } + if (toSet && !this.set(toSet, options)) { + throw new Error('Can\'t create an invalid Parse Object'); + } + } + + /** Prototype getters / setters **/ + + (0, _createClass3.default)(ParseObject, [{ + key: '_getId', + + /** Private methods **/ + + /** + * Returns a local or server Id used uniquely identify this object + */ + value: function () { + if (typeof this.id === 'string') { + return this.id; + } + if (typeof this._localId === 'string') { + return this._localId; + } + var localId = 'local' + String(localCount++); + this._localId = localId; + return localId; + } + + /** + * Returns a unique identifier used to pull data from the State Controller. + */ + + }, { + key: '_getStateIdentifier', + value: function () { + if (singleInstance) { + var _id = this.id; + if (!_id) { + _id = this._getId(); + } + return { + id: _id, + className: this.className + }; + } else { + return this; + } + } + }, { + key: '_getServerData', + value: function () { + var stateController = _CoreManager2.default.getObjectStateController(); + return stateController.getServerData(this._getStateIdentifier()); + } + }, { + key: '_clearServerData', + value: function () { + var serverData = this._getServerData(); + var unset = {}; + for (var attr in serverData) { + unset[attr] = undefined; + } + var stateController = _CoreManager2.default.getObjectStateController(); + stateController.setServerData(this._getStateIdentifier(), unset); + } + }, { + key: '_getPendingOps', + value: function () { + var stateController = _CoreManager2.default.getObjectStateController(); + return stateController.getPendingOps(this._getStateIdentifier()); + } + }, { + key: '_clearPendingOps', + value: function () { + var pending = this._getPendingOps(); + var latest = pending[pending.length - 1]; + var keys = (0, _keys2.default)(latest); + keys.forEach(function (key) { + delete latest[key]; + }); + } + }, { + key: '_getDirtyObjectAttributes', + value: function () { + var attributes = this.attributes; + var stateController = _CoreManager2.default.getObjectStateController(); + var objectCache = stateController.getObjectCache(this._getStateIdentifier()); + var dirty = {}; + for (var attr in attributes) { + var val = attributes[attr]; + if (val && (typeof val === 'undefined' ? 'undefined' : (0, _typeof3.default)(val)) === 'object' && !(val instanceof ParseObject) && !(val instanceof _ParseFile2.default) && !(val instanceof _ParseRelation2.default)) { + // Due to the way browsers construct maps, the key order will not change + // unless the object is changed + try { + var json = (0, _encode2.default)(val, false, true); + var stringified = (0, _stringify2.default)(json); + if (objectCache[attr] !== stringified) { + dirty[attr] = val; + } + } catch (e) { + // Error occurred, possibly by a nested unsaved pointer in a mutable container + // No matter how it happened, it indicates a change in the attribute + dirty[attr] = val; + } + } + } + return dirty; + } + }, { + key: '_toFullJSON', + value: function (seen) { + var json = this.toJSON(seen); + json.__type = 'Object'; + json.className = this.className; + return json; + } + }, { + key: '_getSaveJSON', + value: function () { + var pending = this._getPendingOps(); + var dirtyObjects = this._getDirtyObjectAttributes(); + var json = {}; + + for (var attr in dirtyObjects) { + json[attr] = new _ParseOp.SetOp(dirtyObjects[attr]).toJSON(); + } + for (attr in pending[0]) { + json[attr] = pending[0][attr].toJSON(); + } + return json; + } + }, { + key: '_getSaveParams', + value: function () { + var method = this.id ? 'PUT' : 'POST'; + var body = this._getSaveJSON(); + var path = 'classes/' + this.className; + if (this.id) { + path += '/' + this.id; + } else if (this.className === '_User') { + path = 'users'; + } + return { + method: method, + body: body, + path: path + }; + } + }, { + key: '_finishFetch', + value: function (serverData) { + if (!this.id && serverData.objectId) { + this.id = serverData.objectId; + } + var stateController = _CoreManager2.default.getObjectStateController(); + stateController.initializeState(this._getStateIdentifier()); + var decoded = {}; + for (var attr in serverData) { + if (attr === 'ACL') { + decoded[attr] = new _ParseACL2.default(serverData[attr]); + } else if (attr !== 'objectId') { + decoded[attr] = (0, _decode2.default)(serverData[attr]); + if (decoded[attr] instanceof _ParseRelation2.default) { + decoded[attr]._ensureParentAndKey(this, attr); + } + } + } + if (decoded.createdAt && typeof decoded.createdAt === 'string') { + decoded.createdAt = (0, _parseDate2.default)(decoded.createdAt); + } + if (decoded.updatedAt && typeof decoded.updatedAt === 'string') { + decoded.updatedAt = (0, _parseDate2.default)(decoded.updatedAt); + } + if (!decoded.updatedAt && decoded.createdAt) { + decoded.updatedAt = decoded.createdAt; + } + stateController.commitServerChanges(this._getStateIdentifier(), decoded); + } + }, { + key: '_setExisted', + value: function (existed) { + var stateController = _CoreManager2.default.getObjectStateController(); + var state = stateController.getState(this._getStateIdentifier()); + if (state) { + state.existed = existed; + } + } + }, { + key: '_migrateId', + value: function (serverId) { + if (this._localId && serverId) { + if (singleInstance) { + var stateController = _CoreManager2.default.getObjectStateController(); + var oldState = stateController.removeState(this._getStateIdentifier()); + this.id = serverId; + delete this._localId; + if (oldState) { + stateController.initializeState(this._getStateIdentifier(), oldState); + } + } else { + this.id = serverId; + delete this._localId; + } + } + } + }, { + key: '_handleSaveResponse', + value: function (response, status) { + var changes = {}; + + var stateController = _CoreManager2.default.getObjectStateController(); + var pending = stateController.popPendingState(this._getStateIdentifier()); + for (var attr in pending) { + if (pending[attr] instanceof _ParseOp.RelationOp) { + changes[attr] = pending[attr].applyTo(undefined, this, attr); + } else if (!(attr in response)) { + // Only SetOps and UnsetOps should not come back with results + changes[attr] = pending[attr].applyTo(undefined); + } + } + for (attr in response) { + if ((attr === 'createdAt' || attr === 'updatedAt') && typeof response[attr] === 'string') { + changes[attr] = (0, _parseDate2.default)(response[attr]); + } else if (attr === 'ACL') { + changes[attr] = new _ParseACL2.default(response[attr]); + } else if (attr !== 'objectId') { + changes[attr] = (0, _decode2.default)(response[attr]); + if (changes[attr] instanceof _ParseOp.UnsetOp) { + changes[attr] = undefined; + } + } + } + if (changes.createdAt && !changes.updatedAt) { + changes.updatedAt = changes.createdAt; + } + + this._migrateId(response.objectId); + + if (status !== 201) { + this._setExisted(true); + } + + stateController.commitServerChanges(this._getStateIdentifier(), changes); + } + }, { + key: '_handleSaveError', + value: function () { + this._getPendingOps(); + + var stateController = _CoreManager2.default.getObjectStateController(); + stateController.mergeFirstPendingState(this._getStateIdentifier()); + } + + /** Public methods **/ + + }, { + key: 'initialize', + value: function () {} + // NOOP + + + /** + * Returns a JSON version of the object suitable for saving to Parse. + * @method toJSON + * @return {Object} + */ + + }, { + key: 'toJSON', + value: function (seen) { + var seenEntry = this.id ? this.className + ':' + this.id : this; + var seen = seen || [seenEntry]; + var json = {}; + var attrs = this.attributes; + for (var attr in attrs) { + if ((attr === 'createdAt' || attr === 'updatedAt') && attrs[attr].toJSON) { + json[attr] = attrs[attr].toJSON(); + } else { + json[attr] = (0, _encode2.default)(attrs[attr], false, false, seen); + } + } + var pending = this._getPendingOps(); + for (var attr in pending[0]) { + json[attr] = pending[0][attr].toJSON(); + } + + if (this.id) { + json.objectId = this.id; + } + return json; + } + + /** + * Determines whether this ParseObject is equal to another ParseObject + * @method equals + * @return {Boolean} + */ + + }, { + key: 'equals', + value: function (other) { + if (this === other) { + return true; + } + return other instanceof ParseObject && this.className === other.className && this.id === other.id && typeof this.id !== 'undefined'; + } + + /** + * Returns true if this object has been modified since its last + * save/refresh. If an attribute is specified, it returns true only if that + * particular attribute has been modified since the last save/refresh. + * @method dirty + * @param {String} attr An attribute name (optional). + * @return {Boolean} + */ + + }, { + key: 'dirty', + value: function (attr) { + if (!this.id) { + return true; + } + var pendingOps = this._getPendingOps(); + var dirtyObjects = this._getDirtyObjectAttributes(); + if (attr) { + if (dirtyObjects.hasOwnProperty(attr)) { + return true; + } + for (var i = 0; i < pendingOps.length; i++) { + if (pendingOps[i].hasOwnProperty(attr)) { + return true; + } + } + return false; + } + if ((0, _keys2.default)(pendingOps[0]).length !== 0) { + return true; + } + if ((0, _keys2.default)(dirtyObjects).length !== 0) { + return true; + } + return false; + } + + /** + * Returns an array of keys that have been modified since last save/refresh + * @method dirtyKeys + * @return {Array of string} + */ + + }, { + key: 'dirtyKeys', + value: function () { + var pendingOps = this._getPendingOps(); + var keys = {}; + for (var i = 0; i < pendingOps.length; i++) { + for (var attr in pendingOps[i]) { + keys[attr] = true; + } + } + var dirtyObjects = this._getDirtyObjectAttributes(); + for (var attr in dirtyObjects) { + keys[attr] = true; + } + return (0, _keys2.default)(keys); + } + + /** + * Gets a Pointer referencing this Object. + * @method toPointer + * @return {Object} + */ + + }, { + key: 'toPointer', + value: function () { + if (!this.id) { + throw new Error('Cannot create a pointer to an unsaved ParseObject'); + } + return { + __type: 'Pointer', + className: this.className, + objectId: this.id + }; + } + + /** + * Gets the value of an attribute. + * @method get + * @param {String} attr The string name of an attribute. + */ + + }, { + key: 'get', + value: function (attr) { + return this.attributes[attr]; + } + + /** + * Gets a relation on the given class for the attribute. + * @method relation + * @param String attr The attribute to get the relation for. + */ + + }, { + key: 'relation', + value: function (attr) { + var value = this.get(attr); + if (value) { + if (!(value instanceof _ParseRelation2.default)) { + throw new Error('Called relation() on non-relation field ' + attr); + } + value._ensureParentAndKey(this, attr); + return value; + } + return new _ParseRelation2.default(this, attr); + } + + /** + * Gets the HTML-escaped value of an attribute. + * @method escape + * @param {String} attr The string name of an attribute. + */ + + }, { + key: 'escape', + value: function (attr) { + var val = this.attributes[attr]; + if (val == null) { + return ''; + } + + if (typeof val !== 'string') { + if (typeof val.toString !== 'function') { + return ''; + } + val = val.toString(); + } + return (0, _escape3.default)(val); + } + + /** + * Returns
true
if the attribute contains a value that is not
+ * null or undefined.
+ * @method has
+ * @param {String} attr The string name of the attribute.
+ * @return {Boolean}
+ */
+
+ }, {
+ key: 'has',
+ value: function (attr) {
+ var attributes = this.attributes;
+ if (attributes.hasOwnProperty(attr)) {
+ return attributes[attr] != null;
+ }
+ return false;
+ }
+
+ /**
+ * Sets a hash of model attributes on the object.
+ *
+ * You can call it with an object containing keys and values, or with one + * key and value. For example:
+ * gameTurn.set({ + * player: player1, + * diceRoll: 2 + * }, { + * error: function(gameTurnAgain, error) { + * // The set failed validation. + * } + * }); + * + * game.set("currentPlayer", player2, { + * error: function(gameTurnAgain, error) { + * // The set failed validation. + * } + * }); + * + * game.set("finished", true);+ * + * @method set + * @param {String} key The key to set. + * @param {} value The value to give it. + * @param {Object} options A set of options for the set. + * The only supported option is
error
.
+ * @return {Boolean} true if the set succeeded.
+ */
+
+ }, {
+ key: 'set',
+ value: function (key, value, options) {
+ var changes = {};
+ var newOps = {};
+ if (key && (typeof key === 'undefined' ? 'undefined' : (0, _typeof3.default)(key)) === 'object') {
+ changes = key;
+ options = value;
+ } else if (typeof key === 'string') {
+ changes[key] = value;
+ } else {
+ return this;
+ }
+
+ options = options || {};
+ var readonly = [];
+ if (typeof this.constructor.readOnlyAttributes === 'function') {
+ readonly = readonly.concat(this.constructor.readOnlyAttributes());
+ }
+ for (var k in changes) {
+ if (k === 'createdAt' || k === 'updatedAt') {
+ // This property is read-only, but for legacy reasons we silently
+ // ignore it
+ continue;
+ }
+ if (readonly.indexOf(k) > -1) {
+ throw new Error('Cannot modify readonly attribute: ' + k);
+ }
+ if (options.unset) {
+ newOps[k] = new _ParseOp.UnsetOp();
+ } else if (changes[k] instanceof _ParseOp.Op) {
+ newOps[k] = changes[k];
+ } else if (changes[k] && (0, _typeof3.default)(changes[k]) === 'object' && typeof changes[k].__op === 'string') {
+ newOps[k] = (0, _ParseOp.opFromJSON)(changes[k]);
+ } else if (k === 'objectId' || k === 'id') {
+ if (typeof changes[k] === 'string') {
+ this.id = changes[k];
+ }
+ } else if (k === 'ACL' && (0, _typeof3.default)(changes[k]) === 'object' && !(changes[k] instanceof _ParseACL2.default)) {
+ newOps[k] = new _ParseOp.SetOp(new _ParseACL2.default(changes[k]));
+ } else {
+ newOps[k] = new _ParseOp.SetOp(changes[k]);
+ }
+ }
+
+ // Calculate new values
+ var currentAttributes = this.attributes;
+ var newValues = {};
+ for (var attr in newOps) {
+ if (newOps[attr] instanceof _ParseOp.RelationOp) {
+ newValues[attr] = newOps[attr].applyTo(currentAttributes[attr], this, attr);
+ } else if (!(newOps[attr] instanceof _ParseOp.UnsetOp)) {
+ newValues[attr] = newOps[attr].applyTo(currentAttributes[attr]);
+ }
+ }
+
+ // Validate changes
+ if (!options.ignoreValidation) {
+ var validation = this.validate(newValues);
+ if (validation) {
+ if (typeof options.error === 'function') {
+ options.error(this, validation);
+ }
+ return false;
+ }
+ }
+
+ // Consolidate Ops
+ var pendingOps = this._getPendingOps();
+ var last = pendingOps.length - 1;
+ var stateController = _CoreManager2.default.getObjectStateController();
+ for (var attr in newOps) {
+ var nextOp = newOps[attr].mergeWith(pendingOps[last][attr]);
+ stateController.setPendingOp(this._getStateIdentifier(), attr, nextOp);
+ }
+
+ return this;
+ }
+
+ /**
+ * Remove an attribute from the model. This is a noop if the attribute doesn't
+ * exist.
+ * @method unset
+ * @param {String} attr The string name of an attribute.
+ */
+
+ }, {
+ key: 'unset',
+ value: function (attr, options) {
+ options = options || {};
+ options.unset = true;
+ return this.set(attr, null, options);
+ }
+
+ /**
+ * Atomically increments the value of the given attribute the next time the
+ * object is saved. If no amount is specified, 1 is used by default.
+ *
+ * @method increment
+ * @param attr {String} The key.
+ * @param amount {Number} The amount to increment by (optional).
+ */
+
+ }, {
+ key: 'increment',
+ value: function (attr, amount) {
+ if (typeof amount === 'undefined') {
+ amount = 1;
+ }
+ if (typeof amount !== 'number') {
+ throw new Error('Cannot increment by a non-numeric amount.');
+ }
+ return this.set(attr, new _ParseOp.IncrementOp(amount));
+ }
+
+ /**
+ * Atomically add an object to the end of the array associated with a given
+ * key.
+ * @method add
+ * @param attr {String} The key.
+ * @param item {} The item to add.
+ */
+
+ }, {
+ key: 'add',
+ value: function (attr, item) {
+ return this.set(attr, new _ParseOp.AddOp([item]));
+ }
+
+ /**
+ * Atomically add an object to the array associated with a given key, only
+ * if it is not already present in the array. The position of the insert is
+ * not guaranteed.
+ *
+ * @method addUnique
+ * @param attr {String} The key.
+ * @param item {} The object to add.
+ */
+
+ }, {
+ key: 'addUnique',
+ value: function (attr, item) {
+ return this.set(attr, new _ParseOp.AddUniqueOp([item]));
+ }
+
+ /**
+ * Atomically remove all instances of an object from the array associated
+ * with a given key.
+ *
+ * @method remove
+ * @param attr {String} The key.
+ * @param item {} The object to remove.
+ */
+
+ }, {
+ key: 'remove',
+ value: function (attr, item) {
+ return this.set(attr, new _ParseOp.RemoveOp([item]));
+ }
+
+ /**
+ * Returns an instance of a subclass of Parse.Op describing what kind of
+ * modification has been performed on this field since the last time it was
+ * saved. For example, after calling object.increment("x"), calling
+ * object.op("x") would return an instance of Parse.Op.Increment.
+ *
+ * @method op
+ * @param attr {String} The key.
+ * @returns {Parse.Op} The operation, or undefined if none.
+ */
+
+ }, {
+ key: 'op',
+ value: function (attr) {
+ var pending = this._getPendingOps();
+ for (var i = pending.length; i--;) {
+ if (pending[i][attr]) {
+ return pending[i][attr];
+ }
+ }
+ }
+
+ /**
+ * Creates a new model with identical attributes to this one, similar to Backbone.Model's clone()
+ * @method clone
+ * @return {Parse.Object}
+ */
+
+ }, {
+ key: 'clone',
+ value: function () {
+ var clone = new this.constructor();
+ if (!clone.className) {
+ clone.className = this.className;
+ }
+ var attributes = this.attributes;
+ if (typeof this.constructor.readOnlyAttributes === 'function') {
+ var readonly = this.constructor.readOnlyAttributes() || [];
+ // Attributes are frozen, so we have to rebuild an object,
+ // rather than delete readonly keys
+ var copy = {};
+ for (var a in attributes) {
+ if (readonly.indexOf(a) < 0) {
+ copy[a] = attributes[a];
+ }
+ }
+ attributes = copy;
+ }
+ if (clone.set) {
+ clone.set(attributes);
+ }
+ return clone;
+ }
+
+ /**
+ * Creates a new instance of this object. Not to be confused with clone()
+ * @method newInstance
+ * @return {Parse.Object}
+ */
+
+ }, {
+ key: 'newInstance',
+ value: function () {
+ var clone = new this.constructor();
+ if (!clone.className) {
+ clone.className = this.className;
+ }
+ clone.id = this.id;
+ if (singleInstance) {
+ // Just return an object with the right id
+ return clone;
+ }
+
+ var stateController = _CoreManager2.default.getObjectStateController();
+ if (stateController) {
+ stateController.duplicateState(this._getStateIdentifier(), clone._getStateIdentifier());
+ }
+ return clone;
+ }
+
+ /**
+ * Returns true if this object has never been saved to Parse.
+ * @method isNew
+ * @return {Boolean}
+ */
+
+ }, {
+ key: 'isNew',
+ value: function () {
+ return !this.id;
+ }
+
+ /**
+ * Returns true if this object was created by the Parse server when the
+ * object might have already been there (e.g. in the case of a Facebook
+ * login)
+ * @method existed
+ * @return {Boolean}
+ */
+
+ }, {
+ key: 'existed',
+ value: function () {
+ if (!this.id) {
+ return false;
+ }
+ var stateController = _CoreManager2.default.getObjectStateController();
+ var state = stateController.getState(this._getStateIdentifier());
+ if (state) {
+ return state.existed;
+ }
+ return false;
+ }
+
+ /**
+ * Checks if the model is currently in a valid state.
+ * @method isValid
+ * @return {Boolean}
+ */
+
+ }, {
+ key: 'isValid',
+ value: function () {
+ return !this.validate(this.attributes);
+ }
+
+ /**
+ * You should not call this function directly unless you subclass
+ * Parse.Object
, in which case you can override this method
+ * to provide additional validation on set
and
+ * save
. Your implementation should return
+ *
+ * @method validate
+ * @param {Object} attrs The current data to validate.
+ * @return {} False if the data is valid. An error object otherwise.
+ * @see Parse.Object#set
+ */
+
+ }, {
+ key: 'validate',
+ value: function (attrs) {
+ if (attrs.hasOwnProperty('ACL') && !(attrs.ACL instanceof _ParseACL2.default)) {
+ return new _ParseError2.default(_ParseError2.default.OTHER_CAUSE, 'ACL must be a Parse ACL.');
+ }
+ for (var key in attrs) {
+ if (!/^[A-Za-z][0-9A-Za-z_]*$/.test(key)) {
+ return new _ParseError2.default(_ParseError2.default.INVALID_KEY_NAME);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns the ACL for this object.
+ * @method getACL
+ * @returns {Parse.ACL} An instance of Parse.ACL.
+ * @see Parse.Object#get
+ */
+
+ }, {
+ key: 'getACL',
+ value: function () {
+ var acl = this.get('ACL');
+ if (acl instanceof _ParseACL2.default) {
+ return acl;
+ }
+ return null;
+ }
+
+ /**
+ * Sets the ACL to be used for this object.
+ * @method setACL
+ * @param {Parse.ACL} acl An instance of Parse.ACL.
+ * @param {Object} options Optional Backbone-like options object to be
+ * passed in to set.
+ * @return {Boolean} Whether the set passed validation.
+ * @see Parse.Object#set
+ */
+
+ }, {
+ key: 'setACL',
+ value: function (acl, options) {
+ return this.set('ACL', acl, options);
+ }
+
+ /**
+ * Clears any changes to this object made since the last call to save()
+ * @method revert
+ */
+
+ }, {
+ key: 'revert',
+ value: function () {
+ this._clearPendingOps();
+ }
+
+ /**
+ * Clears all attributes on a model
+ * @method clear
+ */
+
+ }, {
+ key: 'clear',
+ value: function () {
+ var attributes = this.attributes;
+ var erasable = {};
+ var readonly = ['createdAt', 'updatedAt'];
+ if (typeof this.constructor.readOnlyAttributes === 'function') {
+ readonly = readonly.concat(this.constructor.readOnlyAttributes());
+ }
+ for (var attr in attributes) {
+ if (readonly.indexOf(attr) < 0) {
+ erasable[attr] = true;
+ }
+ }
+ return this.set(erasable, { unset: true });
+ }
+
+ /**
+ * Fetch the model from the server. If the server's representation of the
+ * model differs from its current attributes, they will be overriden.
+ *
+ * @method fetch
+ * @param {Object} options A Backbone-style callback object.
+ * Valid options are:+ * object.save();+ * or
+ * object.save(null, options);+ * or
+ * object.save(attrs, options);+ * or
+ * object.save(key, value, options);+ * + * For example,
+ * gameTurn.save({ + * player: "Jake Cutter", + * diceRoll: 2 + * }, { + * success: function(gameTurnAgain) { + * // The save was successful. + * }, + * error: function(gameTurnAgain, error) { + * // The save failed. Error is an instance of Parse.Error. + * } + * });+ * or with promises:
+ * gameTurn.save({ + * player: "Jake Cutter", + * diceRoll: 2 + * }).then(function(gameTurnAgain) { + * // The save was successful. + * }, function(error) { + * // The save failed. Error is an instance of Parse.Error. + * });+ * + * @method save + * @param {Object} options A Backbone-style callback object. + * Valid options are:
+ * Parse.Object.fetchAll([object1, object2, ...], { + * success: function(list) { + * // All the objects were fetched. + * }, + * error: function(error) { + * // An error occurred while fetching one of the objects. + * }, + * }); + *+ * + * @method fetchAll + * @param {Array} list A list of
Parse.Object
.
+ * @param {Object} options A Backbone-style callback object.
+ * @static
+ * Valid options are:+ * Parse.Object.fetchAllIfNeeded([object1, ...], { + * success: function(list) { + * // Objects were fetched and updated. + * }, + * error: function(error) { + * // An error occurred while fetching one of the objects. + * }, + * }); + *+ * + * @method fetchAllIfNeeded + * @param {Array} list A list of
Parse.Object
.
+ * @param {Object} options A Backbone-style callback object.
+ * @static
+ * Valid options are:Unlike saveAll, if an error occurs while deleting an individual model, + * this method will continue trying to delete the rest of the models if + * possible, except in the case of a fatal error like a connection error. + * + *
In particular, the Parse.Error object returned in the case of error may + * be one of two types: + * + *
+ * Parse.Object.destroyAll([object1, object2, ...], { + * success: function() { + * // All the objects were deleted. + * }, + * error: function(error) { + * // An error occurred while deleting one or more of the objects. + * // If this is an aggregate error, then we can inspect each error + * // object individually to determine the reason why a particular + * // object was not deleted. + * if (error.code === Parse.Error.AGGREGATE_ERROR) { + * for (var i = 0; i < error.errors.length; i++) { + * console.log("Couldn't delete " + error.errors[i].object.id + + * "due to " + error.errors[i].message); + * } + * } else { + * console.log("Delete aborted because of " + error.message); + * } + * }, + * }); + *+ * + * @method destroyAll + * @param {Array} list A list of
Parse.Object
.
+ * @param {Object} options A Backbone-style callback object.
+ * @static
+ * Valid options are:+ * Parse.Object.saveAll([object1, object2, ...], { + * success: function(list) { + * // All the objects were saved. + * }, + * error: function(error) { + * // An error occurred while saving one of the objects. + * }, + * }); + *+ * + * @method saveAll + * @param {Array} list A list of
Parse.Object
.
+ * @param {Object} options A Backbone-style callback object.
+ * @static
+ * Valid options are:A shortcut for:
+ * var Foo = Parse.Object.extend("Foo"); + * var pointerToFoo = new Foo(); + * pointerToFoo.id = "myObjectId"; + *+ * + * @method createWithoutData + * @param {String} id The ID of the object to create a reference to. + * @static + * @return {Parse.Object} A Parse.Object reference. + */ + + }, { + key: 'createWithoutData', + value: function (id) { + var obj = new this(); + obj.id = id; + return obj; + } + + /** + * Creates a new instance of a Parse Object from a JSON representation. + * @method fromJSON + * @param {Object} json The JSON map of the Object's data + * @param {boolean} override In single instance mode, all old server data + * is overwritten if this is set to true + * @static + * @return {Parse.Object} A Parse.Object reference + */ + + }, { + key: 'fromJSON', + value: function (json, override) { + if (!json.className) { + throw new Error('Cannot create an object without a className'); + } + var constructor = classMap[json.className]; + var o = constructor ? new constructor() : new ParseObject(json.className); + var otherAttributes = {}; + for (var attr in json) { + if (attr !== 'className' && attr !== '__type') { + otherAttributes[attr] = json[attr]; + } + } + if (override) { + // id needs to be set before clearServerData can work + if (otherAttributes.objectId) { + o.id = otherAttributes.objectId; + } + var preserved = null; + if (typeof o._preserveFieldsOnFetch === 'function') { + preserved = o._preserveFieldsOnFetch(); + } + o._clearServerData(); + if (preserved) { + o._finishFetch(preserved); + } + } + o._finishFetch(otherAttributes); + if (json.objectId) { + o._setExisted(true); + } + return o; + } + + /** + * Registers a subclass of Parse.Object with a specific class name. + * When objects of that class are retrieved from a query, they will be + * instantiated with this subclass. + * This is only necessary when using ES6 subclassing. + * @method registerSubclass + * @param {String} className The class name of the subclass + * @param {Class} constructor The subclass + */ + + }, { + key: 'registerSubclass', + value: function (className, constructor) { + if (typeof className !== 'string') { + throw new TypeError('The first argument must be a valid class name.'); + } + if (typeof constructor === 'undefined') { + throw new TypeError('You must supply a subclass constructor.'); + } + if (typeof constructor !== 'function') { + throw new TypeError('You must register the subclass constructor. ' + 'Did you attempt to register an instance of the subclass?'); + } + classMap[className] = constructor; + if (!constructor.className) { + constructor.className = className; + } + } + + /** + * Creates a new subclass of Parse.Object for the given Parse class name. + * + *
Every extension of a Parse class will inherit from the most recent + * previous extension of that class. When a Parse.Object is automatically + * created by parsing JSON, it will use the most recent extension of that + * class.
+ * + *You should call either:
+ * var MyClass = Parse.Object.extend("MyClass", { + * Instance methods, + * initialize: function(attrs, options) { + * this.someInstanceProperty = [], + * Other instance properties + * } + * }, { + * Class properties + * });+ * or, for Backbone compatibility:
+ * var MyClass = Parse.Object.extend({ + * className: "MyClass", + * Instance methods, + * initialize: function(attrs, options) { + * this.someInstanceProperty = [], + * Other instance properties + * } + * }, { + * Class properties + * });+ * + * @method extend + * @param {String} className The name of the Parse class backing this model. + * @param {Object} protoProps Instance properties to add to instances of the + * class returned from this method. + * @param {Object} classProps Class properties to add the class returned from + * this method. + * @return {Class} A new subclass of Parse.Object. + */ + + }, { + key: 'extend', + value: function (className, protoProps, classProps) { + if (typeof className !== 'string') { + if (className && typeof className.className === 'string') { + return ParseObject.extend(className.className, className, protoProps); + } else { + throw new Error('Parse.Object.extend\'s first argument should be the className.'); + } + } + var adjustedClassName = className; + + if (adjustedClassName === 'User' && _CoreManager2.default.get('PERFORM_USER_REWRITE')) { + adjustedClassName = '_User'; + } + + var parentProto = ParseObject.prototype; + if (this.hasOwnProperty('__super__') && this.__super__) { + parentProto = this.prototype; + } else if (classMap[adjustedClassName]) { + parentProto = classMap[adjustedClassName].prototype; + } + var ParseObjectSubclass = function (attributes, options) { + this.className = adjustedClassName; + this._objCount = objectCount++; + // Enable legacy initializers + if (typeof this.initialize === 'function') { + this.initialize.apply(this, arguments); + } + + if (attributes && (typeof attributes === 'undefined' ? 'undefined' : (0, _typeof3.default)(attributes)) === 'object') { + if (!this.set(attributes || {}, options)) { + throw new Error('Can\'t create an invalid Parse Object'); + } + } + }; + ParseObjectSubclass.className = adjustedClassName; + ParseObjectSubclass.__super__ = parentProto; + + ParseObjectSubclass.prototype = (0, _create2.default)(parentProto, { + constructor: { + value: ParseObjectSubclass, + enumerable: false, + writable: true, + configurable: true + } + }); + + if (protoProps) { + for (var prop in protoProps) { + if (prop !== 'className') { + (0, _defineProperty2.default)(ParseObjectSubclass.prototype, prop, { + value: protoProps[prop], + enumerable: false, + writable: true, + configurable: true + }); + } + } + } + + if (classProps) { + for (var prop in classProps) { + if (prop !== 'className') { + (0, _defineProperty2.default)(ParseObjectSubclass, prop, { + value: classProps[prop], + enumerable: false, + writable: true, + configurable: true + }); + } + } + } + + ParseObjectSubclass.extend = function (name, protoProps, classProps) { + if (typeof name === 'string') { + return ParseObject.extend.call(ParseObjectSubclass, name, protoProps, classProps); + } + return ParseObject.extend.call(ParseObjectSubclass, adjustedClassName, name, protoProps); + }; + ParseObjectSubclass.createWithoutData = ParseObject.createWithoutData; + + classMap[adjustedClassName] = ParseObjectSubclass; + return ParseObjectSubclass; + } + + /** + * Enable single instance objects, where any local objects with the same Id + * share the same attributes, and stay synchronized with each other. + * This is disabled by default in server environments, since it can lead to + * security issues. + * @method enableSingleInstance + */ + + }, { + key: 'enableSingleInstance', + value: function () { + singleInstance = true; + _CoreManager2.default.setObjectStateController(SingleInstanceStateController); + } + + /** + * Disable single instance objects, where any local objects with the same Id + * share the same attributes, and stay synchronized with each other. + * When disabled, you can have two instances of the same object in memory + * without them sharing attributes. + * @method disableSingleInstance + */ + + }, { + key: 'disableSingleInstance', + value: function () { + singleInstance = false; + _CoreManager2.default.setObjectStateController(UniqueInstanceStateController); + } + }]); + return ParseObject; +}(); + +exports.default = ParseObject; + +var DefaultController = { + fetch: function (target, forceFetch, options) { + if (Array.isArray(target)) { + if (target.length < 1) { + return _ParsePromise2.default.as([]); + } + var objs = []; + var ids = []; + var className = null; + var results = []; + var error = null; + target.forEach(function (el, i) { + if (error) { + return; + } + if (!className) { + className = el.className; + } + if (className !== el.className) { + error = new _ParseError2.default(_ParseError2.default.INVALID_CLASS_NAME, 'All objects should be of the same class'); + } + if (!el.id) { + error = new _ParseError2.default(_ParseError2.default.MISSING_OBJECT_ID, 'All objects must have an ID'); + } + if (forceFetch || (0, _keys2.default)(el._getServerData()).length === 0) { + ids.push(el.id); + objs.push(el); + } + results.push(el); + }); + if (error) { + return _ParsePromise2.default.error(error); + } + var query = new _ParseQuery2.default(className); + query.containedIn('objectId', ids); + query._limit = ids.length; + return query.find(options).then(function (objects) { + var idMap = {}; + objects.forEach(function (o) { + idMap[o.id] = o; + }); + for (var i = 0; i < objs.length; i++) { + var obj = objs[i]; + if (!obj || !obj.id || !idMap[obj.id]) { + if (forceFetch) { + return _ParsePromise2.default.error(new _ParseError2.default(_ParseError2.default.OBJECT_NOT_FOUND, 'All objects must exist on the server.')); + } + } + } + if (!singleInstance) { + // If single instance objects are disabled, we need to replace the + for (var i = 0; i < results.length; i++) { + var obj = results[i]; + if (obj && obj.id && idMap[obj.id]) { + var id = obj.id; + obj._finishFetch(idMap[id].toJSON()); + results[i] = idMap[id]; + } + } + } + return _ParsePromise2.default.as(results); + }); + } else { + var RESTController = _CoreManager2.default.getRESTController(); + return RESTController.request('GET', 'classes/' + target.className + '/' + target._getId(), {}, options).then(function (response, status, xhr) { + if (target instanceof ParseObject) { + target._clearPendingOps(); + target._clearServerData(); + target._finishFetch(response); + } + return target; + }); + } + }, + destroy: function (target, options) { + var RESTController = _CoreManager2.default.getRESTController(); + if (Array.isArray(target)) { + if (target.length < 1) { + return _ParsePromise2.default.as([]); + } + var batches = [[]]; + target.forEach(function (obj) { + if (!obj.id) { + return; + } + batches[batches.length - 1].push(obj); + if (batches[batches.length - 1].length >= 20) { + batches.push([]); + } + }); + if (batches[batches.length - 1].length === 0) { + // If the last batch is empty, remove it + batches.pop(); + } + var deleteCompleted = _ParsePromise2.default.as(); + var errors = []; + batches.forEach(function (batch) { + deleteCompleted = deleteCompleted.then(function () { + return RESTController.request('POST', 'batch', { + requests: batch.map(function (obj) { + return { + method: 'DELETE', + path: getServerUrlPath() + 'classes/' + obj.className + '/' + obj._getId(), + body: {} + }; + }) + }, options).then(function (results) { + for (var i = 0; i < results.length; i++) { + if (results[i] && results[i].hasOwnProperty('error')) { + var err = new _ParseError2.default(results[i].error.code, results[i].error.error); + err.object = batch[i]; + errors.push(err); + } + } + }); + }); + }); + return deleteCompleted.then(function () { + if (errors.length) { + var aggregate = new _ParseError2.default(_ParseError2.default.AGGREGATE_ERROR); + aggregate.errors = errors; + return _ParsePromise2.default.error(aggregate); + } + return _ParsePromise2.default.as(target); + }); + } else if (target instanceof ParseObject) { + return RESTController.request('DELETE', 'classes/' + target.className + '/' + target._getId(), {}, options).then(function () { + return _ParsePromise2.default.as(target); + }); + } + return _ParsePromise2.default.as(target); + }, + save: function (target, options) { + var RESTController = _CoreManager2.default.getRESTController(); + var stateController = _CoreManager2.default.getObjectStateController(); + if (Array.isArray(target)) { + if (target.length < 1) { + return _ParsePromise2.default.as([]); + } + + var unsaved = target.concat(); + for (var i = 0; i < target.length; i++) { + if (target[i] instanceof ParseObject) { + unsaved = unsaved.concat((0, _unsavedChildren2.default)(target[i], true)); + } + } + unsaved = (0, _unique2.default)(unsaved); + + var filesSaved = _ParsePromise2.default.as(); + var pending = []; + unsaved.forEach(function (el) { + if (el instanceof _ParseFile2.default) { + filesSaved = filesSaved.then(function () { + return el.save(); + }); + } else if (el instanceof ParseObject) { + pending.push(el); + } + }); + + return filesSaved.then(function () { + var objectError = null; + return _ParsePromise2.default._continueWhile(function () { + return pending.length > 0; + }, function () { + var batch = []; + var nextPending = []; + pending.forEach(function (el) { + if (batch.length < 20 && (0, _canBeSerialized2.default)(el)) { + batch.push(el); + } else { + nextPending.push(el); + } + }); + pending = nextPending; + if (batch.length < 1) { + return _ParsePromise2.default.error(new _ParseError2.default(_ParseError2.default.OTHER_CAUSE, 'Tried to save a batch with a cycle.')); + } + + // Queue up tasks for each object in the batch. + // When every task is ready, the API request will execute + var batchReturned = new _ParsePromise2.default(); + var batchReady = []; + var batchTasks = []; + batch.forEach(function (obj, index) { + var ready = new _ParsePromise2.default(); + batchReady.push(ready); + + stateController.pushPendingState(obj._getStateIdentifier()); + batchTasks.push(stateController.enqueueTask(obj._getStateIdentifier(), function () { + ready.resolve(); + return batchReturned.then(function (responses, status) { + if (responses[index].hasOwnProperty('success')) { + obj._handleSaveResponse(responses[index].success, status); + } else { + if (!objectError && responses[index].hasOwnProperty('error')) { + var serverError = responses[index].error; + objectError = new _ParseError2.default(serverError.code, serverError.error); + // Cancel the rest of the save + pending = []; + } + obj._handleSaveError(); + } + }); + })); + }); + + _ParsePromise2.default.when(batchReady).then(function () { + // Kick off the batch request + return RESTController.request('POST', 'batch', { + requests: batch.map(function (obj) { + var params = obj._getSaveParams(); + params.path = getServerUrlPath() + params.path; + return params; + }) + }, options); + }).then(function (response, status, xhr) { + batchReturned.resolve(response, status); + }); + + return _ParsePromise2.default.when(batchTasks); + }).then(function () { + if (objectError) { + return _ParsePromise2.default.error(objectError); + } + return _ParsePromise2.default.as(target); + }); + }); + } else if (target instanceof ParseObject) { + // copying target lets Flow guarantee the pointer isn't modified elsewhere + var targetCopy = target; + var task = function () { + var params = targetCopy._getSaveParams(); + return RESTController.request(params.method, params.path, params.body, options).then(function (response, status) { + targetCopy._handleSaveResponse(response, status); + }, function (error) { + targetCopy._handleSaveError(); + return _ParsePromise2.default.error(error); + }); + }; + + stateController.pushPendingState(target._getStateIdentifier()); + return stateController.enqueueTask(target._getStateIdentifier(), task).then(function () { + return target; + }, function (error) { + return _ParsePromise2.default.error(error); + }); + } + return _ParsePromise2.default.as(); + } +}; + +_CoreManager2.default.setObjectController(DefaultController); \ No newline at end of file diff --git a/lib/node/ParseOp.js b/lib/node/ParseOp.js new file mode 100644 index 000000000..1eba19303 --- /dev/null +++ b/lib/node/ParseOp.js @@ -0,0 +1,579 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.RelationOp = exports.RemoveOp = exports.AddUniqueOp = exports.AddOp = exports.IncrementOp = exports.UnsetOp = exports.SetOp = exports.Op = undefined; + +var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); + +var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); + +var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); + +var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); + +var _inherits2 = require('babel-runtime/helpers/inherits'); + +var _inherits3 = _interopRequireDefault(_inherits2); + +var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); + +var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); + +var _createClass2 = require('babel-runtime/helpers/createClass'); + +var _createClass3 = _interopRequireDefault(_createClass2); + +exports.opFromJSON = opFromJSON; + +var _arrayContainsObject = require('./arrayContainsObject'); + +var _arrayContainsObject2 = _interopRequireDefault(_arrayContainsObject); + +var _decode = require('./decode'); + +var _decode2 = _interopRequireDefault(_decode); + +var _encode = require('./encode'); + +var _encode2 = _interopRequireDefault(_encode); + +var _ParseObject = require('./ParseObject'); + +var _ParseObject2 = _interopRequireDefault(_ParseObject); + +var _ParseRelation = require('./ParseRelation'); + +var _ParseRelation2 = _interopRequireDefault(_ParseRelation); + +var _unique = require('./unique'); + +var _unique2 = _interopRequireDefault(_unique); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { default: obj }; +} + +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + */ + +function opFromJSON(json) { + if (!json || !json.__op) { + return null; + } + switch (json.__op) { + case 'Delete': + return new UnsetOp(); + case 'Increment': + return new IncrementOp(json.amount); + case 'Add': + return new AddOp((0, _decode2.default)(json.objects)); + case 'AddUnique': + return new AddUniqueOp((0, _decode2.default)(json.objects)); + case 'Remove': + return new RemoveOp((0, _decode2.default)(json.objects)); + case 'AddRelation': + var toAdd = (0, _decode2.default)(json.objects); + if (!Array.isArray(toAdd)) { + return new RelationOp([], []); + } + return new RelationOp(toAdd, []); + case 'RemoveRelation': + var toRemove = (0, _decode2.default)(json.objects); + if (!Array.isArray(toRemove)) { + return new RelationOp([], []); + } + return new RelationOp([], toRemove); + case 'Batch': + var toAdd = []; + var toRemove = []; + for (var i = 0; i < json.ops.length; i++) { + if (json.ops[i].__op === 'AddRelation') { + toAdd = toAdd.concat((0, _decode2.default)(json.ops[i].objects)); + } else if (json.ops[i].__op === 'RemoveRelation') { + toRemove = toRemove.concat((0, _decode2.default)(json.ops[i].objects)); + } + } + return new RelationOp(toAdd, toRemove); + } + return null; +} + +var Op = exports.Op = function () { + function Op() { + (0, _classCallCheck3.default)(this, Op); + } + + (0, _createClass3.default)(Op, [{ + key: 'applyTo', + + // Empty parent class + value: function (value) {} + }, { + key: 'mergeWith', + value: function (previous) {} + }, { + key: 'toJSON', + value: function () {} + }]); + return Op; +}(); + +var SetOp = exports.SetOp = function (_Op) { + (0, _inherits3.default)(SetOp, _Op); + + function SetOp(value) { + (0, _classCallCheck3.default)(this, SetOp); + + var _this = (0, _possibleConstructorReturn3.default)(this, (SetOp.__proto__ || (0, _getPrototypeOf2.default)(SetOp)).call(this)); + + _this._value = value; + return _this; + } + + (0, _createClass3.default)(SetOp, [{ + key: 'applyTo', + value: function (value) { + return this._value; + } + }, { + key: 'mergeWith', + value: function (previous) { + return new SetOp(this._value); + } + }, { + key: 'toJSON', + value: function () { + return (0, _encode2.default)(this._value, false, true); + } + }]); + return SetOp; +}(Op); + +var UnsetOp = exports.UnsetOp = function (_Op2) { + (0, _inherits3.default)(UnsetOp, _Op2); + + function UnsetOp() { + (0, _classCallCheck3.default)(this, UnsetOp); + return (0, _possibleConstructorReturn3.default)(this, (UnsetOp.__proto__ || (0, _getPrototypeOf2.default)(UnsetOp)).apply(this, arguments)); + } + + (0, _createClass3.default)(UnsetOp, [{ + key: 'applyTo', + value: function (value) { + return undefined; + } + }, { + key: 'mergeWith', + value: function (previous) { + return new UnsetOp(); + } + }, { + key: 'toJSON', + value: function () { + return { __op: 'Delete' }; + } + }]); + return UnsetOp; +}(Op); + +var IncrementOp = exports.IncrementOp = function (_Op3) { + (0, _inherits3.default)(IncrementOp, _Op3); + + function IncrementOp(amount) { + (0, _classCallCheck3.default)(this, IncrementOp); + + var _this3 = (0, _possibleConstructorReturn3.default)(this, (IncrementOp.__proto__ || (0, _getPrototypeOf2.default)(IncrementOp)).call(this)); + + if (typeof amount !== 'number') { + throw new TypeError('Increment Op must be initialized with a numeric amount.'); + } + _this3._amount = amount; + return _this3; + } + + (0, _createClass3.default)(IncrementOp, [{ + key: 'applyTo', + value: function (value) { + if (typeof value === 'undefined') { + return this._amount; + } + if (typeof value !== 'number') { + throw new TypeError('Cannot increment a non-numeric value.'); + } + return this._amount + value; + } + }, { + key: 'mergeWith', + value: function (previous) { + if (!previous) { + return this; + } + if (previous instanceof SetOp) { + return new SetOp(this.applyTo(previous._value)); + } + if (previous instanceof UnsetOp) { + return new SetOp(this._amount); + } + if (previous instanceof IncrementOp) { + return new IncrementOp(this.applyTo(previous._amount)); + } + throw new Error('Cannot merge Increment Op with the previous Op'); + } + }, { + key: 'toJSON', + value: function () { + return { __op: 'Increment', amount: this._amount }; + } + }]); + return IncrementOp; +}(Op); + +var AddOp = exports.AddOp = function (_Op4) { + (0, _inherits3.default)(AddOp, _Op4); + + function AddOp(value) { + (0, _classCallCheck3.default)(this, AddOp); + + var _this4 = (0, _possibleConstructorReturn3.default)(this, (AddOp.__proto__ || (0, _getPrototypeOf2.default)(AddOp)).call(this)); + + _this4._value = Array.isArray(value) ? value : [value]; + return _this4; + } + + (0, _createClass3.default)(AddOp, [{ + key: 'applyTo', + value: function (value) { + if (value == null) { + return this._value; + } + if (Array.isArray(value)) { + return value.concat(this._value); + } + throw new Error('Cannot add elements to a non-array value'); + } + }, { + key: 'mergeWith', + value: function (previous) { + if (!previous) { + return this; + } + if (previous instanceof SetOp) { + return new SetOp(this.applyTo(previous._value)); + } + if (previous instanceof UnsetOp) { + return new SetOp(this._value); + } + if (previous instanceof AddOp) { + return new AddOp(this.applyTo(previous._value)); + } + throw new Error('Cannot merge Add Op with the previous Op'); + } + }, { + key: 'toJSON', + value: function () { + return { __op: 'Add', objects: (0, _encode2.default)(this._value, false, true) }; + } + }]); + return AddOp; +}(Op); + +var AddUniqueOp = exports.AddUniqueOp = function (_Op5) { + (0, _inherits3.default)(AddUniqueOp, _Op5); + + function AddUniqueOp(value) { + (0, _classCallCheck3.default)(this, AddUniqueOp); + + var _this5 = (0, _possibleConstructorReturn3.default)(this, (AddUniqueOp.__proto__ || (0, _getPrototypeOf2.default)(AddUniqueOp)).call(this)); + + _this5._value = (0, _unique2.default)(Array.isArray(value) ? value : [value]); + return _this5; + } + + (0, _createClass3.default)(AddUniqueOp, [{ + key: 'applyTo', + value: function (value) { + if (value == null) { + return this._value || []; + } + if (Array.isArray(value)) { + // copying value lets Flow guarantee the pointer isn't modified elsewhere + var valueCopy = value; + var toAdd = []; + this._value.forEach(function (v) { + if (v instanceof _ParseObject2.default) { + if (!(0, _arrayContainsObject2.default)(valueCopy, v)) { + toAdd.push(v); + } + } else { + if (valueCopy.indexOf(v) < 0) { + toAdd.push(v); + } + } + }); + return value.concat(toAdd); + } + throw new Error('Cannot add elements to a non-array value'); + } + }, { + key: 'mergeWith', + value: function (previous) { + if (!previous) { + return this; + } + if (previous instanceof SetOp) { + return new SetOp(this.applyTo(previous._value)); + } + if (previous instanceof UnsetOp) { + return new SetOp(this._value); + } + if (previous instanceof AddUniqueOp) { + return new AddUniqueOp(this.applyTo(previous._value)); + } + throw new Error('Cannot merge AddUnique Op with the previous Op'); + } + }, { + key: 'toJSON', + value: function () { + return { __op: 'AddUnique', objects: (0, _encode2.default)(this._value, false, true) }; + } + }]); + return AddUniqueOp; +}(Op); + +var RemoveOp = exports.RemoveOp = function (_Op6) { + (0, _inherits3.default)(RemoveOp, _Op6); + + function RemoveOp(value) { + (0, _classCallCheck3.default)(this, RemoveOp); + + var _this6 = (0, _possibleConstructorReturn3.default)(this, (RemoveOp.__proto__ || (0, _getPrototypeOf2.default)(RemoveOp)).call(this)); + + _this6._value = (0, _unique2.default)(Array.isArray(value) ? value : [value]); + return _this6; + } + + (0, _createClass3.default)(RemoveOp, [{ + key: 'applyTo', + value: function (value) { + if (value == null) { + return []; + } + if (Array.isArray(value)) { + var i = value.indexOf(this._value); + var removed = value.concat([]); + for (var i = 0; i < this._value.length; i++) { + var index = removed.indexOf(this._value[i]); + while (index > -1) { + removed.splice(index, 1); + index = removed.indexOf(this._value[i]); + } + if (this._value[i] instanceof _ParseObject2.default && this._value[i].id) { + for (var j = 0; j < removed.length; j++) { + if (removed[j] instanceof _ParseObject2.default && this._value[i].id === removed[j].id) { + removed.splice(j, 1); + j--; + } + } + } + } + return removed; + } + throw new Error('Cannot remove elements from a non-array value'); + } + }, { + key: 'mergeWith', + value: function (previous) { + if (!previous) { + return this; + } + if (previous instanceof SetOp) { + return new SetOp(this.applyTo(previous._value)); + } + if (previous instanceof UnsetOp) { + return new UnsetOp(); + } + if (previous instanceof RemoveOp) { + var uniques = previous._value.concat([]); + for (var i = 0; i < this._value.length; i++) { + if (this._value[i] instanceof _ParseObject2.default) { + if (!(0, _arrayContainsObject2.default)(uniques, this._value[i])) { + uniques.push(this._value[i]); + } + } else { + if (uniques.indexOf(this._value[i]) < 0) { + uniques.push(this._value[i]); + } + } + } + return new RemoveOp(uniques); + } + throw new Error('Cannot merge Remove Op with the previous Op'); + } + }, { + key: 'toJSON', + value: function () { + return { __op: 'Remove', objects: (0, _encode2.default)(this._value, false, true) }; + } + }]); + return RemoveOp; +}(Op); + +var RelationOp = exports.RelationOp = function (_Op7) { + (0, _inherits3.default)(RelationOp, _Op7); + + function RelationOp(adds, removes) { + (0, _classCallCheck3.default)(this, RelationOp); + + var _this7 = (0, _possibleConstructorReturn3.default)(this, (RelationOp.__proto__ || (0, _getPrototypeOf2.default)(RelationOp)).call(this)); + + _this7._targetClassName = null; + + if (Array.isArray(adds)) { + _this7.relationsToAdd = (0, _unique2.default)(adds.map(_this7._extractId, _this7)); + } + + if (Array.isArray(removes)) { + _this7.relationsToRemove = (0, _unique2.default)(removes.map(_this7._extractId, _this7)); + } + return _this7; + } + + (0, _createClass3.default)(RelationOp, [{ + key: '_extractId', + value: function (obj) { + if (typeof obj === 'string') { + return obj; + } + if (!obj.id) { + throw new Error('You cannot add or remove an unsaved Parse Object from a relation'); + } + if (!this._targetClassName) { + this._targetClassName = obj.className; + } + if (this._targetClassName !== obj.className) { + throw new Error('Tried to create a Relation with 2 different object types: ' + this._targetClassName + ' and ' + obj.className + '.'); + } + return obj.id; + } + }, { + key: 'applyTo', + value: function (value, object, key) { + if (!value) { + if (!object || !key) { + throw new Error('Cannot apply a RelationOp without either a previous value, or an object and a key'); + } + var parent = new _ParseObject2.default(object.className); + if (object.id && object.id.indexOf('local') === 0) { + parent._localId = object.id; + } else if (object.id) { + parent.id = object.id; + } + var relation = new _ParseRelation2.default(parent, key); + relation.targetClassName = this._targetClassName; + return relation; + } + if (value instanceof _ParseRelation2.default) { + if (this._targetClassName) { + if (value.targetClassName) { + if (this._targetClassName !== value.targetClassName) { + throw new Error('Related object must be a ' + value.targetClassName + ', but a ' + this._targetClassName + ' was passed in.'); + } + } else { + value.targetClassName = this._targetClassName; + } + } + return value; + } else { + throw new Error('Relation cannot be applied to a non-relation field'); + } + } + }, { + key: 'mergeWith', + value: function (previous) { + if (!previous) { + return this; + } else if (previous instanceof UnsetOp) { + throw new Error('You cannot modify a relation after deleting it.'); + } else if (previous instanceof RelationOp) { + if (previous._targetClassName && previous._targetClassName !== this._targetClassName) { + throw new Error('Related object must be of class ' + previous._targetClassName + ', but ' + (this._targetClassName || 'null') + ' was passed in.'); + } + var newAdd = previous.relationsToAdd.concat([]); + this.relationsToRemove.forEach(function (r) { + var index = newAdd.indexOf(r); + if (index > -1) { + newAdd.splice(index, 1); + } + }); + this.relationsToAdd.forEach(function (r) { + var index = newAdd.indexOf(r); + if (index < 0) { + newAdd.push(r); + } + }); + + var newRemove = previous.relationsToRemove.concat([]); + this.relationsToAdd.forEach(function (r) { + var index = newRemove.indexOf(r); + if (index > -1) { + newRemove.splice(index, 1); + } + }); + this.relationsToRemove.forEach(function (r) { + var index = newRemove.indexOf(r); + if (index < 0) { + newRemove.push(r); + } + }); + + var newRelation = new RelationOp(newAdd, newRemove); + newRelation._targetClassName = this._targetClassName; + return newRelation; + } + throw new Error('Cannot merge Relation Op with the previous Op'); + } + }, { + key: 'toJSON', + value: function () { + var _this8 = this; + + var idToPointer = function (id) { + return { + __type: 'Pointer', + className: _this8._targetClassName, + objectId: id + }; + }; + + var adds = null; + var removes = null; + var pointers = null; + + if (this.relationsToAdd.length > 0) { + pointers = this.relationsToAdd.map(idToPointer); + adds = { __op: 'AddRelation', objects: pointers }; + } + if (this.relationsToRemove.length > 0) { + pointers = this.relationsToRemove.map(idToPointer); + removes = { __op: 'RemoveRelation', objects: pointers }; + } + + if (adds && removes) { + return { __op: 'Batch', ops: [adds, removes] }; + } + + return adds || removes || {}; + } + }]); + return RelationOp; +}(Op); \ No newline at end of file diff --git a/lib/node/ParsePromise.js b/lib/node/ParsePromise.js new file mode 100644 index 000000000..a79b4afee --- /dev/null +++ b/lib/node/ParsePromise.js @@ -0,0 +1,740 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _getIterator2 = require('babel-runtime/core-js/get-iterator'); + +var _getIterator3 = _interopRequireDefault(_getIterator2); + +var _typeof2 = require('babel-runtime/helpers/typeof'); + +var _typeof3 = _interopRequireDefault(_typeof2); + +var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); + +var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); + +var _createClass2 = require('babel-runtime/helpers/createClass'); + +var _createClass3 = _interopRequireDefault(_createClass2); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { default: obj }; +} + +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +var _isPromisesAPlusCompliant = true; + +/** + * A Promise is returned by async methods as a hook to provide callbacks to be + * called when the async task is fulfilled. + * + *
Typical usage would be like:
+ * query.find().then(function(results) { + * results[0].set("foo", "bar"); + * return results[0].saveAsync(); + * }).then(function(result) { + * console.log("Updated " + result.id); + * }); + *+ * + * @class Parse.Promise + * @constructor + */ + +var ParsePromise = function () { + function ParsePromise(executor) { + (0, _classCallCheck3.default)(this, ParsePromise); + + this._resolved = false; + this._rejected = false; + this._resolvedCallbacks = []; + this._rejectedCallbacks = []; + + if (typeof executor === 'function') { + executor(this.resolve.bind(this), this.reject.bind(this)); + } + } + + /** + * Marks this promise as fulfilled, firing any callbacks waiting on it. + * @method resolve + * @param {Object} result the result to pass to the callbacks. + */ + + (0, _createClass3.default)(ParsePromise, [{ + key: 'resolve', + value: function () { + if (this._resolved || this._rejected) { + throw new Error('A promise was resolved even though it had already been ' + (this._resolved ? 'resolved' : 'rejected') + '.'); + } + this._resolved = true; + + for (var _len = arguments.length, results = Array(_len), _key = 0; _key < _len; _key++) { + results[_key] = arguments[_key]; + } + + this._result = results; + for (var i = 0; i < this._resolvedCallbacks.length; i++) { + this._resolvedCallbacks[i].apply(this, results); + } + + this._resolvedCallbacks = []; + this._rejectedCallbacks = []; + } + + /** + * Marks this promise as fulfilled, firing any callbacks waiting on it. + * @method reject + * @param {Object} error the error to pass to the callbacks. + */ + + }, { + key: 'reject', + value: function (error) { + if (this._resolved || this._rejected) { + throw new Error('A promise was rejected even though it had already been ' + (this._resolved ? 'resolved' : 'rejected') + '.'); + } + this._rejected = true; + this._error = error; + for (var i = 0; i < this._rejectedCallbacks.length; i++) { + this._rejectedCallbacks[i](error); + } + this._resolvedCallbacks = []; + this._rejectedCallbacks = []; + } + + /** + * Adds callbacks to be called when this promise is fulfilled. Returns a new + * Promise that will be fulfilled when the callback is complete. It allows + * chaining. If the callback itself returns a Promise, then the one returned + * by "then" will not be fulfilled until that one returned by the callback + * is fulfilled. + * @method then + * @param {Function} resolvedCallback Function that is called when this + * Promise is resolved. Once the callback is complete, then the Promise + * returned by "then" will also be fulfilled. + * @param {Function} rejectedCallback Function that is called when this + * Promise is rejected with an error. Once the callback is complete, then + * the promise returned by "then" with be resolved successfully. If + * rejectedCallback is null, or it returns a rejected Promise, then the + * Promise returned by "then" will be rejected with that error. + * @return {Parse.Promise} A new Promise that will be fulfilled after this + * Promise is fulfilled and either callback has completed. If the callback + * returned a Promise, then this Promise will not be fulfilled until that + * one is. + */ + + }, { + key: 'then', + value: function (resolvedCallback, rejectedCallback) { + var _this = this; + + var promise = new ParsePromise(); + + var wrappedResolvedCallback = function () { + for (var _len2 = arguments.length, results = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { + results[_key2] = arguments[_key2]; + } + + if (typeof resolvedCallback === 'function') { + if (_isPromisesAPlusCompliant) { + try { + results = [resolvedCallback.apply(this, results)]; + } catch (e) { + results = [ParsePromise.error(e)]; + } + } else { + results = [resolvedCallback.apply(this, results)]; + } + } + if (results.length === 1 && ParsePromise.is(results[0])) { + results[0].then(function () { + promise.resolve.apply(promise, arguments); + }, function (error) { + promise.reject(error); + }); + } else { + promise.resolve.apply(promise, results); + } + }; + + var wrappedRejectedCallback = function (error) { + var result = []; + if (typeof rejectedCallback === 'function') { + if (_isPromisesAPlusCompliant) { + try { + result = [rejectedCallback(error)]; + } catch (e) { + result = [ParsePromise.error(e)]; + } + } else { + result = [rejectedCallback(error)]; + } + if (result.length === 1 && ParsePromise.is(result[0])) { + result[0].then(function () { + promise.resolve.apply(promise, arguments); + }, function (error) { + promise.reject(error); + }); + } else { + if (_isPromisesAPlusCompliant) { + promise.resolve.apply(promise, result); + } else { + promise.reject(result[0]); + } + } + } else { + promise.reject(error); + } + }; + + var runLater = function (fn) { + fn.call(); + }; + if (_isPromisesAPlusCompliant) { + if (typeof process !== 'undefined' && typeof process.nextTick === 'function') { + runLater = function (fn) { + process.nextTick(fn); + }; + } else if (typeof setTimeout === 'function') { + runLater = function (fn) { + setTimeout(fn, 0); + }; + } + } + + if (this._resolved) { + runLater(function () { + wrappedResolvedCallback.apply(_this, _this._result); + }); + } else if (this._rejected) { + runLater(function () { + wrappedRejectedCallback(_this._error); + }); + } else { + this._resolvedCallbacks.push(wrappedResolvedCallback); + this._rejectedCallbacks.push(wrappedRejectedCallback); + } + + return promise; + } + + /** + * Add handlers to be called when the promise + * is either resolved or rejected + * @method always + */ + + }, { + key: 'always', + value: function (callback) { + return this.then(callback, callback); + } + + /** + * Add handlers to be called when the Promise object is resolved + * @method done + */ + + }, { + key: 'done', + value: function (callback) { + return this.then(callback); + } + + /** + * Add handlers to be called when the Promise object is rejected + * Alias for catch(). + * @method fail + */ + + }, { + key: 'fail', + value: function (callback) { + return this.then(null, callback); + } + + /** + * Add handlers to be called when the Promise object is rejected + * @method catch + */ + + }, { + key: 'catch', + value: function (callback) { + return this.then(null, callback); + } + + /** + * Run the given callbacks after this promise is fulfilled. + * @method _thenRunCallbacks + * @param optionsOrCallback {} A Backbone-style options callback, or a + * callback function. If this is an options object and contains a "model" + * attributes, that will be passed to error callbacks as the first argument. + * @param model {} If truthy, this will be passed as the first result of + * error callbacks. This is for Backbone-compatability. + * @return {Parse.Promise} A promise that will be resolved after the + * callbacks are run, with the same result as this. + */ + + }, { + key: '_thenRunCallbacks', + value: function (optionsOrCallback, model) { + var options = {}; + if (typeof optionsOrCallback === 'function') { + options.success = function (result) { + optionsOrCallback(result, null); + }; + options.error = function (error) { + optionsOrCallback(null, error); + }; + } else if ((typeof optionsOrCallback === 'undefined' ? 'undefined' : (0, _typeof3.default)(optionsOrCallback)) === 'object') { + if (typeof optionsOrCallback.success === 'function') { + options.success = optionsOrCallback.success; + } + if (typeof optionsOrCallback.error === 'function') { + options.error = optionsOrCallback.error; + } + } + + return this.then(function () { + for (var _len3 = arguments.length, results = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) { + results[_key3] = arguments[_key3]; + } + + if (options.success) { + options.success.apply(this, results); + } + return ParsePromise.as.apply(ParsePromise, arguments); + }, function (error) { + if (options.error) { + if (typeof model !== 'undefined') { + options.error(model, error); + } else { + options.error(error); + } + } + // By explicitly returning a rejected Promise, this will work with + // either jQuery or Promises/A+ semantics. + return ParsePromise.error(error); + }); + } + + /** + * Adds a callback function that should be called regardless of whether + * this promise failed or succeeded. The callback will be given either the + * array of results for its first argument, or the error as its second, + * depending on whether this Promise was rejected or resolved. Returns a + * new Promise, like "then" would. + * @method _continueWith + * @param {Function} continuation the callback. + */ + + }, { + key: '_continueWith', + value: function (continuation) { + return this.then(function () { + for (var _len4 = arguments.length, args = Array(_len4), _key4 = 0; _key4 < _len4; _key4++) { + args[_key4] = arguments[_key4]; + } + + return continuation(args, null); + }, function (error) { + return continuation(null, error); + }); + } + + /** + * Returns true iff the given object fulfils the Promise interface. + * @method is + * @param {Object} promise The object to test + * @static + * @return {Boolean} + */ + + }], [{ + key: 'is', + value: function (promise) { + return promise != null && typeof promise.then === 'function'; + } + + /** + * Returns a new promise that is resolved with a given value. + * @method as + * @param value The value to resolve the promise with + * @static + * @return {Parse.Promise} the new promise. + */ + + }, { + key: 'as', + value: function () { + var promise = new ParsePromise(); + + for (var _len5 = arguments.length, values = Array(_len5), _key5 = 0; _key5 < _len5; _key5++) { + values[_key5] = arguments[_key5]; + } + + promise.resolve.apply(promise, values); + return promise; + } + + /** + * Returns a new promise that is resolved with a given value. + * If that value is a thenable Promise (has a .then() prototype + * method), the new promise will be chained to the end of the + * value. + * @method resolve + * @param value The value to resolve the promise with + * @static + * @return {Parse.Promise} the new promise. + */ + + }, { + key: 'resolve', + value: function (value) { + return new ParsePromise(function (resolve, reject) { + if (ParsePromise.is(value)) { + value.then(resolve, reject); + } else { + resolve(value); + } + }); + } + + /** + * Returns a new promise that is rejected with a given error. + * @method error + * @param error The error to reject the promise with + * @static + * @return {Parse.Promise} the new promise. + */ + + }, { + key: 'error', + value: function () { + var promise = new ParsePromise(); + + for (var _len6 = arguments.length, errors = Array(_len6), _key6 = 0; _key6 < _len6; _key6++) { + errors[_key6] = arguments[_key6]; + } + + promise.reject.apply(promise, errors); + return promise; + } + + /** + * Returns a new promise that is rejected with a given error. + * This is an alias for Parse.Promise.error, for compliance with + * the ES6 implementation. + * @method reject + * @param error The error to reject the promise with + * @static + * @return {Parse.Promise} the new promise. + */ + + }, { + key: 'reject', + value: function () { + for (var _len7 = arguments.length, errors = Array(_len7), _key7 = 0; _key7 < _len7; _key7++) { + errors[_key7] = arguments[_key7]; + } + + return ParsePromise.error.apply(null, errors); + } + + /** + * Returns a new promise that is fulfilled when all of the input promises + * are resolved. If any promise in the list fails, then the returned promise + * will be rejected with an array containing the error from each promise. + * If they all succeed, then the returned promise will succeed, with the + * results being the results of all the input + * promises. For example:
+ * var p1 = Parse.Promise.as(1); + * var p2 = Parse.Promise.as(2); + * var p3 = Parse.Promise.as(3); + * + * Parse.Promise.when(p1, p2, p3).then(function(r1, r2, r3) { + * console.log(r1); // prints 1 + * console.log(r2); // prints 2 + * console.log(r3); // prints 3 + * });+ * + * The input promises can also be specified as an array:
+ * var promises = [p1, p2, p3]; + * Parse.Promise.when(promises).then(function(results) { + * console.log(results); // prints [1,2,3] + * }); + *+ * @method when + * @param {Array} promises a list of promises to wait for. + * @static + * @return {Parse.Promise} the new promise. + */ + + }, { + key: 'when', + value: function (promises) { + var objects; + var arrayArgument = Array.isArray(promises); + if (arrayArgument) { + objects = promises; + } else { + objects = arguments; + } + + var total = objects.length; + var hadError = false; + var results = []; + var returnValue = arrayArgument ? [results] : results; + var errors = []; + results.length = objects.length; + errors.length = objects.length; + + if (total === 0) { + return ParsePromise.as.apply(this, returnValue); + } + + var promise = new ParsePromise(); + + var resolveOne = function () { + total--; + if (total <= 0) { + if (hadError) { + promise.reject(errors); + } else { + promise.resolve.apply(promise, returnValue); + } + } + }; + + var chain = function (object, index) { + if (ParsePromise.is(object)) { + object.then(function (result) { + results[index] = result; + resolveOne(); + }, function (error) { + errors[index] = error; + hadError = true; + resolveOne(); + }); + } else { + results[i] = object; + resolveOne(); + } + }; + for (var i = 0; i < objects.length; i++) { + chain(objects[i], i); + } + + return promise; + } + + /** + * Returns a new promise that is fulfilled when all of the promises in the + * iterable argument are resolved. If any promise in the list fails, then + * the returned promise will be immediately rejected with the reason that + * single promise rejected. If they all succeed, then the returned promise + * will succeed, with the results being the results of all the input + * promises. If the iterable provided is empty, the returned promise will + * be immediately resolved. + * + * For example:
+ * var p1 = Parse.Promise.as(1); + * var p2 = Parse.Promise.as(2); + * var p3 = Parse.Promise.as(3); + * + * Parse.Promise.all([p1, p2, p3]).then(function([r1, r2, r3]) { + * console.log(r1); // prints 1 + * console.log(r2); // prints 2 + * console.log(r3); // prints 3 + * });+ * + * @method all + * @param {Iterable} promises an iterable of promises to wait for. + * @static + * @return {Parse.Promise} the new promise. + */ + + }, { + key: 'all', + value: function (promises) { + var total = 0; + var objects = []; + + var _iteratorNormalCompletion = true; + var _didIteratorError = false; + var _iteratorError = undefined; + + try { + for (var _iterator = (0, _getIterator3.default)(promises), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { + var p = _step.value; + + objects[total++] = p; + } + } catch (err) { + _didIteratorError = true; + _iteratorError = err; + } finally { + try { + if (!_iteratorNormalCompletion && _iterator.return) { + _iterator.return(); + } + } finally { + if (_didIteratorError) { + throw _iteratorError; + } + } + } + + if (total === 0) { + return ParsePromise.as([]); + } + + var hadError = false; + var promise = new ParsePromise(); + var resolved = 0; + var results = []; + objects.forEach(function (object, i) { + if (ParsePromise.is(object)) { + object.then(function (result) { + if (hadError) { + return false; + } + results[i] = result; + resolved++; + if (resolved >= total) { + promise.resolve(results); + } + }, function (error) { + // Reject immediately + promise.reject(error); + hadError = true; + }); + } else { + results[i] = object; + resolved++; + if (!hadError && resolved >= total) { + promise.resolve(results); + } + } + }); + + return promise; + } + + /** + * Returns a new promise that is immediately fulfilled when any of the + * promises in the iterable argument are resolved or rejected. If the + * first promise to complete is resolved, the returned promise will be + * resolved with the same value. Likewise, if the first promise to + * complete is rejected, the returned promise will be rejected with the + * same reason. + * + * @method race + * @param {Iterable} promises an iterable of promises to wait for. + * @static + * @return {Parse.Promise} the new promise. + */ + + }, { + key: 'race', + value: function (promises) { + var completed = false; + var promise = new ParsePromise(); + var _iteratorNormalCompletion2 = true; + var _didIteratorError2 = false; + var _iteratorError2 = undefined; + + try { + for (var _iterator2 = (0, _getIterator3.default)(promises), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { + var p = _step2.value; + + if (ParsePromise.is(p)) { + p.then(function (result) { + if (completed) { + return; + } + completed = true; + promise.resolve(result); + }, function (error) { + if (completed) { + return; + } + completed = true; + promise.reject(error); + }); + } else if (!completed) { + completed = true; + promise.resolve(p); + } + } + } catch (err) { + _didIteratorError2 = true; + _iteratorError2 = err; + } finally { + try { + if (!_iteratorNormalCompletion2 && _iterator2.return) { + _iterator2.return(); + } + } finally { + if (_didIteratorError2) { + throw _iteratorError2; + } + } + } + + return promise; + } + + /** + * Runs the given asyncFunction repeatedly, as long as the predicate + * function returns a truthy value. Stops repeating if asyncFunction returns + * a rejected promise. + * @method _continueWhile + * @param {Function} predicate should return false when ready to stop. + * @param {Function} asyncFunction should return a Promise. + * @static + */ + + }, { + key: '_continueWhile', + value: function (predicate, asyncFunction) { + if (predicate()) { + return asyncFunction().then(function () { + return ParsePromise._continueWhile(predicate, asyncFunction); + }); + } + return ParsePromise.as(); + } + }, { + key: 'isPromisesAPlusCompliant', + value: function () { + return _isPromisesAPlusCompliant; + } + }, { + key: 'enableAPlusCompliant', + value: function () { + _isPromisesAPlusCompliant = true; + } + }, { + key: 'disableAPlusCompliant', + value: function () { + _isPromisesAPlusCompliant = false; + } + }]); + return ParsePromise; +}(); + +exports.default = ParsePromise; \ No newline at end of file diff --git a/lib/node/ParseQuery.js b/lib/node/ParseQuery.js new file mode 100644 index 000000000..664f5a737 --- /dev/null +++ b/lib/node/ParseQuery.js @@ -0,0 +1,1340 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _typeof2 = require('babel-runtime/helpers/typeof'); + +var _typeof3 = _interopRequireDefault(_typeof2); + +var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); + +var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); + +var _createClass2 = require('babel-runtime/helpers/createClass'); + +var _createClass3 = _interopRequireDefault(_createClass2); + +var _keys = require('babel-runtime/core-js/object/keys'); + +var _keys2 = _interopRequireDefault(_keys); + +var _CoreManager = require('./CoreManager'); + +var _CoreManager2 = _interopRequireDefault(_CoreManager); + +var _encode = require('./encode'); + +var _encode2 = _interopRequireDefault(_encode); + +var _ParseError = require('./ParseError'); + +var _ParseError2 = _interopRequireDefault(_ParseError); + +var _ParseGeoPoint = require('./ParseGeoPoint'); + +var _ParseGeoPoint2 = _interopRequireDefault(_ParseGeoPoint); + +var _ParseObject = require('./ParseObject'); + +var _ParseObject2 = _interopRequireDefault(_ParseObject); + +var _ParsePromise = require('./ParsePromise'); + +var _ParsePromise2 = _interopRequireDefault(_ParsePromise); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { default: obj }; +} + +/** + * Converts a string into a regex that matches it. + * Surrounding with \Q .. \E does this, we just need to escape any \E's in + * the text separately. + */ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + */ + +function quote(s) { + return '\\Q' + s.replace('\\E', '\\E\\\\E\\Q') + '\\E'; +} + +/** + * Handles pre-populating the result data of a query with select fields, + * making sure that the data object contains keys for all objects that have + * been requested with a select, so that our cached state updates correctly. + */ +function handleSelectResult(data, select) { + var serverDataMask = {}; + + select.forEach(function (field) { + var hasSubObjectSelect = field.indexOf(".") !== -1; + if (!hasSubObjectSelect && !data.hasOwnProperty(field)) { + // this field was selected, but is missing from the retrieved data + data[field] = undefined; + } else if (hasSubObjectSelect) { + // this field references a sub-object, + // so we need to walk down the path components + var pathComponents = field.split("."); + var obj = data; + var serverMask = serverDataMask; + + pathComponents.forEach(function (component, index, arr) { + // add keys if the expected data is missing + if (!obj[component]) { + obj[component] = index == arr.length - 1 ? undefined : {}; + } + obj = obj[component]; + + //add this path component to the server mask so we can fill it in later if needed + if (index < arr.length - 1) { + if (!serverMask[component]) { + serverMask[component] = {}; + } + } + }); + } + }); + + if ((0, _keys2.default)(serverDataMask).length > 0) { + var copyMissingDataWithMask = function copyMissingDataWithMask(src, dest, mask, copyThisLevel) { + //copy missing elements at this level + if (copyThisLevel) { + for (var key in src) { + if (src.hasOwnProperty(key) && !dest.hasOwnProperty(key)) { + dest[key] = src[key]; + } + } + } + for (var key in mask) { + //traverse into objects as needed + copyMissingDataWithMask(src[key], dest[key], mask[key], true); + } + }; + + // When selecting from sub-objects, we don't want to blow away the missing + // information that we may have retrieved before. We've already added any + // missing selected keys to sub-objects, but we still need to add in the + // data for any previously retrieved sub-objects that were not selected. + + var serverData = _CoreManager2.default.getObjectStateController().getServerData({ id: data.objectId, className: data.className }); + + copyMissingDataWithMask(serverData, data, serverDataMask, false); + } +} + +/** + * Creates a new parse Parse.Query for the given Parse.Object subclass. + * @class Parse.Query + * @constructor + * @param {} objectClass An instance of a subclass of Parse.Object, or a Parse className string. + * + *
Parse.Query defines a query that is used to fetch Parse.Objects. The
+ * most common use case is finding all objects that match a query through the
+ * find
method. For example, this sample code fetches all objects
+ * of class MyClass
. It calls a different function depending on
+ * whether the fetch succeeded or not.
+ *
+ *
+ * var query = new Parse.Query(MyClass); + * query.find({ + * success: function(results) { + * // results is an array of Parse.Object. + * }, + * + * error: function(error) { + * // error is an instance of Parse.Error. + * } + * });+ * + *
A Parse.Query can also be used to retrieve a single object whose id is
+ * known, through the get method. For example, this sample code fetches an
+ * object of class MyClass
and id myId
. It calls a
+ * different function depending on whether the fetch succeeded or not.
+ *
+ *
+ * var query = new Parse.Query(MyClass); + * query.get(myId, { + * success: function(object) { + * // object is an instance of Parse.Object. + * }, + * + * error: function(object, error) { + * // error is an instance of Parse.Error. + * } + * });+ * + *
A Parse.Query can also be used to count the number of objects that match
+ * the query without retrieving all of those objects. For example, this
+ * sample code counts the number of objects of the class MyClass
+ *
+ * var query = new Parse.Query(MyClass); + * query.count({ + * success: function(number) { + * // There are number instances of MyClass. + * }, + * + * error: function(error) { + * // error is an instance of Parse.Error. + * } + * });+ */ + +var ParseQuery = function () { + function ParseQuery(objectClass) { + (0, _classCallCheck3.default)(this, ParseQuery); + + if (typeof objectClass === 'string') { + if (objectClass === 'User' && _CoreManager2.default.get('PERFORM_USER_REWRITE')) { + this.className = '_User'; + } else { + this.className = objectClass; + } + } else if (objectClass instanceof _ParseObject2.default) { + this.className = objectClass.className; + } else if (typeof objectClass === 'function') { + if (typeof objectClass.className === 'string') { + this.className = objectClass.className; + } else { + var obj = new objectClass(); + this.className = obj.className; + } + } else { + throw new TypeError('A ParseQuery must be constructed with a ParseObject or class name.'); + } + + this._where = {}; + this._include = []; + this._limit = -1; // negative limit is not sent in the server request + this._skip = 0; + this._extraOptions = {}; + } + + /** + * Adds constraint that at least one of the passed in queries matches. + * @method _orQuery + * @param {Array} queries + * @return {Parse.Query} Returns the query, so you can chain this call. + */ + + (0, _createClass3.default)(ParseQuery, [{ + key: '_orQuery', + value: function (queries) { + var queryJSON = queries.map(function (q) { + return q.toJSON().where; + }); + + this._where.$or = queryJSON; + return this; + } + + /** + * Helper for condition queries + */ + + }, { + key: '_addCondition', + value: function (key, condition, value) { + if (!this._where[key] || typeof this._where[key] === 'string') { + this._where[key] = {}; + } + this._where[key][condition] = (0, _encode2.default)(value, false, true); + return this; + } + + /** + * Converts string for regular expression at the beginning + */ + + }, { + key: '_regexStartWith', + value: function (string) { + return '^' + quote(string); + } + + /** + * Returns a JSON representation of this query. + * @method toJSON + * @return {Object} The JSON representation of the query. + */ + + }, { + key: 'toJSON', + value: function () { + var params = { + where: this._where + }; + + if (this._include.length) { + params.include = this._include.join(','); + } + if (this._select) { + params.keys = this._select.join(','); + } + if (this._limit >= 0) { + params.limit = this._limit; + } + if (this._skip > 0) { + params.skip = this._skip; + } + if (this._order) { + params.order = this._order.join(','); + } + for (var key in this._extraOptions) { + params[key] = this._extraOptions[key]; + } + + return params; + } + + /** + * Constructs a Parse.Object whose id is already known by fetching data from + * the server. Either options.success or options.error is called when the + * find completes. + * + * @method get + * @param {String} objectId The id of the object to be fetched. + * @param {Object} options A Backbone-style options object. + * Valid options are:
var compoundQuery = Parse.Query.or(query1, query2, query3);+ * + * will create a compoundQuery that is an or of the query1, query2, and + * query3. + * @method or + * @param {...Parse.Query} var_args The list of queries to OR. + * @static + * @return {Parse.Query} The query that is the OR of the passed in queries. + */ + + }], [{ + key: 'or', + value: function () { + var className = null; + + for (var _len7 = arguments.length, queries = Array(_len7), _key7 = 0; _key7 < _len7; _key7++) { + queries[_key7] = arguments[_key7]; + } + + queries.forEach(function (q) { + if (!className) { + className = q.className; + } + + if (className !== q.className) { + throw new Error('All queries must be for the same class.'); + } + }); + + var query = new ParseQuery(className); + query._orQuery(queries); + return query; + } + }]); + return ParseQuery; +}(); + +exports.default = ParseQuery; + +var DefaultController = { + find: function (className, params, options) { + var RESTController = _CoreManager2.default.getRESTController(); + + return RESTController.request('GET', 'classes/' + className, params, options); + } +}; + +_CoreManager2.default.setQueryController(DefaultController); \ No newline at end of file diff --git a/lib/node/ParseRelation.js b/lib/node/ParseRelation.js new file mode 100644 index 000000000..fe9ea8769 --- /dev/null +++ b/lib/node/ParseRelation.js @@ -0,0 +1,182 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); + +var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); + +var _createClass2 = require('babel-runtime/helpers/createClass'); + +var _createClass3 = _interopRequireDefault(_createClass2); + +var _ParseOp = require('./ParseOp'); + +var _ParseObject = require('./ParseObject'); + +var _ParseObject2 = _interopRequireDefault(_ParseObject); + +var _ParseQuery = require('./ParseQuery'); + +var _ParseQuery2 = _interopRequireDefault(_ParseQuery); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { default: obj }; +} + +/** + * Creates a new Relation for the given parent object and key. This + * constructor should rarely be used directly, but rather created by + * Parse.Object.relation. + * @class Parse.Relation + * @constructor + * @param {Parse.Object} parent The parent of this relation. + * @param {String} key The key for this relation on the parent. + * + *
+ * A class that is used to access all of the children of a many-to-many + * relationship. Each instance of Parse.Relation is associated with a + * particular parent object and key. + *
+ */ +var ParseRelation = function () { + function ParseRelation(parent, key) { + (0, _classCallCheck3.default)(this, ParseRelation); + + this.parent = parent; + this.key = key; + this.targetClassName = null; + } + + /** + * Makes sure that this relation has the right parent and key. + */ + + (0, _createClass3.default)(ParseRelation, [{ + key: '_ensureParentAndKey', + value: function (parent, key) { + this.key = this.key || key; + if (this.key !== key) { + throw new Error('Internal Error. Relation retrieved from two different keys.'); + } + if (this.parent) { + if (this.parent.className !== parent.className) { + throw new Error('Internal Error. Relation retrieved from two different Objects.'); + } + if (this.parent.id) { + if (this.parent.id !== parent.id) { + throw new Error('Internal Error. Relation retrieved from two different Objects.'); + } + } else if (parent.id) { + this.parent = parent; + } + } else { + this.parent = parent; + } + } + + /** + * Adds a Parse.Object or an array of Parse.Objects to the relation. + * @method add + * @param {} objects The item or items to add. + */ + + }, { + key: 'add', + value: function (objects) { + if (!Array.isArray(objects)) { + objects = [objects]; + } + + var change = new _ParseOp.RelationOp(objects, []); + var parent = this.parent; + if (!parent) { + throw new Error('Cannot add to a Relation without a parent'); + } + parent.set(this.key, change); + this.targetClassName = change._targetClassName; + return parent; + } + + /** + * Removes a Parse.Object or an array of Parse.Objects from this relation. + * @method remove + * @param {} objects The item or items to remove. + */ + + }, { + key: 'remove', + value: function (objects) { + if (!Array.isArray(objects)) { + objects = [objects]; + } + + var change = new _ParseOp.RelationOp([], objects); + if (!this.parent) { + throw new Error('Cannot remove from a Relation without a parent'); + } + this.parent.set(this.key, change); + this.targetClassName = change._targetClassName; + } + + /** + * Returns a JSON version of the object suitable for saving to disk. + * @method toJSON + * @return {Object} + */ + + }, { + key: 'toJSON', + value: function () { + return { + __type: 'Relation', + className: this.targetClassName + }; + } + + /** + * Returns a Parse.Query that is limited to objects in this + * relation. + * @method query + * @return {Parse.Query} + */ + + }, { + key: 'query', + value: function () { + var query; + var parent = this.parent; + if (!parent) { + throw new Error('Cannot construct a query for a Relation without a parent'); + } + if (!this.targetClassName) { + query = new _ParseQuery2.default(parent.className); + query._extraOptions.redirectClassNameForKey = this.key; + } else { + query = new _ParseQuery2.default(this.targetClassName); + } + query._addCondition('$relatedTo', 'object', { + __type: 'Pointer', + className: parent.className, + objectId: parent.id + }); + query._addCondition('$relatedTo', 'key', this.key); + + return query; + } + }]); + return ParseRelation; +}(); /** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + */ + +exports.default = ParseRelation; \ No newline at end of file diff --git a/lib/node/ParseRole.js b/lib/node/ParseRole.js new file mode 100644 index 000000000..567af7a54 --- /dev/null +++ b/lib/node/ParseRole.js @@ -0,0 +1,196 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); + +var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); + +var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); + +var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); + +var _createClass2 = require('babel-runtime/helpers/createClass'); + +var _createClass3 = _interopRequireDefault(_createClass2); + +var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); + +var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); + +var _get2 = require('babel-runtime/helpers/get'); + +var _get3 = _interopRequireDefault(_get2); + +var _inherits2 = require('babel-runtime/helpers/inherits'); + +var _inherits3 = _interopRequireDefault(_inherits2); + +var _ParseACL = require('./ParseACL'); + +var _ParseACL2 = _interopRequireDefault(_ParseACL); + +var _ParseError = require('./ParseError'); + +var _ParseError2 = _interopRequireDefault(_ParseError); + +var _ParseObject2 = require('./ParseObject'); + +var _ParseObject3 = _interopRequireDefault(_ParseObject2); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { default: obj }; +} + +/** + * Represents a Role on the Parse server. Roles represent groupings of + * Users for the purposes of granting permissions (e.g. specifying an ACL + * for an Object). Roles are specified by their sets of child users and + * child roles, all of which are granted any permissions that the parent + * role has. + * + *Roles must have a name (which cannot be changed after creation of the + * role), and must specify an ACL.
+ * @class Parse.Role + * @constructor + * @param {String} name The name of the Role to create. + * @param {Parse.ACL} acl The ACL for this role. Roles must have an ACL. + * A Parse.Role is a local representation of a role persisted to the Parse + * cloud. + */ +var ParseRole = function (_ParseObject) { + (0, _inherits3.default)(ParseRole, _ParseObject); + + function ParseRole(name, acl) { + (0, _classCallCheck3.default)(this, ParseRole); + + var _this = (0, _possibleConstructorReturn3.default)(this, (ParseRole.__proto__ || (0, _getPrototypeOf2.default)(ParseRole)).call(this, '_Role')); + + if (typeof name === 'string' && acl instanceof _ParseACL2.default) { + _this.setName(name); + _this.setACL(acl); + } + return _this; + } + + /** + * Gets the name of the role. You can alternatively call role.get("name") + * + * @method getName + * @return {String} the name of the role. + */ + + (0, _createClass3.default)(ParseRole, [{ + key: 'getName', + value: function () { + var name = this.get('name'); + if (name == null || typeof name === 'string') { + return name; + } + return ''; + } + + /** + * Sets the name for a role. This value must be set before the role has + * been saved to the server, and cannot be set once the role has been + * saved. + * + *+ * A role's name can only contain alphanumeric characters, _, -, and + * spaces. + *
+ * + *This is equivalent to calling role.set("name", name)
+ * + * @method setName + * @param {String} name The name of the role. + * @param {Object} options Standard options object with success and error + * callbacks. + */ + + }, { + key: 'setName', + value: function (name, options) { + return this.set('name', name, options); + } + + /** + * Gets the Parse.Relation for the Parse.Users that are direct + * children of this role. These users are granted any privileges that this + * role has been granted (e.g. read or write access through ACLs). You can + * add or remove users from the role through this relation. + * + *This is equivalent to calling role.relation("users")
+ * + * @method getUsers + * @return {Parse.Relation} the relation for the users belonging to this + * role. + */ + + }, { + key: 'getUsers', + value: function () { + return this.relation('users'); + } + + /** + * Gets the Parse.Relation for the Parse.Roles that are direct + * children of this role. These roles' users are granted any privileges that + * this role has been granted (e.g. read or write access through ACLs). You + * can add or remove child roles from this role through this relation. + * + *This is equivalent to calling role.relation("roles")
+ * + * @method getRoles + * @return {Parse.Relation} the relation for the roles belonging to this + * role. + */ + + }, { + key: 'getRoles', + value: function () { + return this.relation('roles'); + } + }, { + key: 'validate', + value: function (attrs, options) { + var isInvalid = (0, _get3.default)(ParseRole.prototype.__proto__ || (0, _getPrototypeOf2.default)(ParseRole.prototype), 'validate', this).call(this, attrs, options); + if (isInvalid) { + return isInvalid; + } + + if ('name' in attrs && attrs.name !== this.getName()) { + var newName = attrs.name; + if (this.id && this.id !== attrs.objectId) { + // Check to see if the objectId being set matches this.id + // This happens during a fetch -- the id is set before calling fetch + // Let the name be set in this case + return new _ParseError2.default(_ParseError2.default.OTHER_CAUSE, 'A role\'s name can only be set before it has been saved.'); + } + if (typeof newName !== 'string') { + return new _ParseError2.default(_ParseError2.default.OTHER_CAUSE, 'A role\'s name must be a String.'); + } + if (!/^[0-9a-zA-Z\-_ ]+$/.test(newName)) { + return new _ParseError2.default(_ParseError2.default.OTHER_CAUSE, 'A role\'s name can be only contain alphanumeric characters, _, ' + '-, and spaces.'); + } + } + return false; + } + }]); + return ParseRole; +}(_ParseObject3.default); /** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + */ + +exports.default = ParseRole; + +_ParseObject3.default.registerSubclass('_Role', ParseRole); \ No newline at end of file diff --git a/lib/node/ParseSession.js b/lib/node/ParseSession.js new file mode 100644 index 000000000..7fb75c80d --- /dev/null +++ b/lib/node/ParseSession.js @@ -0,0 +1,180 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _typeof2 = require('babel-runtime/helpers/typeof'); + +var _typeof3 = _interopRequireDefault(_typeof2); + +var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); + +var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); + +var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); + +var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); + +var _createClass2 = require('babel-runtime/helpers/createClass'); + +var _createClass3 = _interopRequireDefault(_createClass2); + +var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); + +var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); + +var _inherits2 = require('babel-runtime/helpers/inherits'); + +var _inherits3 = _interopRequireDefault(_inherits2); + +var _CoreManager = require('./CoreManager'); + +var _CoreManager2 = _interopRequireDefault(_CoreManager); + +var _isRevocableSession = require('./isRevocableSession'); + +var _isRevocableSession2 = _interopRequireDefault(_isRevocableSession); + +var _ParseObject2 = require('./ParseObject'); + +var _ParseObject3 = _interopRequireDefault(_ParseObject2); + +var _ParsePromise = require('./ParsePromise'); + +var _ParsePromise2 = _interopRequireDefault(_ParsePromise); + +var _ParseUser = require('./ParseUser'); + +var _ParseUser2 = _interopRequireDefault(_ParseUser); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { default: obj }; +} + +/** + * @class Parse.Session + * @constructor + * + *A Parse.Session object is a local representation of a revocable session. + * This class is a subclass of a Parse.Object, and retains the same + * functionality of a Parse.Object.
+ */ +var ParseSession = function (_ParseObject) { + (0, _inherits3.default)(ParseSession, _ParseObject); + + function ParseSession(attributes) { + (0, _classCallCheck3.default)(this, ParseSession); + + var _this = (0, _possibleConstructorReturn3.default)(this, (ParseSession.__proto__ || (0, _getPrototypeOf2.default)(ParseSession)).call(this, '_Session')); + + if (attributes && (typeof attributes === 'undefined' ? 'undefined' : (0, _typeof3.default)(attributes)) === 'object') { + if (!_this.set(attributes || {})) { + throw new Error('Can\'t create an invalid Session'); + } + } + return _this; + } + + /** + * Returns the session token string. + * @method getSessionToken + * @return {String} + */ + + (0, _createClass3.default)(ParseSession, [{ + key: 'getSessionToken', + value: function () { + var token = this.get('sessionToken'); + if (typeof token === 'string') { + return token; + } + return ''; + } + }], [{ + key: 'readOnlyAttributes', + value: function () { + return ['createdWith', 'expiresAt', 'installationId', 'restricted', 'sessionToken', 'user']; + } + + /** + * Retrieves the Session object for the currently logged in session. + * @method current + * @static + * @return {Parse.Promise} A promise that is resolved with the Parse.Session + * object after it has been fetched. If there is no current user, the + * promise will be rejected. + */ + + }, { + key: 'current', + value: function (options) { + options = options || {}; + var controller = _CoreManager2.default.getSessionController(); + + var sessionOptions = {}; + if (options.hasOwnProperty('useMasterKey')) { + sessionOptions.useMasterKey = options.useMasterKey; + } + return _ParseUser2.default.currentAsync().then(function (user) { + if (!user) { + return _ParsePromise2.default.error('There is no current user.'); + } + user.getSessionToken(); + + sessionOptions.sessionToken = user.getSessionToken(); + return controller.getSession(sessionOptions); + }); + } + + /** + * Determines whether the current session token is revocable. + * This method is useful for migrating Express.js or Node.js web apps to + * use revocable sessions. If you are migrating an app that uses the Parse + * SDK in the browser only, please use Parse.User.enableRevocableSession() + * instead, so that sessions can be automatically upgraded. + * @method isCurrentSessionRevocable + * @static + * @return {Boolean} + */ + + }, { + key: 'isCurrentSessionRevocable', + value: function () { + var currentUser = _ParseUser2.default.current(); + if (currentUser) { + return (0, _isRevocableSession2.default)(currentUser.getSessionToken() || ''); + } + return false; + } + }]); + return ParseSession; +}(_ParseObject3.default); /** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + */ + +exports.default = ParseSession; + +_ParseObject3.default.registerSubclass('_Session', ParseSession); + +var DefaultController = { + getSession: function (options) { + var RESTController = _CoreManager2.default.getRESTController(); + var session = new ParseSession(); + + return RESTController.request('GET', 'sessions/me', {}, options).then(function (sessionData) { + session._finishFetch(sessionData); + session._setExisted(true); + return session; + }); + } +}; + +_CoreManager2.default.setSessionController(DefaultController); \ No newline at end of file diff --git a/lib/node/ParseUser.js b/lib/node/ParseUser.js new file mode 100644 index 000000000..f18f42820 --- /dev/null +++ b/lib/node/ParseUser.js @@ -0,0 +1,1150 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _stringify = require('babel-runtime/core-js/json/stringify'); + +var _stringify2 = _interopRequireDefault(_stringify); + +var _defineProperty = require('babel-runtime/core-js/object/define-property'); + +var _defineProperty2 = _interopRequireDefault(_defineProperty); + +var _typeof2 = require('babel-runtime/helpers/typeof'); + +var _typeof3 = _interopRequireDefault(_typeof2); + +var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); + +var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); + +var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); + +var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); + +var _createClass2 = require('babel-runtime/helpers/createClass'); + +var _createClass3 = _interopRequireDefault(_createClass2); + +var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); + +var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); + +var _get2 = require('babel-runtime/helpers/get'); + +var _get3 = _interopRequireDefault(_get2); + +var _inherits2 = require('babel-runtime/helpers/inherits'); + +var _inherits3 = _interopRequireDefault(_inherits2); + +var _CoreManager = require('./CoreManager'); + +var _CoreManager2 = _interopRequireDefault(_CoreManager); + +var _isRevocableSession = require('./isRevocableSession'); + +var _isRevocableSession2 = _interopRequireDefault(_isRevocableSession); + +var _ParseError = require('./ParseError'); + +var _ParseError2 = _interopRequireDefault(_ParseError); + +var _ParseObject2 = require('./ParseObject'); + +var _ParseObject3 = _interopRequireDefault(_ParseObject2); + +var _ParsePromise = require('./ParsePromise'); + +var _ParsePromise2 = _interopRequireDefault(_ParsePromise); + +var _ParseSession = require('./ParseSession'); + +var _ParseSession2 = _interopRequireDefault(_ParseSession); + +var _Storage = require('./Storage'); + +var _Storage2 = _interopRequireDefault(_Storage); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { default: obj }; +} + +var CURRENT_USER_KEY = 'currentUser'; /** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + */ + +var canUseCurrentUser = !_CoreManager2.default.get('IS_NODE'); +var currentUserCacheMatchesDisk = false; +var currentUserCache = null; + +var authProviders = {}; + +/** + * @class Parse.User + * @constructor + * + *A Parse.User object is a local representation of a user persisted to the + * Parse cloud. This class is a subclass of a Parse.Object, and retains the + * same functionality of a Parse.Object, but also extends it with various + * user specific methods, like authentication, signing up, and validation of + * uniqueness.
+ */ + +var ParseUser = function (_ParseObject) { + (0, _inherits3.default)(ParseUser, _ParseObject); + + function ParseUser(attributes) { + (0, _classCallCheck3.default)(this, ParseUser); + + var _this = (0, _possibleConstructorReturn3.default)(this, (ParseUser.__proto__ || (0, _getPrototypeOf2.default)(ParseUser)).call(this, '_User')); + + if (attributes && (typeof attributes === 'undefined' ? 'undefined' : (0, _typeof3.default)(attributes)) === 'object') { + if (!_this.set(attributes || {})) { + throw new Error('Can\'t create an invalid Parse User'); + } + } + return _this; + } + + /** + * Request a revocable session token to replace the older style of token. + * @method _upgradeToRevocableSession + * @param {Object} options A Backbone-style options object. + * @return {Parse.Promise} A promise that is resolved when the replacement + * token has been fetched. + */ + + (0, _createClass3.default)(ParseUser, [{ + key: '_upgradeToRevocableSession', + value: function (options) { + options = options || {}; + + var upgradeOptions = {}; + if (options.hasOwnProperty('useMasterKey')) { + upgradeOptions.useMasterKey = options.useMasterKey; + } + + var controller = _CoreManager2.default.getUserController(); + return controller.upgradeToRevocableSession(this, upgradeOptions)._thenRunCallbacks(options); + } + + /** + * Unlike in the Android/iOS SDKs, logInWith is unnecessary, since you can + * call linkWith on the user (even if it doesn't exist yet on the server). + * @method _linkWith + */ + + }, { + key: '_linkWith', + value: function (provider, options) { + var _this2 = this; + + var authType; + if (typeof provider === 'string') { + authType = provider; + provider = authProviders[provider]; + } else { + authType = provider.getAuthType(); + } + if (options && options.hasOwnProperty('authData')) { + var authData = this.get('authData') || {}; + if ((typeof authData === 'undefined' ? 'undefined' : (0, _typeof3.default)(authData)) !== 'object') { + throw new Error('Invalid type: authData field should be an object'); + } + authData[authType] = options.authData; + + var controller = _CoreManager2.default.getUserController(); + return controller.linkWith(this, authData)._thenRunCallbacks(options, this); + } else { + var promise = new _ParsePromise2.default(); + provider.authenticate({ + success: function (provider, result) { + var opts = {}; + opts.authData = result; + if (options.success) { + opts.success = options.success; + } + if (options.error) { + opts.error = options.error; + } + _this2._linkWith(provider, opts).then(function () { + promise.resolve(_this2); + }, function (error) { + promise.reject(error); + }); + }, + error: function (provider, _error) { + if (typeof options.error === 'function') { + options.error(_this2, _error); + } + promise.reject(_error); + } + }); + return promise; + } + } + + /** + * Synchronizes auth data for a provider (e.g. puts the access token in the + * right place to be used by the Facebook SDK). + * @method _synchronizeAuthData + */ + + }, { + key: '_synchronizeAuthData', + value: function (provider) { + if (!this.isCurrent() || !provider) { + return; + } + var authType; + if (typeof provider === 'string') { + authType = provider; + provider = authProviders[authType]; + } else { + authType = provider.getAuthType(); + } + var authData = this.get('authData'); + if (!provider || !authData || (typeof authData === 'undefined' ? 'undefined' : (0, _typeof3.default)(authData)) !== 'object') { + return; + } + var success = provider.restoreAuthentication(authData[authType]); + if (!success) { + this._unlinkFrom(provider); + } + } + + /** + * Synchronizes authData for all providers. + * @method _synchronizeAllAuthData + */ + + }, { + key: '_synchronizeAllAuthData', + value: function () { + var authData = this.get('authData'); + if ((typeof authData === 'undefined' ? 'undefined' : (0, _typeof3.default)(authData)) !== 'object') { + return; + } + + for (var key in authData) { + this._synchronizeAuthData(key); + } + } + + /** + * Removes null values from authData (which exist temporarily for + * unlinking) + * @method _cleanupAuthData + */ + + }, { + key: '_cleanupAuthData', + value: function () { + if (!this.isCurrent()) { + return; + } + var authData = this.get('authData'); + if ((typeof authData === 'undefined' ? 'undefined' : (0, _typeof3.default)(authData)) !== 'object') { + return; + } + + for (var key in authData) { + if (!authData[key]) { + delete authData[key]; + } + } + } + + /** + * Unlinks a user from a service. + * @method _unlinkFrom + */ + + }, { + key: '_unlinkFrom', + value: function (provider, options) { + var _this3 = this; + + if (typeof provider === 'string') { + provider = authProviders[provider]; + } else { + provider.getAuthType(); + } + return this._linkWith(provider, { authData: null }).then(function () { + _this3._synchronizeAuthData(provider); + return _ParsePromise2.default.as(_this3); + })._thenRunCallbacks(options); + } + + /** + * Checks whether a user is linked to a service. + * @method _isLinked + */ + + }, { + key: '_isLinked', + value: function (provider) { + var authType; + if (typeof provider === 'string') { + authType = provider; + } else { + authType = provider.getAuthType(); + } + var authData = this.get('authData') || {}; + if ((typeof authData === 'undefined' ? 'undefined' : (0, _typeof3.default)(authData)) !== 'object') { + return false; + } + return !!authData[authType]; + } + + /** + * Deauthenticates all providers. + * @method _logOutWithAll + */ + + }, { + key: '_logOutWithAll', + value: function () { + var authData = this.get('authData'); + if ((typeof authData === 'undefined' ? 'undefined' : (0, _typeof3.default)(authData)) !== 'object') { + return; + } + + for (var key in authData) { + this._logOutWith(key); + } + } + + /** + * Deauthenticates a single provider (e.g. removing access tokens from the + * Facebook SDK). + * @method _logOutWith + */ + + }, { + key: '_logOutWith', + value: function (provider) { + if (!this.isCurrent()) { + return; + } + if (typeof provider === 'string') { + provider = authProviders[provider]; + } + if (provider && provider.deauthenticate) { + provider.deauthenticate(); + } + } + + /** + * Class instance method used to maintain specific keys when a fetch occurs. + * Used to ensure that the session token is not lost. + */ + + }, { + key: '_preserveFieldsOnFetch', + value: function () { + return { + sessionToken: this.get('sessionToken') + }; + } + + /** + * Returns true ifcurrent
would return this user.
+ * @method isCurrent
+ * @return {Boolean}
+ */
+
+ }, {
+ key: 'isCurrent',
+ value: function () {
+ var current = ParseUser.current();
+ return !!current && current.id === this.id;
+ }
+
+ /**
+ * Returns get("username").
+ * @method getUsername
+ * @return {String}
+ */
+
+ }, {
+ key: 'getUsername',
+ value: function () {
+ var username = this.get('username');
+ if (username == null || typeof username === 'string') {
+ return username;
+ }
+ return '';
+ }
+
+ /**
+ * Calls set("username", username, options) and returns the result.
+ * @method setUsername
+ * @param {String} username
+ * @param {Object} options A Backbone-style options object.
+ * @return {Boolean}
+ */
+
+ }, {
+ key: 'setUsername',
+ value: function (username) {
+ // Strip anonymity, even we do not support anonymous user in js SDK, we may
+ // encounter anonymous user created by android/iOS in cloud code.
+ var authData = this.get('authData');
+ if (authData && (typeof authData === 'undefined' ? 'undefined' : (0, _typeof3.default)(authData)) === 'object' && authData.hasOwnProperty('anonymous')) {
+ // We need to set anonymous to null instead of deleting it in order to remove it from Parse.
+ authData.anonymous = null;
+ }
+ this.set('username', username);
+ }
+
+ /**
+ * Calls set("password", password, options) and returns the result.
+ * @method setPassword
+ * @param {String} password
+ * @param {Object} options A Backbone-style options object.
+ * @return {Boolean}
+ */
+
+ }, {
+ key: 'setPassword',
+ value: function (password) {
+ this.set('password', password);
+ }
+
+ /**
+ * Returns get("email").
+ * @method getEmail
+ * @return {String}
+ */
+
+ }, {
+ key: 'getEmail',
+ value: function () {
+ var email = this.get('email');
+ if (email == null || typeof email === 'string') {
+ return email;
+ }
+ return '';
+ }
+
+ /**
+ * Calls set("email", email, options) and returns the result.
+ * @method setEmail
+ * @param {String} email
+ * @param {Object} options A Backbone-style options object.
+ * @return {Boolean}
+ */
+
+ }, {
+ key: 'setEmail',
+ value: function (email) {
+ this.set('email', email);
+ }
+
+ /**
+ * Returns the session token for this user, if the user has been logged in,
+ * or if it is the result of a query with the master key. Otherwise, returns
+ * undefined.
+ * @method getSessionToken
+ * @return {String} the session token, or undefined
+ */
+
+ }, {
+ key: 'getSessionToken',
+ value: function () {
+ var token = this.get('sessionToken');
+ if (token == null || typeof token === 'string') {
+ return token;
+ }
+ return '';
+ }
+
+ /**
+ * Checks whether this user is the current user and has been authenticated.
+ * @method authenticated
+ * @return (Boolean) whether this user is the current user and is logged in.
+ */
+
+ }, {
+ key: 'authenticated',
+ value: function () {
+ var current = ParseUser.current();
+ return !!this.get('sessionToken') && !!current && current.id === this.id;
+ }
+
+ /**
+ * Signs up a new user. You should call this instead of save for
+ * new Parse.Users. This will create a new Parse.User on the server, and
+ * also persist the session on disk so that you can access the user using
+ * current
.
+ *
+ * A username and password must be set before calling signUp.
+ * + *Calls options.success or options.error on completion.
+ * + * @method signUp + * @param {Object} attrs Extra fields to set on the new user, or null. + * @param {Object} options A Backbone-style options object. + * @return {Parse.Promise} A promise that is fulfilled when the signup + * finishes. + */ + + }, { + key: 'signUp', + value: function (attrs, options) { + options = options || {}; + + var signupOptions = {}; + if (options.hasOwnProperty('useMasterKey')) { + signupOptions.useMasterKey = options.useMasterKey; + } + if (options.hasOwnProperty('installationId')) { + signupOptions.installationId = options.installationId; + } + + var controller = _CoreManager2.default.getUserController(); + return controller.signUp(this, attrs, signupOptions)._thenRunCallbacks(options, this); + } + + /** + * Logs in a Parse.User. On success, this saves the session to disk, + * so you can retrieve the currently logged in user using + *current
.
+ *
+ * A username and password must be set before calling logIn.
+ * + *Calls options.success or options.error on completion.
+ * + * @method logIn + * @param {Object} options A Backbone-style options object. + * @return {Parse.Promise} A promise that is fulfilled with the user when + * the login is complete. + */ + + }, { + key: 'logIn', + value: function (options) { + options = options || {}; + + var loginOptions = {}; + if (options.hasOwnProperty('useMasterKey')) { + loginOptions.useMasterKey = options.useMasterKey; + } + if (options.hasOwnProperty('installationId')) { + loginOptions.installationId = options.installationId; + } + + var controller = _CoreManager2.default.getUserController(); + return controller.logIn(this, loginOptions)._thenRunCallbacks(options, this); + } + + /** + * Wrap the default save behavior with functionality to save to local + * storage if this is current user. + */ + + }, { + key: 'save', + value: function () { + var _this4 = this; + + for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + return (0, _get3.default)(ParseUser.prototype.__proto__ || (0, _getPrototypeOf2.default)(ParseUser.prototype), 'save', this).apply(this, args).then(function () { + if (_this4.isCurrent()) { + return _CoreManager2.default.getUserController().updateUserOnDisk(_this4); + } + return _this4; + }); + } + + /** + * Wrap the default destroy behavior with functionality that logs out + * the current user when it is destroyed + */ + + }, { + key: 'destroy', + value: function () { + var _this5 = this; + + for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { + args[_key2] = arguments[_key2]; + } + + return (0, _get3.default)(ParseUser.prototype.__proto__ || (0, _getPrototypeOf2.default)(ParseUser.prototype), 'destroy', this).apply(this, args).then(function () { + if (_this5.isCurrent()) { + return _CoreManager2.default.getUserController().removeUserFromDisk(); + } + return _this5; + }); + } + + /** + * Wrap the default fetch behavior with functionality to save to local + * storage if this is current user. + */ + + }, { + key: 'fetch', + value: function () { + var _this6 = this; + + for (var _len3 = arguments.length, args = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) { + args[_key3] = arguments[_key3]; + } + + return (0, _get3.default)(ParseUser.prototype.__proto__ || (0, _getPrototypeOf2.default)(ParseUser.prototype), 'fetch', this).apply(this, args).then(function () { + if (_this6.isCurrent()) { + return _CoreManager2.default.getUserController().updateUserOnDisk(_this6); + } + return _this6; + }); + } + }], [{ + key: 'readOnlyAttributes', + value: function () { + return ['sessionToken']; + } + + /** + * Adds functionality to the existing Parse.User class + * @method extend + * @param {Object} protoProps A set of properties to add to the prototype + * @param {Object} classProps A set of static properties to add to the class + * @static + * @return {Class} The newly extended Parse.User class + */ + + }, { + key: 'extend', + value: function (protoProps, classProps) { + if (protoProps) { + for (var prop in protoProps) { + if (prop !== 'className') { + (0, _defineProperty2.default)(ParseUser.prototype, prop, { + value: protoProps[prop], + enumerable: false, + writable: true, + configurable: true + }); + } + } + } + + if (classProps) { + for (var prop in classProps) { + if (prop !== 'className') { + (0, _defineProperty2.default)(ParseUser, prop, { + value: classProps[prop], + enumerable: false, + writable: true, + configurable: true + }); + } + } + } + + return ParseUser; + } + + /** + * Retrieves the currently logged in ParseUser with a valid session, + * either from memory or localStorage, if necessary. + * @method current + * @static + * @return {Parse.Object} The currently logged in Parse.User. + */ + + }, { + key: 'current', + value: function () { + if (!canUseCurrentUser) { + return null; + } + var controller = _CoreManager2.default.getUserController(); + return controller.currentUser(); + } + + /** + * Retrieves the currently logged in ParseUser from asynchronous Storage. + * @method currentAsync + * @static + * @return {Parse.Promise} A Promise that is resolved with the currently + * logged in Parse User + */ + + }, { + key: 'currentAsync', + value: function () { + if (!canUseCurrentUser) { + return _ParsePromise2.default.as(null); + } + var controller = _CoreManager2.default.getUserController(); + return controller.currentUserAsync(); + } + + /** + * Signs up a new user with a username (or email) and password. + * This will create a new Parse.User on the server, and also persist the + * session in localStorage so that you can access the user using + * {@link #current}. + * + *Calls options.success or options.error on completion.
+ * + * @method signUp + * @param {String} username The username (or email) to sign up with. + * @param {String} password The password to sign up with. + * @param {Object} attrs Extra fields to set on the new user. + * @param {Object} options A Backbone-style options object. + * @static + * @return {Parse.Promise} A promise that is fulfilled with the user when + * the signup completes. + */ + + }, { + key: 'signUp', + value: function (username, password, attrs, options) { + attrs = attrs || {}; + attrs.username = username; + attrs.password = password; + var user = new ParseUser(attrs); + return user.signUp({}, options); + } + + /** + * Logs in a user with a username (or email) and password. On success, this + * saves the session to disk, so you can retrieve the currently logged in + * user usingcurrent
.
+ *
+ * Calls options.success or options.error on completion.
+ * + * @method logIn + * @param {String} username The username (or email) to log in with. + * @param {String} password The password to log in with. + * @param {Object} options A Backbone-style options object. + * @static + * @return {Parse.Promise} A promise that is fulfilled with the user when + * the login completes. + */ + + }, { + key: 'logIn', + value: function (username, password, options) { + if (typeof username !== 'string') { + return _ParsePromise2.default.error(new _ParseError2.default(_ParseError2.default.OTHER_CAUSE, 'Username must be a string.')); + } else if (typeof password !== 'string') { + return _ParsePromise2.default.error(new _ParseError2.default(_ParseError2.default.OTHER_CAUSE, 'Password must be a string.')); + } + var user = new ParseUser(); + user._finishFetch({ username: username, password: password }); + return user.logIn(options); + } + + /** + * Logs in a user with a session token. On success, this saves the session + * to disk, so you can retrieve the currently logged in user using + *current
.
+ *
+ * Calls options.success or options.error on completion.
+ * + * @method become + * @param {String} sessionToken The sessionToken to log in with. + * @param {Object} options A Backbone-style options object. + * @static + * @return {Parse.Promise} A promise that is fulfilled with the user when + * the login completes. + */ + + }, { + key: 'become', + value: function (sessionToken, options) { + if (!canUseCurrentUser) { + throw new Error('It is not memory-safe to become a user in a server environment'); + } + options = options || {}; + + var becomeOptions = { + sessionToken: sessionToken + }; + if (options.hasOwnProperty('useMasterKey')) { + becomeOptions.useMasterKey = options.useMasterKey; + } + + var controller = _CoreManager2.default.getUserController(); + return controller.become(becomeOptions)._thenRunCallbacks(options); + } + }, { + key: 'logInWith', + value: function (provider, options) { + return ParseUser._logInWith(provider, options); + } + + /** + * Logs out the currently logged in user session. This will remove the + * session from disk, log out of linked services, and future calls to + *current
will return null
.
+ * @method logOut
+ * @static
+ * @return {Parse.Promise} A promise that is resolved when the session is
+ * destroyed on the server.
+ */
+
+ }, {
+ key: 'logOut',
+ value: function () {
+ if (!canUseCurrentUser) {
+ throw new Error('There is no current user user on a node.js server environment.');
+ }
+
+ var controller = _CoreManager2.default.getUserController();
+ return controller.logOut();
+ }
+
+ /**
+ * Requests a password reset email to be sent to the specified email address
+ * associated with the user account. This email allows the user to securely
+ * reset their password on the Parse site.
+ *
+ * Calls options.success or options.error on completion.
+ * + * @method requestPasswordReset + * @param {String} email The email address associated with the user that + * forgot their password. + * @param {Object} options A Backbone-style options object. + * @static + */ + + }, { + key: 'requestPasswordReset', + value: function (email, options) { + options = options || {}; + + var requestOptions = {}; + if (options.hasOwnProperty('useMasterKey')) { + requestOptions.useMasterKey = options.useMasterKey; + } + + var controller = _CoreManager2.default.getUserController(); + return controller.requestPasswordReset(email, requestOptions)._thenRunCallbacks(options); + } + + /** + * Allow someone to define a custom User class without className + * being rewritten to _User. The default behavior is to rewrite + * User to _User for legacy reasons. This allows developers to + * override that behavior. + * + * @method allowCustomUserClass + * @param {Boolean} isAllowed Whether or not to allow custom User class + * @static + */ + + }, { + key: 'allowCustomUserClass', + value: function (isAllowed) { + _CoreManager2.default.set('PERFORM_USER_REWRITE', !isAllowed); + } + + /** + * Allows a legacy application to start using revocable sessions. If the + * current session token is not revocable, a request will be made for a new, + * revocable session. + * It is not necessary to call this method from cloud code unless you are + * handling user signup or login from the server side. In a cloud code call, + * this function will not attempt to upgrade the current token. + * @method enableRevocableSession + * @param {Object} options A Backbone-style options object. + * @static + * @return {Parse.Promise} A promise that is resolved when the process has + * completed. If a replacement session token is requested, the promise + * will be resolved after a new token has been fetched. + */ + + }, { + key: 'enableRevocableSession', + value: function (options) { + options = options || {}; + _CoreManager2.default.set('FORCE_REVOCABLE_SESSION', true); + if (canUseCurrentUser) { + var current = ParseUser.current(); + if (current) { + return current._upgradeToRevocableSession(options); + } + } + return _ParsePromise2.default.as()._thenRunCallbacks(options); + } + + /** + * Enables the use of become or the current user in a server + * environment. These features are disabled by default, since they depend on + * global objects that are not memory-safe for most servers. + * @method enableUnsafeCurrentUser + * @static + */ + + }, { + key: 'enableUnsafeCurrentUser', + value: function () { + canUseCurrentUser = true; + } + + /** + * Disables the use of become or the current user in any environment. + * These features are disabled on servers by default, since they depend on + * global objects that are not memory-safe for most servers. + * @method disableUnsafeCurrentUser + * @static + */ + + }, { + key: 'disableUnsafeCurrentUser', + value: function () { + canUseCurrentUser = false; + } + }, { + key: '_registerAuthenticationProvider', + value: function (provider) { + authProviders[provider.getAuthType()] = provider; + // Synchronize the current user with the auth provider. + ParseUser.currentAsync().then(function (current) { + if (current) { + current._synchronizeAuthData(provider.getAuthType()); + } + }); + } + }, { + key: '_logInWith', + value: function (provider, options) { + var user = new ParseUser(); + return user._linkWith(provider, options); + } + }, { + key: '_clearCache', + value: function () { + currentUserCache = null; + currentUserCacheMatchesDisk = false; + } + }, { + key: '_setCurrentUserCache', + value: function (user) { + currentUserCache = user; + } + }]); + return ParseUser; +}(_ParseObject3.default); + +exports.default = ParseUser; + +_ParseObject3.default.registerSubclass('_User', ParseUser); + +var DefaultController = { + updateUserOnDisk: function (user) { + var path = _Storage2.default.generatePath(CURRENT_USER_KEY); + var json = user.toJSON(); + json.className = '_User'; + return _Storage2.default.setItemAsync(path, (0, _stringify2.default)(json)).then(function () { + return user; + }); + }, + removeUserFromDisk: function () { + var path = _Storage2.default.generatePath(CURRENT_USER_KEY); + currentUserCacheMatchesDisk = true; + currentUserCache = null; + return _Storage2.default.removeItemAsync(path); + }, + setCurrentUser: function (user) { + currentUserCache = user; + user._cleanupAuthData(); + user._synchronizeAllAuthData(); + return DefaultController.updateUserOnDisk(user); + }, + currentUser: function () { + if (currentUserCache) { + return currentUserCache; + } + if (currentUserCacheMatchesDisk) { + return null; + } + if (_Storage2.default.async()) { + throw new Error('Cannot call currentUser() when using a platform with an async ' + 'storage system. Call currentUserAsync() instead.'); + } + var path = _Storage2.default.generatePath(CURRENT_USER_KEY); + var userData = _Storage2.default.getItem(path); + currentUserCacheMatchesDisk = true; + if (!userData) { + currentUserCache = null; + return null; + } + userData = JSON.parse(userData); + if (!userData.className) { + userData.className = '_User'; + } + if (userData._id) { + if (userData.objectId !== userData._id) { + userData.objectId = userData._id; + } + delete userData._id; + } + if (userData._sessionToken) { + userData.sessionToken = userData._sessionToken; + delete userData._sessionToken; + } + var current = _ParseObject3.default.fromJSON(userData); + currentUserCache = current; + current._synchronizeAllAuthData(); + return current; + }, + currentUserAsync: function () { + if (currentUserCache) { + return _ParsePromise2.default.as(currentUserCache); + } + if (currentUserCacheMatchesDisk) { + return _ParsePromise2.default.as(null); + } + var path = _Storage2.default.generatePath(CURRENT_USER_KEY); + return _Storage2.default.getItemAsync(path).then(function (userData) { + currentUserCacheMatchesDisk = true; + if (!userData) { + currentUserCache = null; + return _ParsePromise2.default.as(null); + } + userData = JSON.parse(userData); + if (!userData.className) { + userData.className = '_User'; + } + if (userData._id) { + if (userData.objectId !== userData._id) { + userData.objectId = userData._id; + } + delete userData._id; + } + if (userData._sessionToken) { + userData.sessionToken = userData._sessionToken; + delete userData._sessionToken; + } + var current = _ParseObject3.default.fromJSON(userData); + currentUserCache = current; + current._synchronizeAllAuthData(); + return _ParsePromise2.default.as(current); + }); + }, + signUp: function (user, attrs, options) { + var username = attrs && attrs.username || user.get('username'); + var password = attrs && attrs.password || user.get('password'); + + if (!username || !username.length) { + return _ParsePromise2.default.error(new _ParseError2.default(_ParseError2.default.OTHER_CAUSE, 'Cannot sign up user with an empty name.')); + } + if (!password || !password.length) { + return _ParsePromise2.default.error(new _ParseError2.default(_ParseError2.default.OTHER_CAUSE, 'Cannot sign up user with an empty password.')); + } + + return user.save(attrs, options).then(function () { + // Clear the password field + user._finishFetch({ password: undefined }); + + if (canUseCurrentUser) { + return DefaultController.setCurrentUser(user); + } + return user; + }); + }, + logIn: function (user, options) { + var RESTController = _CoreManager2.default.getRESTController(); + var stateController = _CoreManager2.default.getObjectStateController(); + var auth = { + username: user.get('username'), + password: user.get('password') + }; + return RESTController.request('GET', 'login', auth, options).then(function (response, status) { + user._migrateId(response.objectId); + user._setExisted(true); + stateController.setPendingOp(user._getStateIdentifier(), 'username', undefined); + stateController.setPendingOp(user._getStateIdentifier(), 'password', undefined); + response.password = undefined; + user._finishFetch(response); + if (!canUseCurrentUser) { + // We can't set the current user, so just return the one we logged in + return _ParsePromise2.default.as(user); + } + return DefaultController.setCurrentUser(user); + }); + }, + become: function (options) { + var user = new ParseUser(); + var RESTController = _CoreManager2.default.getRESTController(); + return RESTController.request('GET', 'users/me', {}, options).then(function (response, status) { + user._finishFetch(response); + user._setExisted(true); + return DefaultController.setCurrentUser(user); + }); + }, + logOut: function () { + return DefaultController.currentUserAsync().then(function (currentUser) { + var path = _Storage2.default.generatePath(CURRENT_USER_KEY); + var promise = _Storage2.default.removeItemAsync(path); + var RESTController = _CoreManager2.default.getRESTController(); + if (currentUser !== null) { + var currentSession = currentUser.getSessionToken(); + if (currentSession && (0, _isRevocableSession2.default)(currentSession)) { + promise = promise.then(function () { + return RESTController.request('POST', 'logout', {}, { sessionToken: currentSession }); + }); + } + currentUser._logOutWithAll(); + currentUser._finishFetch({ sessionToken: undefined }); + } + currentUserCacheMatchesDisk = true; + currentUserCache = null; + + return promise; + }); + }, + requestPasswordReset: function (email, options) { + var RESTController = _CoreManager2.default.getRESTController(); + return RESTController.request('POST', 'requestPasswordReset', { email: email }, options); + }, + upgradeToRevocableSession: function (user, options) { + var token = user.getSessionToken(); + if (!token) { + return _ParsePromise2.default.error(new _ParseError2.default(_ParseError2.default.SESSION_MISSING, 'Cannot upgrade a user with no session token')); + } + + options.sessionToken = token; + + var RESTController = _CoreManager2.default.getRESTController(); + return RESTController.request('POST', 'upgradeToRevocableSession', {}, options).then(function (result) { + var session = new _ParseSession2.default(); + session._finishFetch(result); + user._finishFetch({ sessionToken: session.getSessionToken() }); + if (user.isCurrent()) { + return DefaultController.setCurrentUser(user); + } + return _ParsePromise2.default.as(user); + }); + }, + linkWith: function (user, authData) { + return user.save({ authData: authData }).then(function () { + if (canUseCurrentUser) { + return DefaultController.setCurrentUser(user); + } + return user; + }); + } +}; + +_CoreManager2.default.setUserController(DefaultController); \ No newline at end of file diff --git a/lib/node/Push.js b/lib/node/Push.js new file mode 100644 index 000000000..cfb740767 --- /dev/null +++ b/lib/node/Push.js @@ -0,0 +1,98 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _typeof2 = require('babel-runtime/helpers/typeof'); + +var _typeof3 = _interopRequireDefault(_typeof2); + +exports.send = send; + +var _CoreManager = require('./CoreManager'); + +var _CoreManager2 = _interopRequireDefault(_CoreManager); + +var _ParseQuery = require('./ParseQuery'); + +var _ParseQuery2 = _interopRequireDefault(_ParseQuery); + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { default: obj }; +} + +/** + * Contains functions to deal with Push in Parse. + * @class Parse.Push + * @static + */ + +/** + * Sends a push notification. + * @method send + * @param {Object} data - The data of the push notification. Valid fields + * are: + *+ * var dimensions = { + * gender: 'm', + * source: 'web', + * dayType: 'weekend' + * }; + * Parse.Analytics.track('signup', dimensions); + *+ * + * There is a default limit of 8 dimensions per event tracked. + * + * @method track + * @param {String} name The name of the custom event to report to Parse as + * having happened. + * @param {Object} dimensions The dictionary of information by which to + * segment this event. + * @param {Object} options A Backbone-style callback object. + * @return {Parse.Promise} A promise that is resolved when the round-trip + * to the server completes. + */ +export function track(name, dimensions, options) { + name = name || ''; + name = name.replace(/^\s*/, ''); + name = name.replace(/\s*$/, ''); + if (name.length === 0) { + throw new TypeError('A name for the custom event must be provided'); + } + + for (var key in dimensions) { + if (typeof key !== 'string' || typeof dimensions[key] !== 'string') { + throw new TypeError('track() dimensions expects keys and values of type "string".'); + } + } + + options = options || {}; + return CoreManager.getAnalyticsController().track(name, dimensions)._thenRunCallbacks(options); +} + +var DefaultController = { + track(name, dimensions) { + var RESTController = CoreManager.getRESTController(); + return RESTController.request('POST', 'events/' + name, { dimensions: dimensions }); + } +}; + +CoreManager.setAnalyticsController(DefaultController); \ No newline at end of file diff --git a/lib/react-native/Cloud.js b/lib/react-native/Cloud.js new file mode 100644 index 000000000..604707acd --- /dev/null +++ b/lib/react-native/Cloud.js @@ -0,0 +1,86 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + */ + +import CoreManager from './CoreManager'; +import decode from './decode'; +import encode from './encode'; +import ParseError from './ParseError'; +import ParsePromise from './ParsePromise'; + +/** + * Contains functions for calling and declaring + * cloud functions. + *
+ * Some functions are only available from Cloud Code. + *
+ * + * @class Parse.Cloud + * @static + */ + +/** + * Makes a call to a cloud function. + * @method run + * @param {String} name The function name. + * @param {Object} data The parameters to send to the cloud function. + * @param {Object} options A Backbone-style options object + * options.success, if set, should be a function to handle a successful + * call to a cloud function. options.error should be a function that + * handles an error running the cloud function. Both functions are + * optional. Both functions take a single argument. + * @return {Parse.Promise} A promise that will be resolved with the result + * of the function. + */ +export function run(name, data, options) { + options = options || {}; + + if (typeof name !== 'string' || name.length === 0) { + throw new TypeError('Cloud function name must be a string.'); + } + + var requestOptions = {}; + if (options.useMasterKey) { + requestOptions.useMasterKey = options.useMasterKey; + } + if (options.sessionToken) { + requestOptions.sessionToken = options.sessionToken; + } + + return CoreManager.getCloudController().run(name, data, requestOptions)._thenRunCallbacks(options); +} + +var DefaultController = { + run(name, data, options) { + var RESTController = CoreManager.getRESTController(); + + var payload = encode(data, true); + + var requestOptions = {}; + if (options.hasOwnProperty('useMasterKey')) { + requestOptions.useMasterKey = options.useMasterKey; + } + if (options.hasOwnProperty('sessionToken')) { + requestOptions.sessionToken = options.sessionToken; + } + + var request = RESTController.request('POST', 'functions/' + name, payload, requestOptions); + + return request.then(function (res) { + var decoded = decode(res); + if (decoded && decoded.hasOwnProperty('result')) { + return ParsePromise.as(decoded.result); + } + return ParsePromise.error(new ParseError(ParseError.INVALID_JSON, 'The server returned an invalid response.')); + })._thenRunCallbacks(options); + } +}; + +CoreManager.setCloudController(DefaultController); \ No newline at end of file diff --git a/lib/react-native/CoreManager.js b/lib/react-native/CoreManager.js new file mode 100644 index 000000000..97062e559 --- /dev/null +++ b/lib/react-native/CoreManager.js @@ -0,0 +1,188 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + */ + +var config = { + // Defaults + IS_NODE: typeof process !== 'undefined' && !!process.versions && !!process.versions.node && !process.versions.electron, + REQUEST_ATTEMPT_LIMIT: 5, + SERVER_URL: 'https://api.parse.com/1', + LIVEQUERY_SERVER_URL: null, + VERSION: 'js' + '1.9.2', + APPLICATION_ID: null, + JAVASCRIPT_KEY: null, + MASTER_KEY: null, + USE_MASTER_KEY: false, + PERFORM_USER_REWRITE: true, + FORCE_REVOCABLE_SESSION: false +}; + +function requireMethods(name, methods, controller) { + methods.forEach(func => { + if (typeof controller[func] !== 'function') { + throw new Error(`${name} must implement ${func}()`); + } + }); +} + +module.exports = { + get: function (key) { + if (config.hasOwnProperty(key)) { + return config[key]; + } + throw new Error('Configuration key not found: ' + key); + }, + + set: function (key, value) { + config[key] = value; + }, + + /* Specialized Controller Setters/Getters */ + + setAnalyticsController(controller) { + requireMethods('AnalyticsController', ['track'], controller); + config['AnalyticsController'] = controller; + }, + + getAnalyticsController() { + return config['AnalyticsController']; + }, + + setCloudController(controller) { + requireMethods('CloudController', ['run'], controller); + config['CloudController'] = controller; + }, + + getCloudController() { + return config['CloudController']; + }, + + setConfigController(controller) { + requireMethods('ConfigController', ['current', 'get'], controller); + config['ConfigController'] = controller; + }, + + getConfigController() { + return config['ConfigController']; + }, + + setFileController(controller) { + requireMethods('FileController', ['saveFile', 'saveBase64'], controller); + config['FileController'] = controller; + }, + + getFileController() { + return config['FileController']; + }, + + setInstallationController(controller) { + requireMethods('InstallationController', ['currentInstallationId'], controller); + config['InstallationController'] = controller; + }, + + getInstallationController() { + return config['InstallationController']; + }, + + setObjectController(controller) { + requireMethods('ObjectController', ['save', 'fetch', 'destroy'], controller); + config['ObjectController'] = controller; + }, + + getObjectController() { + return config['ObjectController']; + }, + + setObjectStateController(controller) { + requireMethods('ObjectStateController', ['getState', 'initializeState', 'removeState', 'getServerData', 'setServerData', 'getPendingOps', 'setPendingOp', 'pushPendingState', 'popPendingState', 'mergeFirstPendingState', 'getObjectCache', 'estimateAttribute', 'estimateAttributes', 'commitServerChanges', 'enqueueTask', 'clearAllState'], controller); + + config['ObjectStateController'] = controller; + }, + + getObjectStateController() { + return config['ObjectStateController']; + }, + + setPushController(controller) { + requireMethods('PushController', ['send'], controller); + config['PushController'] = controller; + }, + + getPushController() { + return config['PushController']; + }, + + setQueryController(controller) { + requireMethods('QueryController', ['find'], controller); + config['QueryController'] = controller; + }, + + getQueryController() { + return config['QueryController']; + }, + + setRESTController(controller) { + requireMethods('RESTController', ['request', 'ajax'], controller); + config['RESTController'] = controller; + }, + + getRESTController() { + return config['RESTController']; + }, + + setSessionController(controller) { + requireMethods('SessionController', ['getSession'], controller); + config['SessionController'] = controller; + }, + + getSessionController() { + return config['SessionController']; + }, + + setStorageController(controller) { + if (controller.async) { + requireMethods('An async StorageController', ['getItemAsync', 'setItemAsync', 'removeItemAsync'], controller); + } else { + requireMethods('A synchronous StorageController', ['getItem', 'setItem', 'removeItem'], controller); + } + config['StorageController'] = controller; + }, + + getStorageController() { + return config['StorageController']; + }, + + setUserController(controller) { + requireMethods('UserController', ['setCurrentUser', 'currentUser', 'currentUserAsync', 'signUp', 'logIn', 'become', 'logOut', 'requestPasswordReset', 'upgradeToRevocableSession', 'linkWith'], controller); + config['UserController'] = controller; + }, + + getUserController() { + return config['UserController']; + }, + + setLiveQueryController(controller) { + requireMethods('LiveQueryController', ['subscribe', 'unsubscribe', 'open', 'close'], controller); + config['LiveQueryController'] = controller; + }, + + getLiveQueryController() { + return config['LiveQueryController']; + }, + + setHooksController(controller) { + requireMethods('HooksController', ['create', 'get', 'update', 'remove'], controller); + config['HooksController'] = controller; + }, + + getHooksController() { + return config['HooksController']; + } +}; \ No newline at end of file diff --git a/lib/react-native/EventEmitter.js b/lib/react-native/EventEmitter.js new file mode 100644 index 000000000..3450b4ac6 --- /dev/null +++ b/lib/react-native/EventEmitter.js @@ -0,0 +1,16 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * This is a simple wrapper to unify EventEmitter implementations across platforms. + */ + +{ + const EventEmitter = require('EventEmitter'); + EventEmitter.prototype.on = EventEmitter.prototype.addListener; + module.exports = EventEmitter; +} \ No newline at end of file diff --git a/lib/react-native/FacebookUtils.js b/lib/react-native/FacebookUtils.js new file mode 100644 index 000000000..24ce10c01 --- /dev/null +++ b/lib/react-native/FacebookUtils.js @@ -0,0 +1,229 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * -weak + */ + +import parseDate from './parseDate'; +import ParseUser from './ParseUser'; + +var PUBLIC_KEY = "*"; + +var initialized = false; +var requestedPermissions; +var initOptions; +var provider = { + authenticate(options) { + if (typeof FB === 'undefined') { + options.error(this, 'Facebook SDK not found.'); + } + FB.login(response => { + if (response.authResponse) { + if (options.success) { + options.success(this, { + id: response.authResponse.userID, + access_token: response.authResponse.accessToken, + expiration_date: new Date(response.authResponse.expiresIn * 1000 + new Date().getTime()).toJSON() + }); + } + } else { + if (options.error) { + options.error(this, response); + } + } + }, { + scope: requestedPermissions + }); + }, + + restoreAuthentication(authData) { + if (authData) { + var expiration = parseDate(authData.expiration_date); + var expiresIn = expiration ? (expiration.getTime() - new Date().getTime()) / 1000 : 0; + + var authResponse = { + userID: authData.id, + accessToken: authData.access_token, + expiresIn: expiresIn + }; + var newOptions = {}; + if (initOptions) { + for (var key in initOptions) { + newOptions[key] = initOptions[key]; + } + } + newOptions.authResponse = authResponse; + + // Suppress checks for login status from the browser. + newOptions.status = false; + + // If the user doesn't match the one known by the FB SDK, log out. + // Most of the time, the users will match -- it's only in cases where + // the FB SDK knows of a different user than the one being restored + // from a Parse User that logged in with username/password. + var existingResponse = FB.getAuthResponse(); + if (existingResponse && existingResponse.userID !== authResponse.userID) { + FB.logout(); + } + + FB.init(newOptions); + } + return true; + }, + + getAuthType() { + return 'facebook'; + }, + + deauthenticate() { + this.restoreAuthentication(null); + } +}; + +/** + * Provides a set of utilities for using Parse with Facebook. + * @class Parse.FacebookUtils + * @static + */ +var FacebookUtils = { + /** + * Initializes Parse Facebook integration. Call this function after you + * have loaded the Facebook Javascript SDK with the same parameters + * as you would pass to
+ *
+ * FB.init()
. Parse.FacebookUtils will invoke FB.init() for you
+ * with these arguments.
+ *
+ * @method init
+ * @param {Object} options Facebook options argument as described here:
+ *
+ * FB.init(). The status flag will be coerced to 'false' because it
+ * interferes with Parse Facebook integration. Call FB.getLoginStatus()
+ * explicitly if this behavior is required by your application.
+ */
+ init(options) {
+ if (typeof FB === 'undefined') {
+ throw new Error('The Facebook JavaScript SDK must be loaded before calling init.');
+ }
+ initOptions = {};
+ if (options) {
+ for (var key in options) {
+ initOptions[key] = options[key];
+ }
+ }
+ if (initOptions.status && typeof console !== 'undefined') {
+ var warn = console.warn || console.log || function () {};
+ warn.call(console, 'The "status" flag passed into' + ' FB.init, when set to true, can interfere with Parse Facebook' + ' integration, so it has been suppressed. Please call' + ' FB.getLoginStatus() explicitly if you require this behavior.');
+ }
+ initOptions.status = false;
+ FB.init(initOptions);
+ ParseUser._registerAuthenticationProvider(provider);
+ initialized = true;
+ },
+
+ /**
+ * Gets whether the user has their account linked to Facebook.
+ *
+ * @method isLinked
+ * @param {Parse.User} user User to check for a facebook link.
+ * The user must be logged in on this device.
+ * @return {Boolean} true
if the user has their account
+ * linked to Facebook.
+ */
+ isLinked(user) {
+ return user._isLinked('facebook');
+ },
+
+ /**
+ * Logs in a user using Facebook. This method delegates to the Facebook
+ * SDK to authenticate the user, and then automatically logs in (or
+ * creates, in the case where it is a new user) a Parse.User.
+ *
+ * @method logIn
+ * @param {String, Object} permissions The permissions required for Facebook
+ * log in. This is a comma-separated string of permissions.
+ * Alternatively, supply a Facebook authData object as described in our
+ * REST API docs if you want to handle getting facebook auth tokens
+ * yourself.
+ * @param {Object} options Standard options object with success and error
+ * callbacks.
+ */
+ logIn(permissions, options) {
+ if (!permissions || typeof permissions === 'string') {
+ if (!initialized) {
+ throw new Error('You must initialize FacebookUtils before calling logIn.');
+ }
+ requestedPermissions = permissions;
+ return ParseUser._logInWith('facebook', options);
+ } else {
+ var newOptions = {};
+ if (options) {
+ for (var key in options) {
+ newOptions[key] = options[key];
+ }
+ }
+ newOptions.authData = permissions;
+ return ParseUser._logInWith('facebook', newOptions);
+ }
+ },
+
+ /**
+ * Links Facebook to an existing PFUser. This method delegates to the
+ * Facebook SDK to authenticate the user, and then automatically links
+ * the account to the Parse.User.
+ *
+ * @method link
+ * @param {Parse.User} user User to link to Facebook. This must be the
+ * current user.
+ * @param {String, Object} permissions The permissions required for Facebook
+ * log in. This is a comma-separated string of permissions.
+ * Alternatively, supply a Facebook authData object as described in our
+ * REST API docs if you want to handle getting facebook auth tokens
+ * yourself.
+ * @param {Object} options Standard options object with success and error
+ * callbacks.
+ */
+ link(user, permissions, options) {
+ if (!permissions || typeof permissions === 'string') {
+ if (!initialized) {
+ throw new Error('You must initialize FacebookUtils before calling link.');
+ }
+ requestedPermissions = permissions;
+ return user._linkWith('facebook', options);
+ } else {
+ var newOptions = {};
+ if (options) {
+ for (var key in options) {
+ newOptions[key] = options[key];
+ }
+ }
+ newOptions.authData = permissions;
+ return user._linkWith('facebook', newOptions);
+ }
+ },
+
+ /**
+ * Unlinks the Parse.User from a Facebook account.
+ *
+ * @method unlink
+ * @param {Parse.User} user User to unlink from Facebook. This must be the
+ * current user.
+ * @param {Object} options Standard options object with success and error
+ * callbacks.
+ */
+ unlink: function (user, options) {
+ if (!initialized) {
+ throw new Error('You must initialize FacebookUtils before calling unlink.');
+ }
+ return user._unlinkFrom('facebook', options);
+ }
+};
+
+export default FacebookUtils;
\ No newline at end of file
diff --git a/lib/react-native/InstallationController.js b/lib/react-native/InstallationController.js
new file mode 100644
index 000000000..75aad6d0b
--- /dev/null
+++ b/lib/react-native/InstallationController.js
@@ -0,0 +1,54 @@
+/**
+ * Copyright (c) 2015-present, Parse, LLC.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ *
+ *
+ */
+
+import CoreManager from './CoreManager';
+import ParsePromise from './ParsePromise';
+import Storage from './Storage';
+
+var iidCache = null;
+
+function hexOctet() {
+ return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
+}
+
+function generateId() {
+ return hexOctet() + hexOctet() + '-' + hexOctet() + '-' + hexOctet() + '-' + hexOctet() + '-' + hexOctet() + hexOctet() + hexOctet();
+}
+
+var InstallationController = {
+ currentInstallationId() {
+ if (typeof iidCache === 'string') {
+ return ParsePromise.as(iidCache);
+ }
+ var path = Storage.generatePath('installationId');
+ return Storage.getItemAsync(path).then(iid => {
+ if (!iid) {
+ iid = generateId();
+ return Storage.setItemAsync(path, iid).then(() => {
+ iidCache = iid;
+ return iid;
+ });
+ }
+ iidCache = iid;
+ return iid;
+ });
+ },
+
+ _clearCache() {
+ iidCache = null;
+ },
+
+ _setInstallationIdCache(iid) {
+ iidCache = iid;
+ }
+};
+
+module.exports = InstallationController;
\ No newline at end of file
diff --git a/lib/react-native/LiveQueryClient.js b/lib/react-native/LiveQueryClient.js
new file mode 100644
index 000000000..0af495cb4
--- /dev/null
+++ b/lib/react-native/LiveQueryClient.js
@@ -0,0 +1,430 @@
+/**
+ * Copyright (c) 2015-present, Parse, LLC.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+import EventEmitter from './EventEmitter';
+import ParsePromise from './ParsePromise';
+import ParseObject from './ParseObject';
+import LiveQuerySubscription from './LiveQuerySubscription';
+
+// The LiveQuery client inner state
+const CLIENT_STATE = {
+ INITIALIZED: 'initialized',
+ CONNECTING: 'connecting',
+ CONNECTED: 'connected',
+ CLOSED: 'closed',
+ RECONNECTING: 'reconnecting',
+ DISCONNECTED: 'disconnected'
+};
+
+// The event type the LiveQuery client should sent to server
+const OP_TYPES = {
+ CONNECT: 'connect',
+ SUBSCRIBE: 'subscribe',
+ UNSUBSCRIBE: 'unsubscribe',
+ ERROR: 'error'
+};
+
+// The event we get back from LiveQuery server
+const OP_EVENTS = {
+ CONNECTED: 'connected',
+ SUBSCRIBED: 'subscribed',
+ UNSUBSCRIBED: 'unsubscribed',
+ ERROR: 'error',
+ CREATE: 'create',
+ UPDATE: 'update',
+ ENTER: 'enter',
+ LEAVE: 'leave',
+ DELETE: 'delete'
+};
+
+// The event the LiveQuery client should emit
+const CLIENT_EMMITER_TYPES = {
+ CLOSE: 'close',
+ ERROR: 'error',
+ OPEN: 'open'
+};
+
+// The event the LiveQuery subscription should emit
+const SUBSCRIPTION_EMMITER_TYPES = {
+ OPEN: 'open',
+ CLOSE: 'close',
+ ERROR: 'error',
+ CREATE: 'create',
+ UPDATE: 'update',
+ ENTER: 'enter',
+ LEAVE: 'leave',
+ DELETE: 'delete'
+};
+
+let generateInterval = k => {
+ return Math.random() * Math.min(30, Math.pow(2, k) - 1) * 1000;
+};
+
+/**
+ * Creates a new LiveQueryClient.
+ * Extends events.EventEmitter
+ * cloud functions.
+ *
+ * A wrapper of a standard WebSocket client. We add several useful methods to
+ * help you connect/disconnect to LiveQueryServer, subscribe/unsubscribe a ParseQuery easily.
+ *
+ * javascriptKey and masterKey are used for verifying the LiveQueryClient when it tries
+ * to connect to the LiveQuery server
+ *
+ * @class Parse.LiveQueryClient
+ * @constructor
+ * @param {Object} options
+ * @param {string} options.applicationId - applicationId of your Parse app
+ * @param {string} options.serverURL - the URL of your LiveQuery server
+ * @param {string} options.javascriptKey (optional)
+ * @param {string} options.masterKey (optional) Your Parse Master Key. (Node.js only!)
+ * @param {string} options.sessionToken (optional)
+ *
+ *
+ * We expose three events to help you monitor the status of the LiveQueryClient.
+ *
+ * + * let Parse = require('parse/node'); + * let LiveQueryClient = Parse.LiveQueryClient; + * let client = new LiveQueryClient({ + * applicationId: '', + * serverURL: '', + * javascriptKey: '', + * masterKey: '' + * }); + *+ * + * Open - When we establish the WebSocket connection to the LiveQuery server, you'll get this event. + *
+ * client.on('open', () => { + * + * });+ * + * Close - When we lose the WebSocket connection to the LiveQuery server, you'll get this event. + *
+ * client.on('close', () => { + * + * });+ * + * Error - When some network error or LiveQuery server error happens, you'll get this event. + *
+ * client.on('error', (error) => { + * + * });+ * + * + */ +export default class LiveQueryClient extends EventEmitter { + + constructor({ + applicationId, + serverURL, + javascriptKey, + masterKey, + sessionToken + }) { + super(); + + if (!serverURL || serverURL.indexOf('ws') !== 0) { + throw new Error('You need to set a proper Parse LiveQuery server url before using LiveQueryClient'); + } + + this.reconnectHandle = null; + this.attempts = 1;; + this.id = 0; + this.requestId = 1; + this.serverURL = serverURL; + this.applicationId = applicationId; + this.javascriptKey = javascriptKey; + this.masterKey = masterKey; + this.sessionToken = sessionToken; + this.connectPromise = new ParsePromise(); + this.subscriptions = new Map(); + this.state = CLIENT_STATE.INITIALIZED; + } + + shouldOpen() { + return this.state === CLIENT_STATE.INITIALIZED || this.state === CLIENT_STATE.DISCONNECTED; + } + + /** + * Subscribes to a ParseQuery + * + * If you provide the sessionToken, when the LiveQuery server gets ParseObject's + * updates from parse server, it'll try to check whether the sessionToken fulfills + * the ParseObject's ACL. The LiveQuery server will only send updates to clients whose + * sessionToken is fit for the ParseObject's ACL. You can check the LiveQuery protocol + * here for more details. The subscription you get is the same subscription you get + * from our Standard API. + * + * @method subscribe + * @param {Object} query - the ParseQuery you want to subscribe to + * @param {string} sessionToken (optional) + * @return {Object} subscription + */ + subscribe(query, sessionToken) { + if (!query) { + return; + } + let where = query.toJSON().where; + let className = query.className; + let subscribeRequest = { + op: OP_TYPES.SUBSCRIBE, + requestId: this.requestId, + query: { + className, + where + } + }; + + if (sessionToken) { + subscribeRequest.sessionToken = sessionToken; + } + + let subscription = new LiveQuerySubscription(this.requestId, query, sessionToken); + this.subscriptions.set(this.requestId, subscription); + this.requestId += 1; + this.connectPromise.then(() => { + this.socket.send(JSON.stringify(subscribeRequest)); + }); + + // adding listener so process does not crash + // best practice is for developer to register their own listener + subscription.on('error', () => {}); + + return subscription; + } + + /** + * After calling unsubscribe you'll stop receiving events from the subscription object. + * + * @method unsubscribe + * @param {Object} subscription - subscription you would like to unsubscribe from. + */ + unsubscribe(subscription) { + if (!subscription) { + return; + } + + this.subscriptions.delete(subscription.id); + let unsubscribeRequest = { + op: OP_TYPES.UNSUBSCRIBE, + requestId: subscription.id + }; + this.connectPromise.then(() => { + this.socket.send(JSON.stringify(unsubscribeRequest)); + }); + } + + /** + * After open is called, the LiveQueryClient will try to send a connect request + * to the LiveQuery server. + * + * @method open + */ + open() { + let WebSocketImplementation = this._getWebSocketImplementation(); + if (!WebSocketImplementation) { + this.emit(CLIENT_EMMITER_TYPES.ERROR, 'Can not find WebSocket implementation'); + return; + } + + if (this.state !== CLIENT_STATE.RECONNECTING) { + this.state = CLIENT_STATE.CONNECTING; + } + + // Get WebSocket implementation + this.socket = new WebSocketImplementation(this.serverURL); + + // Bind WebSocket callbacks + this.socket.onopen = () => { + this._handleWebSocketOpen(); + }; + + this.socket.onmessage = event => { + this._handleWebSocketMessage(event); + }; + + this.socket.onclose = () => { + this._handleWebSocketClose(); + }; + + this.socket.onerror = error => { + this._handleWebSocketError(error); + }; + } + + resubscribe() { + this.subscriptions.forEach((subscription, requestId) => { + let query = subscription.query; + let where = query.toJSON().where; + let className = query.className; + let sessionToken = subscription.sessionToken; + let subscribeRequest = { + op: OP_TYPES.SUBSCRIBE, + requestId, + query: { + className, + where + } + }; + + if (sessionToken) { + subscribeRequest.sessionToken = sessionToken; + } + + this.connectPromise.then(() => { + this.socket.send(JSON.stringify(subscribeRequest)); + }); + }); + } + + /** + * This method will close the WebSocket connection to this LiveQueryClient, + * cancel the auto reconnect and unsubscribe all subscriptions based on it. + * + * @method close + */ + close() { + if (this.state === CLIENT_STATE.INITIALIZED || this.state === CLIENT_STATE.DISCONNECTED) { + return; + } + this.state = CLIENT_STATE.DISCONNECTED; + this.socket.close(); + // Notify each subscription about the close + for (let subscription of this.subscriptions.values()) { + subscription.emit(SUBSCRIPTION_EMMITER_TYPES.CLOSE); + } + this._handleReset(); + this.emit(CLIENT_EMMITER_TYPES.CLOSE); + } + + _getWebSocketImplementation() { + return WebSocket; + } + + // ensure we start with valid state if connect is called again after close + _handleReset() { + this.attempts = 1;; + this.id = 0; + this.requestId = 1; + this.connectPromise = new ParsePromise(); + this.subscriptions = new Map(); + } + + _handleWebSocketOpen() { + this.attempts = 1; + let connectRequest = { + op: OP_TYPES.CONNECT, + applicationId: this.applicationId, + javascriptKey: this.javascriptKey, + masterKey: this.masterKey, + sessionToken: this.sessionToken + }; + this.socket.send(JSON.stringify(connectRequest)); + } + + _handleWebSocketMessage(event) { + let data = event.data; + if (typeof data === 'string') { + data = JSON.parse(data); + } + let subscription = null; + if (data.requestId) { + subscription = this.subscriptions.get(data.requestId); + } + switch (data.op) { + case OP_EVENTS.CONNECTED: + if (this.state === CLIENT_STATE.RECONNECTING) { + this.resubscribe(); + } + this.emit(CLIENT_EMMITER_TYPES.OPEN); + this.id = data.clientId; + this.connectPromise.resolve(); + this.state = CLIENT_STATE.CONNECTED; + break; + case OP_EVENTS.SUBSCRIBED: + if (subscription) { + subscription.emit(SUBSCRIPTION_EMMITER_TYPES.OPEN); + } + break; + case OP_EVENTS.ERROR: + if (data.requestId) { + if (subscription) { + subscription.emit(SUBSCRIPTION_EMMITER_TYPES.ERROR, data.error); + } + } else { + this.emit(CLIENT_EMMITER_TYPES.ERROR, data.error); + } + break; + case OP_EVENTS.UNSUBSCRIBED: + // We have already deleted subscription in unsubscribe(), do nothing here + break; + default: + // create, update, enter, leave, delete cases + let className = data.object.className; + // Delete the extrea __type and className fields during transfer to full JSON + delete data.object.__type; + delete data.object.className; + let parseObject = new ParseObject(className); + parseObject._finishFetch(data.object); + if (!subscription) { + break; + } + subscription.emit(data.op, parseObject); + } + } + + _handleWebSocketClose() { + if (this.state === CLIENT_STATE.DISCONNECTED) { + return; + } + this.state = CLIENT_STATE.CLOSED; + this.emit(CLIENT_EMMITER_TYPES.CLOSE); + // Notify each subscription about the close + for (let subscription of this.subscriptions.values()) { + subscription.emit(SUBSCRIPTION_EMMITER_TYPES.CLOSE); + } + this._handleReconnect(); + } + + _handleWebSocketError(error) { + this.emit(CLIENT_EMMITER_TYPES.ERROR, error); + for (let subscription of this.subscriptions.values()) { + subscription.emit(SUBSCRIPTION_EMMITER_TYPES.ERROR); + } + this._handleReconnect(); + } + + _handleReconnect() { + // if closed or currently reconnecting we stop attempting to reconnect + if (this.state === CLIENT_STATE.DISCONNECTED) { + return; + } + + this.state = CLIENT_STATE.RECONNECTING; + let time = generateInterval(this.attempts); + + // handle case when both close/error occur at frequent rates we ensure we do not reconnect unnecessarily. + // we're unable to distinguish different between close/error when we're unable to reconnect therefore + // we try to reonnect in both cases + // server side ws and browser WebSocket behave differently in when close/error get triggered + + if (this.reconnectHandle) { + clearTimeout(this.reconnectHandle); + } + + this.reconnectHandle = setTimeout((() => { + this.attempts++; + this.connectPromise = new ParsePromise(); + this.open(); + }).bind(this), time); + } +} \ No newline at end of file diff --git a/lib/react-native/LiveQuerySubscription.js b/lib/react-native/LiveQuerySubscription.js new file mode 100644 index 000000000..4f19b2b6e --- /dev/null +++ b/lib/react-native/LiveQuerySubscription.js @@ -0,0 +1,112 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ + +import EventEmitter from './EventEmitter'; +import CoreManager from './CoreManager'; + +/** + * Creates a new LiveQuery Subscription. + * Extends events.EventEmitter + * cloud functions. + * + * @constructor + * @param {string} id - subscription id + * @param {string} query - query to subscribe to + * @param {string} sessionToken - optional session token + * + *
Open Event - When you call query.subscribe(), we send a subscribe request to + * the LiveQuery server, when we get the confirmation from the LiveQuery server, + * this event will be emitted. When the client loses WebSocket connection to the + * LiveQuery server, we will try to auto reconnect the LiveQuery server. If we + * reconnect the LiveQuery server and successfully resubscribe the ParseQuery, + * you'll also get this event. + * + *
+ * subscription.on('open', () => { + * + * });+ * + *
Create Event - When a new ParseObject is created and it fulfills the ParseQuery you subscribe, + * you'll get this event. The object is the ParseObject which is created. + * + *
+ * subscription.on('create', (object) => { + * + * });+ * + *
Update Event - When an existing ParseObject which fulfills the ParseQuery you subscribe + * is updated (The ParseObject fulfills the ParseQuery before and after changes), + * you'll get this event. The object is the ParseObject which is updated. + * Its content is the latest value of the ParseObject. + * + *
+ * subscription.on('update', (object) => { + * + * });+ * + *
Enter Event - When an existing ParseObject's old value doesn't fulfill the ParseQuery + * but its new value fulfills the ParseQuery, you'll get this event. The object is the + * ParseObject which enters the ParseQuery. Its content is the latest value of the ParseObject. + * + *
+ * subscription.on('enter', (object) => { + * + * });+ * + * + *
Update Event - When an existing ParseObject's old value fulfills the ParseQuery but its new value + * doesn't fulfill the ParseQuery, you'll get this event. The object is the ParseObject + * which leaves the ParseQuery. Its content is the latest value of the ParseObject. + * + *
+ * subscription.on('leave', (object) => { + * + * });+ * + * + *
Delete Event - When an existing ParseObject which fulfills the ParseQuery is deleted, you'll + * get this event. The object is the ParseObject which is deleted. + * + *
+ * subscription.on('delete', (object) => { + * + * });+ * + * + *
Close Event - When the client loses the WebSocket connection to the LiveQuery + * server and we stop receiving events, you'll get this event. + * + *
+ * subscription.on('close', () => { + * + * });+ * + * + */ +export default class Subscription extends EventEmitter { + constructor(id, query, sessionToken) { + super(); + this.id = id; + this.query = query; + this.sessionToken = sessionToken; + } + + /** + * @method unsubscribe + */ + unsubscribe() { + let _this = this; + CoreManager.getLiveQueryController().getDefaultLiveQueryClient().then(liveQueryClient => { + liveQueryClient.unsubscribe(_this); + _this.emit('close'); + this.resolve(); + }); + } +} \ No newline at end of file diff --git a/lib/react-native/ObjectStateMutations.js b/lib/react-native/ObjectStateMutations.js new file mode 100644 index 000000000..349dd150b --- /dev/null +++ b/lib/react-native/ObjectStateMutations.js @@ -0,0 +1,121 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + */ + +import encode from './encode'; +import ParseFile from './ParseFile'; +import ParseObject from './ParseObject'; +import ParsePromise from './ParsePromise'; +import ParseRelation from './ParseRelation'; +import TaskQueue from './TaskQueue'; +import { RelationOp } from './ParseOp'; + +export function defaultState() { + return { + serverData: {}, + pendingOps: [{}], + objectCache: {}, + tasks: new TaskQueue(), + existed: false + }; +} + +export function setServerData(serverData, attributes) { + for (let attr in attributes) { + if (typeof attributes[attr] !== 'undefined') { + serverData[attr] = attributes[attr]; + } else { + delete serverData[attr]; + } + } +} + +export function setPendingOp(pendingOps, attr, op) { + let last = pendingOps.length - 1; + if (op) { + pendingOps[last][attr] = op; + } else { + delete pendingOps[last][attr]; + } +} + +export function pushPendingState(pendingOps) { + pendingOps.push({}); +} + +export function popPendingState(pendingOps) { + let first = pendingOps.shift(); + if (!pendingOps.length) { + pendingOps[0] = {}; + } + return first; +} + +export function mergeFirstPendingState(pendingOps) { + let first = popPendingState(pendingOps); + let next = pendingOps[0]; + for (let attr in first) { + if (next[attr] && first[attr]) { + let merged = next[attr].mergeWith(first[attr]); + if (merged) { + next[attr] = merged; + } + } else { + next[attr] = first[attr]; + } + } +} + +export function estimateAttribute(serverData, pendingOps, className, id, attr) { + let value = serverData[attr]; + for (let i = 0; i < pendingOps.length; i++) { + if (pendingOps[i][attr]) { + if (pendingOps[i][attr] instanceof RelationOp) { + if (id) { + value = pendingOps[i][attr].applyTo(value, { className: className, id: id }, attr); + } + } else { + value = pendingOps[i][attr].applyTo(value); + } + } + } + return value; +} + +export function estimateAttributes(serverData, pendingOps, className, id) { + let data = {}; + + for (var attr in serverData) { + data[attr] = serverData[attr]; + } + for (let i = 0; i < pendingOps.length; i++) { + for (attr in pendingOps[i]) { + if (pendingOps[i][attr] instanceof RelationOp) { + if (id) { + data[attr] = pendingOps[i][attr].applyTo(data[attr], { className: className, id: id }, attr); + } + } else { + data[attr] = pendingOps[i][attr].applyTo(data[attr]); + } + } + } + return data; +} + +export function commitServerChanges(serverData, objectCache, changes) { + for (let attr in changes) { + let val = changes[attr]; + serverData[attr] = val; + if (val && typeof val === 'object' && !(val instanceof ParseObject) && !(val instanceof ParseFile) && !(val instanceof ParseRelation)) { + let json = encode(val, false, true); + objectCache[attr] = JSON.stringify(json); + } + } +} \ No newline at end of file diff --git a/lib/react-native/Parse.js b/lib/react-native/Parse.js new file mode 100644 index 000000000..b95835026 --- /dev/null +++ b/lib/react-native/Parse.js @@ -0,0 +1,141 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +import decode from './decode'; +import encode from './encode'; +import CoreManager from './CoreManager'; +import InstallationController from './InstallationController'; +import * as ParseOp from './ParseOp'; +import RESTController from './RESTController'; + +/** + * Contains all Parse API classes and functions. + * @class Parse + * @static + */ +var Parse = { + /** + * Call this method first to set up your authentication tokens for Parse. + * You can get your keys from the Data Browser on parse.com. + * @method initialize + * @param {String} applicationId Your Parse Application ID. + * @param {String} javaScriptKey (optional) Your Parse JavaScript Key (Not needed for parse-server) + * @param {String} masterKey (optional) Your Parse Master Key. (Node.js only!) + * @static + */ + initialize(applicationId, javaScriptKey) { + Parse._initialize(applicationId, javaScriptKey); + }, + + _initialize(applicationId, javaScriptKey, masterKey) { + CoreManager.set('APPLICATION_ID', applicationId); + CoreManager.set('JAVASCRIPT_KEY', javaScriptKey); + CoreManager.set('MASTER_KEY', masterKey); + CoreManager.set('USE_MASTER_KEY', false); + } +}; + +/** These legacy setters may eventually be deprecated **/ +Object.defineProperty(Parse, 'applicationId', { + get() { + return CoreManager.get('APPLICATION_ID'); + }, + set(value) { + CoreManager.set('APPLICATION_ID', value); + } +}); +Object.defineProperty(Parse, 'javaScriptKey', { + get() { + return CoreManager.get('JAVASCRIPT_KEY'); + }, + set(value) { + CoreManager.set('JAVASCRIPT_KEY', value); + } +}); +Object.defineProperty(Parse, 'masterKey', { + get() { + return CoreManager.get('MASTER_KEY'); + }, + set(value) { + CoreManager.set('MASTER_KEY', value); + } +}); +Object.defineProperty(Parse, 'serverURL', { + get() { + return CoreManager.get('SERVER_URL'); + }, + set(value) { + CoreManager.set('SERVER_URL', value); + } +}); +Object.defineProperty(Parse, 'liveQueryServerURL', { + get() { + return CoreManager.get('LIVEQUERY_SERVER_URL'); + }, + set(value) { + CoreManager.set('LIVEQUERY_SERVER_URL', value); + } +}); +/** End setters **/ + +Parse.ACL = require('./ParseACL').default; +Parse.Analytics = require('./Analytics'); +Parse.Cloud = require('./Cloud'); +Parse.CoreManager = require('./CoreManager'); +Parse.Config = require('./ParseConfig').default; +Parse.Error = require('./ParseError').default; +Parse.FacebookUtils = require('./FacebookUtils').default; +Parse.File = require('./ParseFile').default; +Parse.GeoPoint = require('./ParseGeoPoint').default; +Parse.Installation = require('./ParseInstallation').default; +Parse.Object = require('./ParseObject').default; +Parse.Op = { + Set: ParseOp.SetOp, + Unset: ParseOp.UnsetOp, + Increment: ParseOp.IncrementOp, + Add: ParseOp.AddOp, + Remove: ParseOp.RemoveOp, + AddUnique: ParseOp.AddUniqueOp, + Relation: ParseOp.RelationOp +}; +Parse.Promise = require('./ParsePromise').default; +Parse.Push = require('./Push'); +Parse.Query = require('./ParseQuery').default; +Parse.Relation = require('./ParseRelation').default; +Parse.Role = require('./ParseRole').default; +Parse.Session = require('./ParseSession').default; +Parse.Storage = require('./Storage'); +Parse.User = require('./ParseUser').default; +Parse.LiveQuery = require('./ParseLiveQuery').default; +Parse.LiveQueryClient = require('./LiveQueryClient').default; + +Parse._request = function (...args) { + return CoreManager.getRESTController().request.apply(null, args); +}; +Parse._ajax = function (...args) { + return CoreManager.getRESTController().ajax.apply(null, args); +}; +// We attempt to match the signatures of the legacy versions of these methods +Parse._decode = function (_, value) { + return decode(value); +}; +Parse._encode = function (value, _, disallowObjects) { + return encode(value, disallowObjects); +}; +Parse._getInstallationId = function () { + return CoreManager.getInstallationController().currentInstallationId(); +}; + +CoreManager.setInstallationController(InstallationController); +CoreManager.setRESTController(RESTController); + +// For legacy requires, of the form `var Parse = require('parse').Parse` +Parse.Parse = Parse; + +module.exports = Parse; \ No newline at end of file diff --git a/lib/react-native/ParseACL.js b/lib/react-native/ParseACL.js new file mode 100644 index 000000000..2c28c6fa6 --- /dev/null +++ b/lib/react-native/ParseACL.js @@ -0,0 +1,325 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + */ + +import ParseRole from './ParseRole'; +import ParseUser from './ParseUser'; + +var PUBLIC_KEY = '*'; + +/** + * Creates a new ACL. + * If no argument is given, the ACL has no permissions for anyone. + * If the argument is a Parse.User, the ACL will have read and write + * permission for only that user. + * If the argument is any other JSON object, that object will be interpretted + * as a serialized ACL created with toJSON(). + * @class Parse.ACL + * @constructor + * + *
An ACL, or Access Control List can be added to any
+ * Parse.Object
to restrict access to only a subset of users
+ * of your application.
Parse.Error
.
+ * @param {String} message A detailed description of the error.
+ */
+export default class ParseError {
+ constructor(code, message) {
+ this.code = code;
+ this.message = message;
+ }
+}
+
+/**
+ * Error code indicating some error other than those enumerated here.
+ * @property OTHER_CAUSE
+ * @static
+ * @final
+ */
+ParseError.OTHER_CAUSE = -1;
+
+/**
+ * Error code indicating that something has gone wrong with the server.
+ * If you get this error code, it is Parse's fault. Contact us at
+ * https://parse.com/help
+ * @property INTERNAL_SERVER_ERROR
+ * @static
+ * @final
+ */
+ParseError.INTERNAL_SERVER_ERROR = 1;
+
+/**
+ * Error code indicating the connection to the Parse servers failed.
+ * @property CONNECTION_FAILED
+ * @static
+ * @final
+ */
+ParseError.CONNECTION_FAILED = 100;
+
+/**
+ * Error code indicating the specified object doesn't exist.
+ * @property OBJECT_NOT_FOUND
+ * @static
+ * @final
+ */
+ParseError.OBJECT_NOT_FOUND = 101;
+
+/**
+ * Error code indicating you tried to query with a datatype that doesn't
+ * support it, like exact matching an array or object.
+ * @property INVALID_QUERY
+ * @static
+ * @final
+ */
+ParseError.INVALID_QUERY = 102;
+
+/**
+ * Error code indicating a missing or invalid classname. Classnames are
+ * case-sensitive. They must start with a letter, and a-zA-Z0-9_ are the
+ * only valid characters.
+ * @property INVALID_CLASS_NAME
+ * @static
+ * @final
+ */
+ParseError.INVALID_CLASS_NAME = 103;
+
+/**
+ * Error code indicating an unspecified object id.
+ * @property MISSING_OBJECT_ID
+ * @static
+ * @final
+ */
+ParseError.MISSING_OBJECT_ID = 104;
+
+/**
+ * Error code indicating an invalid key name. Keys are case-sensitive. They
+ * must start with a letter, and a-zA-Z0-9_ are the only valid characters.
+ * @property INVALID_KEY_NAME
+ * @static
+ * @final
+ */
+ParseError.INVALID_KEY_NAME = 105;
+
+/**
+ * Error code indicating a malformed pointer. You should not see this unless
+ * you have been mucking about changing internal Parse code.
+ * @property INVALID_POINTER
+ * @static
+ * @final
+ */
+ParseError.INVALID_POINTER = 106;
+
+/**
+ * Error code indicating that badly formed JSON was received upstream. This
+ * either indicates you have done something unusual with modifying how
+ * things encode to JSON, or the network is failing badly.
+ * @property INVALID_JSON
+ * @static
+ * @final
+ */
+ParseError.INVALID_JSON = 107;
+
+/**
+ * Error code indicating that the feature you tried to access is only
+ * available internally for testing purposes.
+ * @property COMMAND_UNAVAILABLE
+ * @static
+ * @final
+ */
+ParseError.COMMAND_UNAVAILABLE = 108;
+
+/**
+ * You must call Parse.initialize before using the Parse library.
+ * @property NOT_INITIALIZED
+ * @static
+ * @final
+ */
+ParseError.NOT_INITIALIZED = 109;
+
+/**
+ * Error code indicating that a field was set to an inconsistent type.
+ * @property INCORRECT_TYPE
+ * @static
+ * @final
+ */
+ParseError.INCORRECT_TYPE = 111;
+
+/**
+ * Error code indicating an invalid channel name. A channel name is either
+ * an empty string (the broadcast channel) or contains only a-zA-Z0-9_
+ * characters and starts with a letter.
+ * @property INVALID_CHANNEL_NAME
+ * @static
+ * @final
+ */
+ParseError.INVALID_CHANNEL_NAME = 112;
+
+/**
+ * Error code indicating that push is misconfigured.
+ * @property PUSH_MISCONFIGURED
+ * @static
+ * @final
+ */
+ParseError.PUSH_MISCONFIGURED = 115;
+
+/**
+ * Error code indicating that the object is too large.
+ * @property OBJECT_TOO_LARGE
+ * @static
+ * @final
+ */
+ParseError.OBJECT_TOO_LARGE = 116;
+
+/**
+ * Error code indicating that the operation isn't allowed for clients.
+ * @property OPERATION_FORBIDDEN
+ * @static
+ * @final
+ */
+ParseError.OPERATION_FORBIDDEN = 119;
+
+/**
+ * Error code indicating the result was not found in the cache.
+ * @property CACHE_MISS
+ * @static
+ * @final
+ */
+ParseError.CACHE_MISS = 120;
+
+/**
+ * Error code indicating that an invalid key was used in a nested
+ * JSONObject.
+ * @property INVALID_NESTED_KEY
+ * @static
+ * @final
+ */
+ParseError.INVALID_NESTED_KEY = 121;
+
+/**
+ * Error code indicating that an invalid filename was used for ParseFile.
+ * A valid file name contains only a-zA-Z0-9_. characters and is between 1
+ * and 128 characters.
+ * @property INVALID_FILE_NAME
+ * @static
+ * @final
+ */
+ParseError.INVALID_FILE_NAME = 122;
+
+/**
+ * Error code indicating an invalid ACL was provided.
+ * @property INVALID_ACL
+ * @static
+ * @final
+ */
+ParseError.INVALID_ACL = 123;
+
+/**
+ * Error code indicating that the request timed out on the server. Typically
+ * this indicates that the request is too expensive to run.
+ * @property TIMEOUT
+ * @static
+ * @final
+ */
+ParseError.TIMEOUT = 124;
+
+/**
+ * Error code indicating that the email address was invalid.
+ * @property INVALID_EMAIL_ADDRESS
+ * @static
+ * @final
+ */
+ParseError.INVALID_EMAIL_ADDRESS = 125;
+
+/**
+ * Error code indicating a missing content type.
+ * @property MISSING_CONTENT_TYPE
+ * @static
+ * @final
+ */
+ParseError.MISSING_CONTENT_TYPE = 126;
+
+/**
+ * Error code indicating a missing content length.
+ * @property MISSING_CONTENT_LENGTH
+ * @static
+ * @final
+ */
+ParseError.MISSING_CONTENT_LENGTH = 127;
+
+/**
+ * Error code indicating an invalid content length.
+ * @property INVALID_CONTENT_LENGTH
+ * @static
+ * @final
+ */
+ParseError.INVALID_CONTENT_LENGTH = 128;
+
+/**
+ * Error code indicating a file that was too large.
+ * @property FILE_TOO_LARGE
+ * @static
+ * @final
+ */
+ParseError.FILE_TOO_LARGE = 129;
+
+/**
+ * Error code indicating an error saving a file.
+ * @property FILE_SAVE_ERROR
+ * @static
+ * @final
+ */
+ParseError.FILE_SAVE_ERROR = 130;
+
+/**
+ * Error code indicating that a unique field was given a value that is
+ * already taken.
+ * @property DUPLICATE_VALUE
+ * @static
+ * @final
+ */
+ParseError.DUPLICATE_VALUE = 137;
+
+/**
+ * Error code indicating that a role's name is invalid.
+ * @property INVALID_ROLE_NAME
+ * @static
+ * @final
+ */
+ParseError.INVALID_ROLE_NAME = 139;
+
+/**
+ * Error code indicating that an application quota was exceeded. Upgrade to
+ * resolve.
+ * @property EXCEEDED_QUOTA
+ * @static
+ * @final
+ */
+ParseError.EXCEEDED_QUOTA = 140;
+
+/**
+ * Error code indicating that a Cloud Code script failed.
+ * @property SCRIPT_FAILED
+ * @static
+ * @final
+ */
+ParseError.SCRIPT_FAILED = 141;
+
+/**
+ * Error code indicating that a Cloud Code validation failed.
+ * @property VALIDATION_ERROR
+ * @static
+ * @final
+ */
+ParseError.VALIDATION_ERROR = 142;
+
+/**
+ * Error code indicating that invalid image data was provided.
+ * @property INVALID_IMAGE_DATA
+ * @static
+ * @final
+ */
+ParseError.INVALID_IMAGE_DATA = 143;
+
+/**
+ * Error code indicating an unsaved file.
+ * @property UNSAVED_FILE_ERROR
+ * @static
+ * @final
+ */
+ParseError.UNSAVED_FILE_ERROR = 151;
+
+/**
+ * Error code indicating an invalid push time.
+ * @property INVALID_PUSH_TIME_ERROR
+ * @static
+ * @final
+ */
+ParseError.INVALID_PUSH_TIME_ERROR = 152;
+
+/**
+ * Error code indicating an error deleting a file.
+ * @property FILE_DELETE_ERROR
+ * @static
+ * @final
+ */
+ParseError.FILE_DELETE_ERROR = 153;
+
+/**
+ * Error code indicating that the application has exceeded its request
+ * limit.
+ * @property REQUEST_LIMIT_EXCEEDED
+ * @static
+ * @final
+ */
+ParseError.REQUEST_LIMIT_EXCEEDED = 155;
+
+/**
+ * Error code indicating an invalid event name.
+ * @property INVALID_EVENT_NAME
+ * @static
+ * @final
+ */
+ParseError.INVALID_EVENT_NAME = 160;
+
+/**
+ * Error code indicating that the username is missing or empty.
+ * @property USERNAME_MISSING
+ * @static
+ * @final
+ */
+ParseError.USERNAME_MISSING = 200;
+
+/**
+ * Error code indicating that the password is missing or empty.
+ * @property PASSWORD_MISSING
+ * @static
+ * @final
+ */
+ParseError.PASSWORD_MISSING = 201;
+
+/**
+ * Error code indicating that the username has already been taken.
+ * @property USERNAME_TAKEN
+ * @static
+ * @final
+ */
+ParseError.USERNAME_TAKEN = 202;
+
+/**
+ * Error code indicating that the email has already been taken.
+ * @property EMAIL_TAKEN
+ * @static
+ * @final
+ */
+ParseError.EMAIL_TAKEN = 203;
+
+/**
+ * Error code indicating that the email is missing, but must be specified.
+ * @property EMAIL_MISSING
+ * @static
+ * @final
+ */
+ParseError.EMAIL_MISSING = 204;
+
+/**
+ * Error code indicating that a user with the specified email was not found.
+ * @property EMAIL_NOT_FOUND
+ * @static
+ * @final
+ */
+ParseError.EMAIL_NOT_FOUND = 205;
+
+/**
+ * Error code indicating that a user object without a valid session could
+ * not be altered.
+ * @property SESSION_MISSING
+ * @static
+ * @final
+ */
+ParseError.SESSION_MISSING = 206;
+
+/**
+ * Error code indicating that a user can only be created through signup.
+ * @property MUST_CREATE_USER_THROUGH_SIGNUP
+ * @static
+ * @final
+ */
+ParseError.MUST_CREATE_USER_THROUGH_SIGNUP = 207;
+
+/**
+ * Error code indicating that an an account being linked is already linked
+ * to another user.
+ * @property ACCOUNT_ALREADY_LINKED
+ * @static
+ * @final
+ */
+ParseError.ACCOUNT_ALREADY_LINKED = 208;
+
+/**
+ * Error code indicating that the current session token is invalid.
+ * @property INVALID_SESSION_TOKEN
+ * @static
+ * @final
+ */
+ParseError.INVALID_SESSION_TOKEN = 209;
+
+/**
+ * Error code indicating that a user cannot be linked to an account because
+ * that account's id could not be found.
+ * @property LINKED_ID_MISSING
+ * @static
+ * @final
+ */
+ParseError.LINKED_ID_MISSING = 250;
+
+/**
+ * Error code indicating that a user with a linked (e.g. Facebook) account
+ * has an invalid session.
+ * @property INVALID_LINKED_SESSION
+ * @static
+ * @final
+ */
+ParseError.INVALID_LINKED_SESSION = 251;
+
+/**
+ * Error code indicating that a service being linked (e.g. Facebook or
+ * Twitter) is unsupported.
+ * @property UNSUPPORTED_SERVICE
+ * @static
+ * @final
+ */
+ParseError.UNSUPPORTED_SERVICE = 252;
+
+/**
+ * Error code indicating that there were multiple errors. Aggregate errors
+ * have an "errors" property, which is an array of error objects with more
+ * detail about each error that occurred.
+ * @property AGGREGATE_ERROR
+ * @static
+ * @final
+ */
+ParseError.AGGREGATE_ERROR = 600;
+
+/**
+ * Error code indicating the client was unable to read an input file.
+ * @property FILE_READ_ERROR
+ * @static
+ * @final
+ */
+ParseError.FILE_READ_ERROR = 601;
+
+/**
+ * Error code indicating a real error code is unavailable because
+ * we had to use an XDomainRequest object to allow CORS requests in
+ * Internet Explorer, which strips the body from HTTP responses that have
+ * a non-2XX status code.
+ * @property X_DOMAIN_REQUEST
+ * @static
+ * @final
+ */
+ParseError.X_DOMAIN_REQUEST = 602;
\ No newline at end of file
diff --git a/lib/react-native/ParseFile.js b/lib/react-native/ParseFile.js
new file mode 100644
index 000000000..0b1b448a8
--- /dev/null
+++ b/lib/react-native/ParseFile.js
@@ -0,0 +1,247 @@
+/**
+ * Copyright (c) 2015-present, Parse, LLC.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ *
+ *
+ */
+
+import CoreManager from './CoreManager';
+import ParsePromise from './ParsePromise';
+
+var dataUriRegexp = /^data:([a-zA-Z]*\/[a-zA-Z+.-]*);(charset=[a-zA-Z0-9\-\/\s]*,)?base64,/;
+
+function b64Digit(number) {
+ if (number < 26) {
+ return String.fromCharCode(65 + number);
+ }
+ if (number < 52) {
+ return String.fromCharCode(97 + (number - 26));
+ }
+ if (number < 62) {
+ return String.fromCharCode(48 + (number - 52));
+ }
+ if (number === 62) {
+ return '+';
+ }
+ if (number === 63) {
+ return '/';
+ }
+ throw new TypeError('Tried to encode large digit ' + number + ' in base64.');
+}
+
+/**
+ * A Parse.File is a local representation of a file that is saved to the Parse
+ * cloud.
+ * @class Parse.File
+ * @constructor
+ * @param name {String} The file's name. This will be prefixed by a unique
+ * value once the file has finished saving. The file name must begin with
+ * an alphanumeric character, and consist of alphanumeric characters,
+ * periods, spaces, underscores, or dashes.
+ * @param data {Array} The data for the file, as either:
+ * 1. an Array of byte value Numbers, or
+ * 2. an Object like { base64: "..." } with a base64-encoded String.
+ * 3. a File object selected with a file upload control. (3) only works
+ * in Firefox 3.6+, Safari 6.0.2+, Chrome 7+, and IE 10+.
+ * For example:+ * var fileUploadControl = $("#profilePhotoFileUpload")[0]; + * if (fileUploadControl.files.length > 0) { + * var file = fileUploadControl.files[0]; + * var name = "photo.jpg"; + * var parseFile = new Parse.File(name, file); + * parseFile.save().then(function() { + * // The file has been saved to Parse. + * }, function(error) { + * // The file either could not be read, or could not be saved to Parse. + * }); + * }+ * @param type {String} Optional Content-Type header to use for the file. If + * this is omitted, the content type will be inferred from the name's + * extension. + */ +export default class ParseFile { + + constructor(name, data, type) { + var specifiedType = type || ''; + + this._name = name; + + if (data !== undefined) { + if (Array.isArray(data)) { + this._source = { + format: 'base64', + base64: ParseFile.encodeBase64(data), + type: specifiedType + }; + } else if (typeof File !== 'undefined' && data instanceof File) { + this._source = { + format: 'file', + file: data, + type: specifiedType + }; + } else if (data && typeof data.base64 === 'string') { + const base64 = data.base64; + var commaIndex = base64.indexOf(','); + + if (commaIndex !== -1) { + var matches = dataUriRegexp.exec(base64.slice(0, commaIndex + 1)); + // if data URI with type and charset, there will be 4 matches. + this._source = { + format: 'base64', + base64: base64.slice(commaIndex + 1), + type: matches[1] + }; + } else { + this._source = { + format: 'base64', + base64: base64, + type: specifiedType + }; + } + } else { + throw new TypeError('Cannot create a Parse.File with that data.'); + } + } + } + + /** + * Gets the name of the file. Before save is called, this is the filename + * given by the user. After save is called, that name gets prefixed with a + * unique identifier. + * @method name + * @return {String} + */ + name() { + return this._name; + } + + /** + * Gets the url of the file. It is only available after you save the file or + * after you get the file from a Parse.Object. + * @method url + * @param {Object} options An object to specify url options + * @return {String} + */ + url(options) { + options = options || {}; + if (!this._url) { + return; + } + if (options.forceSecure) { + return this._url.replace(/^http:\/\//i, 'https://'); + } else { + return this._url; + } + } + + /** + * Saves the file to the Parse cloud. + * @method save + * @param {Object} options A Backbone-style options object. + * @return {Parse.Promise} Promise that is resolved when the save finishes. + */ + save(options) { + options = options || {}; + var controller = CoreManager.getFileController(); + if (!this._previousSave) { + if (this._source.format === 'file') { + this._previousSave = controller.saveFile(this._name, this._source).then(res => { + this._name = res.name; + this._url = res.url; + return this; + }); + } else { + this._previousSave = controller.saveBase64(this._name, this._source).then(res => { + this._name = res.name; + this._url = res.url; + return this; + }); + } + } + if (this._previousSave) { + return this._previousSave._thenRunCallbacks(options); + } + } + + toJSON() { + return { + __type: 'File', + name: this._name, + url: this._url + }; + } + + equals(other) { + if (this === other) { + return true; + } + // Unsaved Files are never equal, since they will be saved to different URLs + return other instanceof ParseFile && this.name() === other.name() && this.url() === other.url() && typeof this.url() !== 'undefined'; + } + + static fromJSON(obj) { + if (obj.__type !== 'File') { + throw new TypeError('JSON object does not represent a ParseFile'); + } + var file = new ParseFile(obj.name); + file._url = obj.url; + return file; + } + + static encodeBase64(bytes) { + var chunks = []; + chunks.length = Math.ceil(bytes.length / 3); + for (var i = 0; i < chunks.length; i++) { + var b1 = bytes[i * 3]; + var b2 = bytes[i * 3 + 1] || 0; + var b3 = bytes[i * 3 + 2] || 0; + + var has2 = i * 3 + 1 < bytes.length; + var has3 = i * 3 + 2 < bytes.length; + + chunks[i] = [b64Digit(b1 >> 2 & 0x3F), b64Digit(b1 << 4 & 0x30 | b2 >> 4 & 0x0F), has2 ? b64Digit(b2 << 2 & 0x3C | b3 >> 6 & 0x03) : '=', has3 ? b64Digit(b3 & 0x3F) : '='].join(''); + } + + return chunks.join(''); + } +} + +var DefaultController = { + saveFile: function (name, source) { + if (source.format !== 'file') { + throw new Error('saveFile can only be used with File-type sources.'); + } + // To directly upload a File, we use a REST-style AJAX request + var headers = { + 'X-Parse-Application-ID': CoreManager.get('APPLICATION_ID'), + 'X-Parse-JavaScript-Key': CoreManager.get('JAVASCRIPT_KEY'), + 'Content-Type': source.type || (source.file ? source.file.type : null) + }; + var url = CoreManager.get('SERVER_URL'); + if (url[url.length - 1] !== '/') { + url += '/'; + } + url += 'files/' + name; + return CoreManager.getRESTController().ajax('POST', url, source.file, headers); + }, + + saveBase64: function (name, source) { + if (source.format !== 'base64') { + throw new Error('saveBase64 can only be used with Base64-type sources.'); + } + var data = { + base64: source.base64 + }; + if (source.type) { + data._ContentType = source.type; + } + + return CoreManager.getRESTController().request('POST', 'files/' + name, data); + } +}; + +CoreManager.setFileController(DefaultController); \ No newline at end of file diff --git a/lib/react-native/ParseGeoPoint.js b/lib/react-native/ParseGeoPoint.js new file mode 100644 index 000000000..e6dcddf43 --- /dev/null +++ b/lib/react-native/ParseGeoPoint.js @@ -0,0 +1,186 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + */ + +import ParsePromise from './ParsePromise'; + +/** + * Creates a new GeoPoint with any of the following forms:
+ * new GeoPoint(otherGeoPoint) + * new GeoPoint(30, 30) + * new GeoPoint([30, 30]) + * new GeoPoint({latitude: 30, longitude: 30}) + * new GeoPoint() // defaults to (0, 0) + *+ * @class Parse.GeoPoint + * @constructor + * + *
Represents a latitude / longitude point that may be associated + * with a key in a ParseObject or used as a reference point for geo queries. + * This allows proximity-based queries on the key.
+ * + *Only one key in a class may contain a GeoPoint.
+ * + *Example:
+ * var point = new Parse.GeoPoint(30.0, -20.0); + * var object = new Parse.Object("PlaceObject"); + * object.set("location", point); + * object.save();+ */ +export default class ParseGeoPoint { + + constructor(arg1, arg2) { + if (Array.isArray(arg1)) { + ParseGeoPoint._validate(arg1[0], arg1[1]); + this._latitude = arg1[0]; + this._longitude = arg1[1]; + } else if (typeof arg1 === 'object') { + ParseGeoPoint._validate(arg1.latitude, arg1.longitude); + this._latitude = arg1.latitude; + this._longitude = arg1.longitude; + } else if (typeof arg1 === 'number' && typeof arg2 === 'number') { + ParseGeoPoint._validate(arg1, arg2); + this._latitude = arg1; + this._longitude = arg2; + } else { + this._latitude = 0; + this._longitude = 0; + } + } + + /** + * North-south portion of the coordinate, in range [-90, 90]. + * Throws an exception if set out of range in a modern browser. + * @property latitude + * @type Number + */ + get latitude() { + return this._latitude; + } + + set latitude(val) { + ParseGeoPoint._validate(val, this.longitude); + this._latitude = val; + } + + /** + * East-west portion of the coordinate, in range [-180, 180]. + * Throws if set out of range in a modern browser. + * @property longitude + * @type Number + */ + get longitude() { + return this._longitude; + } + + set longitude(val) { + ParseGeoPoint._validate(this.latitude, val); + this._longitude = val; + } + + /** + * Returns a JSON representation of the GeoPoint, suitable for Parse. + * @method toJSON + * @return {Object} + */ + toJSON() { + ParseGeoPoint._validate(this._latitude, this._longitude); + return { + __type: 'GeoPoint', + latitude: this._latitude, + longitude: this._longitude + }; + } + + equals(other) { + return other instanceof ParseGeoPoint && this.latitude === other.latitude && this.longitude === other.longitude; + } + + /** + * Returns the distance from this GeoPoint to another in radians. + * @method radiansTo + * @param {Parse.GeoPoint} point the other Parse.GeoPoint. + * @return {Number} + */ + radiansTo(point) { + var d2r = Math.PI / 180.0; + var lat1rad = this.latitude * d2r; + var long1rad = this.longitude * d2r; + var lat2rad = point.latitude * d2r; + var long2rad = point.longitude * d2r; + + var sinDeltaLatDiv2 = Math.sin((lat1rad - lat2rad) / 2); + var sinDeltaLongDiv2 = Math.sin((long1rad - long2rad) / 2); + // Square of half the straight line chord distance between both points. + var a = sinDeltaLatDiv2 * sinDeltaLatDiv2 + Math.cos(lat1rad) * Math.cos(lat2rad) * sinDeltaLongDiv2 * sinDeltaLongDiv2; + a = Math.min(1.0, a); + return 2 * Math.asin(Math.sqrt(a)); + } + + /** + * Returns the distance from this GeoPoint to another in kilometers. + * @method kilometersTo + * @param {Parse.GeoPoint} point the other Parse.GeoPoint. + * @return {Number} + */ + kilometersTo(point) { + return this.radiansTo(point) * 6371.0; + } + + /** + * Returns the distance from this GeoPoint to another in miles. + * @method milesTo + * @param {Parse.GeoPoint} point the other Parse.GeoPoint. + * @return {Number} + */ + milesTo(point) { + return this.radiansTo(point) * 3958.8; + } + + /** + * Throws an exception if the given lat-long is out of bounds. + */ + static _validate(latitude, longitude) { + if (latitude !== latitude || longitude !== longitude) { + throw new TypeError('GeoPoint latitude and longitude must be valid numbers'); + } + if (latitude < -90.0) { + throw new TypeError('GeoPoint latitude out of bounds: ' + latitude + ' < -90.0.'); + } + if (latitude > 90.0) { + throw new TypeError('GeoPoint latitude out of bounds: ' + latitude + ' > 90.0.'); + } + if (longitude < -180.0) { + throw new TypeError('GeoPoint longitude out of bounds: ' + longitude + ' < -180.0.'); + } + if (longitude > 180.0) { + throw new TypeError('GeoPoint longitude out of bounds: ' + longitude + ' > 180.0.'); + } + } + + /** + * Creates a GeoPoint with the user's current location, if available. + * Calls options.success with a new GeoPoint instance or calls options.error. + * @method current + * @param {Object} options An object with success and error callbacks. + * @static + */ + static current(options) { + var promise = new ParsePromise(); + navigator.geolocation.getCurrentPosition(function (location) { + promise.resolve(new ParseGeoPoint(location.coords.latitude, location.coords.longitude)); + }, function (error) { + promise.reject(error); + }); + + return promise._thenRunCallbacks(options); + } +} \ No newline at end of file diff --git a/lib/react-native/ParseHooks.js b/lib/react-native/ParseHooks.js new file mode 100644 index 000000000..e079fcd51 --- /dev/null +++ b/lib/react-native/ParseHooks.js @@ -0,0 +1,125 @@ +import CoreManager from './CoreManager'; +import decode from './decode'; +import encode from './encode'; +import ParseError from './ParseError'; +import ParsePromise from './ParsePromise'; + +export function getFunctions() { + return CoreManager.getHooksController().get("functions"); +} + +export function getTriggers() { + return CoreManager.getHooksController().get("triggers"); +} + +export function getFunction(name) { + return CoreManager.getHooksController().get("functions", name); +} + +export function getTrigger(className, triggerName) { + return CoreManager.getHooksController().get("triggers", className, triggerName); +} + +export function createFunction(functionName, url) { + return create({ functionName: functionName, url: url }); +} + +export function createTrigger(className, triggerName, url) { + return create({ className: className, triggerName: triggerName, url: url }); +} + +export function create(hook) { + return CoreManager.getHooksController().create(hook); +} + +export function updateFunction(functionName, url) { + return update({ functionName: functionName, url: url }); +} + +export function updateTrigger(className, triggerName, url) { + return update({ className: className, triggerName: triggerName, url: url }); +} + +export function update(hook) { + return CoreManager.getHooksController().update(hook); +} + +export function removeFunction(functionName) { + return remove({ functionName: functionName }); +} + +export function removeTrigger(className, triggerName) { + return remove({ className: className, triggerName: triggerName }); +} + +export function remove(hook) { + return CoreManager.getHooksController().remove(hook); +} + +var DefaultController = { + + get(type, functionName, triggerName) { + var url = "/hooks/" + type; + if (functionName) { + url += "/" + functionName; + if (triggerName) { + url += "/" + triggerName; + } + } + return this.sendRequest("GET", url); + }, + + create(hook) { + var url; + if (hook.functionName && hook.url) { + url = "/hooks/functions"; + } else if (hook.className && hook.triggerName && hook.url) { + url = "/hooks/triggers"; + } else { + return Promise.reject({ error: 'invalid hook declaration', code: 143 }); + } + return this.sendRequest("POST", url, hook); + }, + + remove(hook) { + var url; + if (hook.functionName) { + url = "/hooks/functions/" + hook.functionName; + delete hook.functionName; + } else if (hook.className && hook.triggerName) { + url = "/hooks/triggers/" + hook.className + "/" + hook.triggerName; + delete hook.className; + delete hook.triggerName; + } else { + return Promise.reject({ error: 'invalid hook declaration', code: 143 }); + } + return this.sendRequest("PUT", url, { "__op": "Delete" }); + }, + + update(hook) { + var url; + if (hook.functionName && hook.url) { + url = "/hooks/functions/" + hook.functionName; + delete hook.functionName; + } else if (hook.className && hook.triggerName && hook.url) { + url = "/hooks/triggers/" + hook.className + "/" + hook.triggerName; + delete hook.className; + delete hook.triggerName; + } else { + return Promise.reject({ error: 'invalid hook declaration', code: 143 }); + } + return this.sendRequest('PUT', url, hook); + }, + + sendRequest(method, url, body) { + return CoreManager.getRESTController().request(method, url, body, { useMasterKey: true }).then(res => { + var decoded = decode(res); + if (decoded) { + return ParsePromise.as(decoded); + } + return ParsePromise.error(new ParseError(ParseError.INVALID_JSON, 'The server returned an invalid response.')); + }); + } +}; + +CoreManager.setHooksController(DefaultController); \ No newline at end of file diff --git a/lib/react-native/ParseInstallation.js b/lib/react-native/ParseInstallation.js new file mode 100644 index 000000000..fd29a3f91 --- /dev/null +++ b/lib/react-native/ParseInstallation.js @@ -0,0 +1,25 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + */ + +import ParseObject from './ParseObject'; + +export default class Installation extends ParseObject { + constructor(attributes) { + super('_Installation'); + if (attributes && typeof attributes === 'object') { + if (!this.set(attributes || {})) { + throw new Error('Can\'t create an invalid Session'); + } + } + } +} + +ParseObject.registerSubclass('_Installation', Installation); \ No newline at end of file diff --git a/lib/react-native/ParseLiveQuery.js b/lib/react-native/ParseLiveQuery.js new file mode 100644 index 000000000..3ad072a72 --- /dev/null +++ b/lib/react-native/ParseLiveQuery.js @@ -0,0 +1,212 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + */ + +import EventEmitter from './EventEmitter'; +import LiveQueryClient from './LiveQueryClient'; +import CoreManager from './CoreManager'; +import ParsePromise from './ParsePromise'; + +function open() { + const LiveQueryController = CoreManager.getLiveQueryController(); + LiveQueryController.open(); +} + +function close() { + const LiveQueryController = CoreManager.getLiveQueryController(); + LiveQueryController.close(); +} + +/** + * + * We expose three events to help you monitor the status of the WebSocket connection: + * + *
Open - When we establish the WebSocket connection to the LiveQuery server, you'll get this event. + * + *
+ * Parse.LiveQuery.on('open', () => { + * + * });+ * + *
Close - When we lose the WebSocket connection to the LiveQuery server, you'll get this event. + * + *
+ * Parse.LiveQuery.on('close', () => { + * + * });+ * + *
Error - When some network error or LiveQuery server error happens, you'll get this event. + * + *
+ * Parse.LiveQuery.on('error', (error) => { + * + * });+ * + * @class Parse.LiveQuery + * @static + * + */ +let LiveQuery = new EventEmitter(); + +/** + * After open is called, the LiveQuery will try to send a connect request + * to the LiveQuery server. + * + * @method open + */ +LiveQuery.open = open; + +/** + * When you're done using LiveQuery, you can call Parse.LiveQuery.close(). + * This function will close the WebSocket connection to the LiveQuery server, + * cancel the auto reconnect, and unsubscribe all subscriptions based on it. + * If you call query.subscribe() after this, we'll create a new WebSocket + * connection to the LiveQuery server. + * + * @method close + */ + +LiveQuery.close = close; +// Register a default onError callback to make sure we do not crash on error +LiveQuery.on('error', () => {}); + +export default LiveQuery; + +function getSessionToken() { + const controller = CoreManager.getUserController(); + return controller.currentUserAsync().then(currentUser => { + return currentUser ? currentUser.getSessionToken() : undefined; + }); +} + +function getLiveQueryClient() { + return CoreManager.getLiveQueryController().getDefaultLiveQueryClient(); +} + +let defaultLiveQueryClient; +const DefaultLiveQueryController = { + setDefaultLiveQueryClient(liveQueryClient) { + defaultLiveQueryClient = liveQueryClient; + }, + getDefaultLiveQueryClient() { + if (defaultLiveQueryClient) { + return ParsePromise.as(defaultLiveQueryClient); + } + + return getSessionToken().then(sessionToken => { + let liveQueryServerURL = CoreManager.get('LIVEQUERY_SERVER_URL'); + + if (liveQueryServerURL && liveQueryServerURL.indexOf('ws') !== 0) { + throw new Error('You need to set a proper Parse LiveQuery server url before using LiveQueryClient'); + } + + // If we can not find Parse.liveQueryServerURL, we try to extract it from Parse.serverURL + if (!liveQueryServerURL) { + const tempServerURL = CoreManager.get('SERVER_URL'); + let protocol = 'ws://'; + // If Parse is being served over SSL/HTTPS, ensure LiveQuery Server uses 'wss://' prefix + if (tempServerURL.indexOf('https') === 0) { + protocol = 'wss://'; + } + const host = tempServerURL.replace(/^https?:\/\//, ''); + liveQueryServerURL = protocol + host; + CoreManager.set('LIVEQUERY_SERVER_URL', liveQueryServerURL); + } + + const applicationId = CoreManager.get('APPLICATION_ID'); + const javascriptKey = CoreManager.get('JAVASCRIPT_KEY'); + const masterKey = CoreManager.get('MASTER_KEY'); + // Get currentUser sessionToken if possible + defaultLiveQueryClient = new LiveQueryClient({ + applicationId, + serverURL: liveQueryServerURL, + javascriptKey, + masterKey, + sessionToken + }); + // Register a default onError callback to make sure we do not crash on error + // Cannot create these events on a nested way because of EventEmiiter from React Native + defaultLiveQueryClient.on('error', error => { + LiveQuery.emit('error', error); + }); + defaultLiveQueryClient.on('open', () => { + LiveQuery.emit('open'); + }); + defaultLiveQueryClient.on('close', () => { + LiveQuery.emit('close'); + }); + + return defaultLiveQueryClient; + }); + }, + open() { + getLiveQueryClient().then(liveQueryClient => { + this.resolve(liveQueryClient.open()); + }); + }, + close() { + getLiveQueryClient().then(liveQueryClient => { + this.resolve(liveQueryClient.close()); + }); + }, + subscribe(query) { + let subscriptionWrap = new EventEmitter(); + + getLiveQueryClient().then(liveQueryClient => { + if (liveQueryClient.shouldOpen()) { + liveQueryClient.open(); + } + let promiseSessionToken = getSessionToken(); + // new event emitter + return promiseSessionToken.then(sessionToken => { + + let subscription = liveQueryClient.subscribe(query, sessionToken); + // enter, leave create, etc + + subscriptionWrap.id = subscription.id; + subscriptionWrap.query = subscription.query; + subscriptionWrap.sessionToken = subscription.sessionToken; + subscriptionWrap.unsubscribe = subscription.unsubscribe; + // Cannot create these events on a nested way because of EventEmiiter from React Native + subscription.on('open', () => { + subscriptionWrap.emit('open'); + }); + subscription.on('create', object => { + subscriptionWrap.emit('create', object); + }); + subscription.on('update', object => { + subscriptionWrap.emit('update', object); + }); + subscription.on('enter', object => { + subscriptionWrap.emit('enter', object); + }); + subscription.on('leave', object => { + subscriptionWrap.emit('leave', object); + }); + subscription.on('delete', object => { + subscriptionWrap.emit('delete', object); + }); + + this.resolve(); + }); + }); + return subscriptionWrap; + }, + unsubscribe(subscription) { + getLiveQueryClient().then(liveQueryClient => { + this.resolve(liveQueryClient.unsubscribe(subscription)); + }); + }, + _clearCachedDefaultClient() { + defaultLiveQueryClient = null; + } +}; + +CoreManager.setLiveQueryController(DefaultLiveQueryController); \ No newline at end of file diff --git a/lib/react-native/ParseObject.js b/lib/react-native/ParseObject.js new file mode 100644 index 000000000..e92c14e18 --- /dev/null +++ b/lib/react-native/ParseObject.js @@ -0,0 +1,1742 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + */ + +import CoreManager from './CoreManager'; +import canBeSerialized from './canBeSerialized'; +import decode from './decode'; +import encode from './encode'; +import equals from './equals'; +import escape from './escape'; +import ParseACL from './ParseACL'; +import parseDate from './parseDate'; +import ParseError from './ParseError'; +import ParseFile from './ParseFile'; +import { opFromJSON, Op, SetOp, UnsetOp, IncrementOp, AddOp, AddUniqueOp, RemoveOp, RelationOp } from './ParseOp'; +import ParsePromise from './ParsePromise'; +import ParseQuery from './ParseQuery'; +import ParseRelation from './ParseRelation'; +import * as SingleInstanceStateController from './SingleInstanceStateController'; +import unique from './unique'; +import * as UniqueInstanceStateController from './UniqueInstanceStateController'; +import unsavedChildren from './unsavedChildren'; + +// Mapping of class names to constructors, so we can populate objects from the +// server with appropriate subclasses of ParseObject +var classMap = {}; + +// Global counter for generating unique local Ids +var localCount = 0; +// Global counter for generating unique Ids for non-single-instance objects +var objectCount = 0; +// On web clients, objects are single-instance: any two objects with the same Id +// will have the same attributes. However, this may be dangerous default +// behavior in a server scenario +var singleInstance = !CoreManager.get('IS_NODE'); +if (singleInstance) { + CoreManager.setObjectStateController(SingleInstanceStateController); +} else { + CoreManager.setObjectStateController(UniqueInstanceStateController); +} + +function getServerUrlPath() { + var serverUrl = CoreManager.get('SERVER_URL'); + if (serverUrl[serverUrl.length - 1] !== '/') { + serverUrl += '/'; + } + var url = serverUrl.replace(/https?:\/\//, ''); + return url.substr(url.indexOf('/')); +} + +/** + * Creates a new model with defined attributes. + * + *
You won't normally call this method directly. It is recommended that
+ * you use a subclass of Parse.Object
instead, created by calling
+ * extend
.
However, if you don't want to use a subclass, or aren't sure which + * subclass is appropriate, you can use this form:
+ * var object = new Parse.Object("ClassName"); + *+ * That is basically equivalent to:
+ * var MyClass = Parse.Object.extend("ClassName"); + * var object = new MyClass(); + *+ * + * @class Parse.Object + * @constructor + * @param {String} className The class name for the object + * @param {Object} attributes The initial set of data to store in the object. + * @param {Object} options The options for this object instance. + */ +export default class ParseObject { + /** + * The ID of this object, unique within its class. + * @property id + * @type String + */ + constructor(className, attributes, options) { + // Enable legacy initializers + if (typeof this.initialize === 'function') { + this.initialize.apply(this, arguments); + } + + var toSet = null; + this._objCount = objectCount++; + if (typeof className === 'string') { + this.className = className; + if (attributes && typeof attributes === 'object') { + toSet = attributes; + } + } else if (className && typeof className === 'object') { + this.className = className.className; + toSet = {}; + for (var attr in className) { + if (attr !== 'className') { + toSet[attr] = className[attr]; + } + } + if (attributes && typeof attributes === 'object') { + options = attributes; + } + } + if (toSet && !this.set(toSet, options)) { + throw new Error('Can\'t create an invalid Parse Object'); + } + } + + /** Prototype getters / setters **/ + + get attributes() { + let stateController = CoreManager.getObjectStateController(); + return Object.freeze(stateController.estimateAttributes(this._getStateIdentifier())); + } + + /** + * The first time this object was saved on the server. + * @property createdAt + * @type Date + */ + get createdAt() { + return this._getServerData().createdAt; + } + + /** + * The last time this object was updated on the server. + * @property updatedAt + * @type Date + */ + get updatedAt() { + return this._getServerData().updatedAt; + } + + /** Private methods **/ + + /** + * Returns a local or server Id used uniquely identify this object + */ + _getId() { + if (typeof this.id === 'string') { + return this.id; + } + if (typeof this._localId === 'string') { + return this._localId; + } + var localId = 'local' + String(localCount++); + this._localId = localId; + return localId; + } + + /** + * Returns a unique identifier used to pull data from the State Controller. + */ + _getStateIdentifier() { + if (singleInstance) { + let id = this.id; + if (!id) { + id = this._getId(); + } + return { + id: id, + className: this.className + }; + } else { + return this; + } + } + + _getServerData() { + let stateController = CoreManager.getObjectStateController(); + return stateController.getServerData(this._getStateIdentifier()); + } + + _clearServerData() { + var serverData = this._getServerData(); + var unset = {}; + for (var attr in serverData) { + unset[attr] = undefined; + } + let stateController = CoreManager.getObjectStateController(); + stateController.setServerData(this._getStateIdentifier(), unset); + } + + _getPendingOps() { + let stateController = CoreManager.getObjectStateController(); + return stateController.getPendingOps(this._getStateIdentifier()); + } + + _clearPendingOps() { + var pending = this._getPendingOps(); + var latest = pending[pending.length - 1]; + var keys = Object.keys(latest); + keys.forEach(key => { + delete latest[key]; + }); + } + + _getDirtyObjectAttributes() { + var attributes = this.attributes; + var stateController = CoreManager.getObjectStateController(); + var objectCache = stateController.getObjectCache(this._getStateIdentifier()); + var dirty = {}; + for (var attr in attributes) { + var val = attributes[attr]; + if (val && typeof val === 'object' && !(val instanceof ParseObject) && !(val instanceof ParseFile) && !(val instanceof ParseRelation)) { + // Due to the way browsers construct maps, the key order will not change + // unless the object is changed + try { + var json = encode(val, false, true); + var stringified = JSON.stringify(json); + if (objectCache[attr] !== stringified) { + dirty[attr] = val; + } + } catch (e) { + // Error occurred, possibly by a nested unsaved pointer in a mutable container + // No matter how it happened, it indicates a change in the attribute + dirty[attr] = val; + } + } + } + return dirty; + } + + _toFullJSON(seen) { + var json = this.toJSON(seen); + json.__type = 'Object'; + json.className = this.className; + return json; + } + + _getSaveJSON() { + var pending = this._getPendingOps(); + var dirtyObjects = this._getDirtyObjectAttributes(); + var json = {}; + + for (var attr in dirtyObjects) { + json[attr] = new SetOp(dirtyObjects[attr]).toJSON(); + } + for (attr in pending[0]) { + json[attr] = pending[0][attr].toJSON(); + } + return json; + } + + _getSaveParams() { + var method = this.id ? 'PUT' : 'POST'; + var body = this._getSaveJSON(); + var path = 'classes/' + this.className; + if (this.id) { + path += '/' + this.id; + } else if (this.className === '_User') { + path = 'users'; + } + return { + method, + body, + path + }; + } + + _finishFetch(serverData) { + if (!this.id && serverData.objectId) { + this.id = serverData.objectId; + } + let stateController = CoreManager.getObjectStateController(); + stateController.initializeState(this._getStateIdentifier()); + var decoded = {}; + for (var attr in serverData) { + if (attr === 'ACL') { + decoded[attr] = new ParseACL(serverData[attr]); + } else if (attr !== 'objectId') { + decoded[attr] = decode(serverData[attr]); + if (decoded[attr] instanceof ParseRelation) { + decoded[attr]._ensureParentAndKey(this, attr); + } + } + } + if (decoded.createdAt && typeof decoded.createdAt === 'string') { + decoded.createdAt = parseDate(decoded.createdAt); + } + if (decoded.updatedAt && typeof decoded.updatedAt === 'string') { + decoded.updatedAt = parseDate(decoded.updatedAt); + } + if (!decoded.updatedAt && decoded.createdAt) { + decoded.updatedAt = decoded.createdAt; + } + stateController.commitServerChanges(this._getStateIdentifier(), decoded); + } + + _setExisted(existed) { + let stateController = CoreManager.getObjectStateController(); + let state = stateController.getState(this._getStateIdentifier()); + if (state) { + state.existed = existed; + } + } + + _migrateId(serverId) { + if (this._localId && serverId) { + if (singleInstance) { + let stateController = CoreManager.getObjectStateController(); + let oldState = stateController.removeState(this._getStateIdentifier()); + this.id = serverId; + delete this._localId; + if (oldState) { + stateController.initializeState(this._getStateIdentifier(), oldState); + } + } else { + this.id = serverId; + delete this._localId; + } + } + } + + _handleSaveResponse(response, status) { + var changes = {}; + + var stateController = CoreManager.getObjectStateController(); + var pending = stateController.popPendingState(this._getStateIdentifier()); + for (var attr in pending) { + if (pending[attr] instanceof RelationOp) { + changes[attr] = pending[attr].applyTo(undefined, this, attr); + } else if (!(attr in response)) { + // Only SetOps and UnsetOps should not come back with results + changes[attr] = pending[attr].applyTo(undefined); + } + } + for (attr in response) { + if ((attr === 'createdAt' || attr === 'updatedAt') && typeof response[attr] === 'string') { + changes[attr] = parseDate(response[attr]); + } else if (attr === 'ACL') { + changes[attr] = new ParseACL(response[attr]); + } else if (attr !== 'objectId') { + changes[attr] = decode(response[attr]); + if (changes[attr] instanceof UnsetOp) { + changes[attr] = undefined; + } + } + } + if (changes.createdAt && !changes.updatedAt) { + changes.updatedAt = changes.createdAt; + } + + this._migrateId(response.objectId); + + if (status !== 201) { + this._setExisted(true); + } + + stateController.commitServerChanges(this._getStateIdentifier(), changes); + } + + _handleSaveError() { + this._getPendingOps(); + + let stateController = CoreManager.getObjectStateController(); + stateController.mergeFirstPendingState(this._getStateIdentifier()); + } + + /** Public methods **/ + + initialize() {} + // NOOP + + + /** + * Returns a JSON version of the object suitable for saving to Parse. + * @method toJSON + * @return {Object} + */ + toJSON(seen) { + var seenEntry = this.id ? this.className + ':' + this.id : this; + var seen = seen || [seenEntry]; + var json = {}; + var attrs = this.attributes; + for (var attr in attrs) { + if ((attr === 'createdAt' || attr === 'updatedAt') && attrs[attr].toJSON) { + json[attr] = attrs[attr].toJSON(); + } else { + json[attr] = encode(attrs[attr], false, false, seen); + } + } + var pending = this._getPendingOps(); + for (var attr in pending[0]) { + json[attr] = pending[0][attr].toJSON(); + } + + if (this.id) { + json.objectId = this.id; + } + return json; + } + + /** + * Determines whether this ParseObject is equal to another ParseObject + * @method equals + * @return {Boolean} + */ + equals(other) { + if (this === other) { + return true; + } + return other instanceof ParseObject && this.className === other.className && this.id === other.id && typeof this.id !== 'undefined'; + } + + /** + * Returns true if this object has been modified since its last + * save/refresh. If an attribute is specified, it returns true only if that + * particular attribute has been modified since the last save/refresh. + * @method dirty + * @param {String} attr An attribute name (optional). + * @return {Boolean} + */ + dirty(attr) { + if (!this.id) { + return true; + } + var pendingOps = this._getPendingOps(); + var dirtyObjects = this._getDirtyObjectAttributes(); + if (attr) { + if (dirtyObjects.hasOwnProperty(attr)) { + return true; + } + for (var i = 0; i < pendingOps.length; i++) { + if (pendingOps[i].hasOwnProperty(attr)) { + return true; + } + } + return false; + } + if (Object.keys(pendingOps[0]).length !== 0) { + return true; + } + if (Object.keys(dirtyObjects).length !== 0) { + return true; + } + return false; + } + + /** + * Returns an array of keys that have been modified since last save/refresh + * @method dirtyKeys + * @return {Array of string} + */ + dirtyKeys() { + var pendingOps = this._getPendingOps(); + var keys = {}; + for (var i = 0; i < pendingOps.length; i++) { + for (var attr in pendingOps[i]) { + keys[attr] = true; + } + } + var dirtyObjects = this._getDirtyObjectAttributes(); + for (var attr in dirtyObjects) { + keys[attr] = true; + } + return Object.keys(keys); + } + + /** + * Gets a Pointer referencing this Object. + * @method toPointer + * @return {Object} + */ + toPointer() { + if (!this.id) { + throw new Error('Cannot create a pointer to an unsaved ParseObject'); + } + return { + __type: 'Pointer', + className: this.className, + objectId: this.id + }; + } + + /** + * Gets the value of an attribute. + * @method get + * @param {String} attr The string name of an attribute. + */ + get(attr) { + return this.attributes[attr]; + } + + /** + * Gets a relation on the given class for the attribute. + * @method relation + * @param String attr The attribute to get the relation for. + */ + relation(attr) { + var value = this.get(attr); + if (value) { + if (!(value instanceof ParseRelation)) { + throw new Error('Called relation() on non-relation field ' + attr); + } + value._ensureParentAndKey(this, attr); + return value; + } + return new ParseRelation(this, attr); + } + + /** + * Gets the HTML-escaped value of an attribute. + * @method escape + * @param {String} attr The string name of an attribute. + */ + escape(attr) { + var val = this.attributes[attr]; + if (val == null) { + return ''; + } + + if (typeof val !== 'string') { + if (typeof val.toString !== 'function') { + return ''; + } + val = val.toString(); + } + return escape(val); + } + + /** + * Returns
true
if the attribute contains a value that is not
+ * null or undefined.
+ * @method has
+ * @param {String} attr The string name of the attribute.
+ * @return {Boolean}
+ */
+ has(attr) {
+ var attributes = this.attributes;
+ if (attributes.hasOwnProperty(attr)) {
+ return attributes[attr] != null;
+ }
+ return false;
+ }
+
+ /**
+ * Sets a hash of model attributes on the object.
+ *
+ * You can call it with an object containing keys and values, or with one + * key and value. For example:
+ * gameTurn.set({ + * player: player1, + * diceRoll: 2 + * }, { + * error: function(gameTurnAgain, error) { + * // The set failed validation. + * } + * }); + * + * game.set("currentPlayer", player2, { + * error: function(gameTurnAgain, error) { + * // The set failed validation. + * } + * }); + * + * game.set("finished", true);+ * + * @method set + * @param {String} key The key to set. + * @param {} value The value to give it. + * @param {Object} options A set of options for the set. + * The only supported option is
error
.
+ * @return {Boolean} true if the set succeeded.
+ */
+ set(key, value, options) {
+ var changes = {};
+ var newOps = {};
+ if (key && typeof key === 'object') {
+ changes = key;
+ options = value;
+ } else if (typeof key === 'string') {
+ changes[key] = value;
+ } else {
+ return this;
+ }
+
+ options = options || {};
+ var readonly = [];
+ if (typeof this.constructor.readOnlyAttributes === 'function') {
+ readonly = readonly.concat(this.constructor.readOnlyAttributes());
+ }
+ for (var k in changes) {
+ if (k === 'createdAt' || k === 'updatedAt') {
+ // This property is read-only, but for legacy reasons we silently
+ // ignore it
+ continue;
+ }
+ if (readonly.indexOf(k) > -1) {
+ throw new Error('Cannot modify readonly attribute: ' + k);
+ }
+ if (options.unset) {
+ newOps[k] = new UnsetOp();
+ } else if (changes[k] instanceof Op) {
+ newOps[k] = changes[k];
+ } else if (changes[k] && typeof changes[k] === 'object' && typeof changes[k].__op === 'string') {
+ newOps[k] = opFromJSON(changes[k]);
+ } else if (k === 'objectId' || k === 'id') {
+ if (typeof changes[k] === 'string') {
+ this.id = changes[k];
+ }
+ } else if (k === 'ACL' && typeof changes[k] === 'object' && !(changes[k] instanceof ParseACL)) {
+ newOps[k] = new SetOp(new ParseACL(changes[k]));
+ } else {
+ newOps[k] = new SetOp(changes[k]);
+ }
+ }
+
+ // Calculate new values
+ var currentAttributes = this.attributes;
+ var newValues = {};
+ for (var attr in newOps) {
+ if (newOps[attr] instanceof RelationOp) {
+ newValues[attr] = newOps[attr].applyTo(currentAttributes[attr], this, attr);
+ } else if (!(newOps[attr] instanceof UnsetOp)) {
+ newValues[attr] = newOps[attr].applyTo(currentAttributes[attr]);
+ }
+ }
+
+ // Validate changes
+ if (!options.ignoreValidation) {
+ var validation = this.validate(newValues);
+ if (validation) {
+ if (typeof options.error === 'function') {
+ options.error(this, validation);
+ }
+ return false;
+ }
+ }
+
+ // Consolidate Ops
+ var pendingOps = this._getPendingOps();
+ var last = pendingOps.length - 1;
+ var stateController = CoreManager.getObjectStateController();
+ for (var attr in newOps) {
+ var nextOp = newOps[attr].mergeWith(pendingOps[last][attr]);
+ stateController.setPendingOp(this._getStateIdentifier(), attr, nextOp);
+ }
+
+ return this;
+ }
+
+ /**
+ * Remove an attribute from the model. This is a noop if the attribute doesn't
+ * exist.
+ * @method unset
+ * @param {String} attr The string name of an attribute.
+ */
+ unset(attr, options) {
+ options = options || {};
+ options.unset = true;
+ return this.set(attr, null, options);
+ }
+
+ /**
+ * Atomically increments the value of the given attribute the next time the
+ * object is saved. If no amount is specified, 1 is used by default.
+ *
+ * @method increment
+ * @param attr {String} The key.
+ * @param amount {Number} The amount to increment by (optional).
+ */
+ increment(attr, amount) {
+ if (typeof amount === 'undefined') {
+ amount = 1;
+ }
+ if (typeof amount !== 'number') {
+ throw new Error('Cannot increment by a non-numeric amount.');
+ }
+ return this.set(attr, new IncrementOp(amount));
+ }
+
+ /**
+ * Atomically add an object to the end of the array associated with a given
+ * key.
+ * @method add
+ * @param attr {String} The key.
+ * @param item {} The item to add.
+ */
+ add(attr, item) {
+ return this.set(attr, new AddOp([item]));
+ }
+
+ /**
+ * Atomically add an object to the array associated with a given key, only
+ * if it is not already present in the array. The position of the insert is
+ * not guaranteed.
+ *
+ * @method addUnique
+ * @param attr {String} The key.
+ * @param item {} The object to add.
+ */
+ addUnique(attr, item) {
+ return this.set(attr, new AddUniqueOp([item]));
+ }
+
+ /**
+ * Atomically remove all instances of an object from the array associated
+ * with a given key.
+ *
+ * @method remove
+ * @param attr {String} The key.
+ * @param item {} The object to remove.
+ */
+ remove(attr, item) {
+ return this.set(attr, new RemoveOp([item]));
+ }
+
+ /**
+ * Returns an instance of a subclass of Parse.Op describing what kind of
+ * modification has been performed on this field since the last time it was
+ * saved. For example, after calling object.increment("x"), calling
+ * object.op("x") would return an instance of Parse.Op.Increment.
+ *
+ * @method op
+ * @param attr {String} The key.
+ * @returns {Parse.Op} The operation, or undefined if none.
+ */
+ op(attr) {
+ var pending = this._getPendingOps();
+ for (var i = pending.length; i--;) {
+ if (pending[i][attr]) {
+ return pending[i][attr];
+ }
+ }
+ }
+
+ /**
+ * Creates a new model with identical attributes to this one, similar to Backbone.Model's clone()
+ * @method clone
+ * @return {Parse.Object}
+ */
+ clone() {
+ let clone = new this.constructor();
+ if (!clone.className) {
+ clone.className = this.className;
+ }
+ let attributes = this.attributes;
+ if (typeof this.constructor.readOnlyAttributes === 'function') {
+ let readonly = this.constructor.readOnlyAttributes() || [];
+ // Attributes are frozen, so we have to rebuild an object,
+ // rather than delete readonly keys
+ let copy = {};
+ for (let a in attributes) {
+ if (readonly.indexOf(a) < 0) {
+ copy[a] = attributes[a];
+ }
+ }
+ attributes = copy;
+ }
+ if (clone.set) {
+ clone.set(attributes);
+ }
+ return clone;
+ }
+
+ /**
+ * Creates a new instance of this object. Not to be confused with clone()
+ * @method newInstance
+ * @return {Parse.Object}
+ */
+ newInstance() {
+ let clone = new this.constructor();
+ if (!clone.className) {
+ clone.className = this.className;
+ }
+ clone.id = this.id;
+ if (singleInstance) {
+ // Just return an object with the right id
+ return clone;
+ }
+
+ let stateController = CoreManager.getObjectStateController();
+ if (stateController) {
+ stateController.duplicateState(this._getStateIdentifier(), clone._getStateIdentifier());
+ }
+ return clone;
+ }
+
+ /**
+ * Returns true if this object has never been saved to Parse.
+ * @method isNew
+ * @return {Boolean}
+ */
+ isNew() {
+ return !this.id;
+ }
+
+ /**
+ * Returns true if this object was created by the Parse server when the
+ * object might have already been there (e.g. in the case of a Facebook
+ * login)
+ * @method existed
+ * @return {Boolean}
+ */
+ existed() {
+ if (!this.id) {
+ return false;
+ }
+ let stateController = CoreManager.getObjectStateController();
+ let state = stateController.getState(this._getStateIdentifier());
+ if (state) {
+ return state.existed;
+ }
+ return false;
+ }
+
+ /**
+ * Checks if the model is currently in a valid state.
+ * @method isValid
+ * @return {Boolean}
+ */
+ isValid() {
+ return !this.validate(this.attributes);
+ }
+
+ /**
+ * You should not call this function directly unless you subclass
+ * Parse.Object
, in which case you can override this method
+ * to provide additional validation on set
and
+ * save
. Your implementation should return
+ *
+ * @method validate
+ * @param {Object} attrs The current data to validate.
+ * @return {} False if the data is valid. An error object otherwise.
+ * @see Parse.Object#set
+ */
+ validate(attrs) {
+ if (attrs.hasOwnProperty('ACL') && !(attrs.ACL instanceof ParseACL)) {
+ return new ParseError(ParseError.OTHER_CAUSE, 'ACL must be a Parse ACL.');
+ }
+ for (var key in attrs) {
+ if (!/^[A-Za-z][0-9A-Za-z_]*$/.test(key)) {
+ return new ParseError(ParseError.INVALID_KEY_NAME);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns the ACL for this object.
+ * @method getACL
+ * @returns {Parse.ACL} An instance of Parse.ACL.
+ * @see Parse.Object#get
+ */
+ getACL() {
+ var acl = this.get('ACL');
+ if (acl instanceof ParseACL) {
+ return acl;
+ }
+ return null;
+ }
+
+ /**
+ * Sets the ACL to be used for this object.
+ * @method setACL
+ * @param {Parse.ACL} acl An instance of Parse.ACL.
+ * @param {Object} options Optional Backbone-like options object to be
+ * passed in to set.
+ * @return {Boolean} Whether the set passed validation.
+ * @see Parse.Object#set
+ */
+ setACL(acl, options) {
+ return this.set('ACL', acl, options);
+ }
+
+ /**
+ * Clears any changes to this object made since the last call to save()
+ * @method revert
+ */
+ revert() {
+ this._clearPendingOps();
+ }
+
+ /**
+ * Clears all attributes on a model
+ * @method clear
+ */
+ clear() {
+ var attributes = this.attributes;
+ var erasable = {};
+ var readonly = ['createdAt', 'updatedAt'];
+ if (typeof this.constructor.readOnlyAttributes === 'function') {
+ readonly = readonly.concat(this.constructor.readOnlyAttributes());
+ }
+ for (var attr in attributes) {
+ if (readonly.indexOf(attr) < 0) {
+ erasable[attr] = true;
+ }
+ }
+ return this.set(erasable, { unset: true });
+ }
+
+ /**
+ * Fetch the model from the server. If the server's representation of the
+ * model differs from its current attributes, they will be overriden.
+ *
+ * @method fetch
+ * @param {Object} options A Backbone-style callback object.
+ * Valid options are:+ * object.save();+ * or
+ * object.save(null, options);+ * or
+ * object.save(attrs, options);+ * or
+ * object.save(key, value, options);+ * + * For example,
+ * gameTurn.save({ + * player: "Jake Cutter", + * diceRoll: 2 + * }, { + * success: function(gameTurnAgain) { + * // The save was successful. + * }, + * error: function(gameTurnAgain, error) { + * // The save failed. Error is an instance of Parse.Error. + * } + * });+ * or with promises:
+ * gameTurn.save({ + * player: "Jake Cutter", + * diceRoll: 2 + * }).then(function(gameTurnAgain) { + * // The save was successful. + * }, function(error) { + * // The save failed. Error is an instance of Parse.Error. + * });+ * + * @method save + * @param {Object} options A Backbone-style callback object. + * Valid options are:
+ * Parse.Object.fetchAll([object1, object2, ...], { + * success: function(list) { + * // All the objects were fetched. + * }, + * error: function(error) { + * // An error occurred while fetching one of the objects. + * }, + * }); + *+ * + * @method fetchAll + * @param {Array} list A list of
Parse.Object
.
+ * @param {Object} options A Backbone-style callback object.
+ * @static
+ * Valid options are:+ * Parse.Object.fetchAllIfNeeded([object1, ...], { + * success: function(list) { + * // Objects were fetched and updated. + * }, + * error: function(error) { + * // An error occurred while fetching one of the objects. + * }, + * }); + *+ * + * @method fetchAllIfNeeded + * @param {Array} list A list of
Parse.Object
.
+ * @param {Object} options A Backbone-style callback object.
+ * @static
+ * Valid options are:Unlike saveAll, if an error occurs while deleting an individual model, + * this method will continue trying to delete the rest of the models if + * possible, except in the case of a fatal error like a connection error. + * + *
In particular, the Parse.Error object returned in the case of error may + * be one of two types: + * + *
+ * Parse.Object.destroyAll([object1, object2, ...], { + * success: function() { + * // All the objects were deleted. + * }, + * error: function(error) { + * // An error occurred while deleting one or more of the objects. + * // If this is an aggregate error, then we can inspect each error + * // object individually to determine the reason why a particular + * // object was not deleted. + * if (error.code === Parse.Error.AGGREGATE_ERROR) { + * for (var i = 0; i < error.errors.length; i++) { + * console.log("Couldn't delete " + error.errors[i].object.id + + * "due to " + error.errors[i].message); + * } + * } else { + * console.log("Delete aborted because of " + error.message); + * } + * }, + * }); + *+ * + * @method destroyAll + * @param {Array} list A list of
Parse.Object
.
+ * @param {Object} options A Backbone-style callback object.
+ * @static
+ * Valid options are:+ * Parse.Object.saveAll([object1, object2, ...], { + * success: function(list) { + * // All the objects were saved. + * }, + * error: function(error) { + * // An error occurred while saving one of the objects. + * }, + * }); + *+ * + * @method saveAll + * @param {Array} list A list of
Parse.Object
.
+ * @param {Object} options A Backbone-style callback object.
+ * @static
+ * Valid options are:A shortcut for:
+ * var Foo = Parse.Object.extend("Foo"); + * var pointerToFoo = new Foo(); + * pointerToFoo.id = "myObjectId"; + *+ * + * @method createWithoutData + * @param {String} id The ID of the object to create a reference to. + * @static + * @return {Parse.Object} A Parse.Object reference. + */ + static createWithoutData(id) { + var obj = new this(); + obj.id = id; + return obj; + } + + /** + * Creates a new instance of a Parse Object from a JSON representation. + * @method fromJSON + * @param {Object} json The JSON map of the Object's data + * @param {boolean} override In single instance mode, all old server data + * is overwritten if this is set to true + * @static + * @return {Parse.Object} A Parse.Object reference + */ + static fromJSON(json, override) { + if (!json.className) { + throw new Error('Cannot create an object without a className'); + } + var constructor = classMap[json.className]; + var o = constructor ? new constructor() : new ParseObject(json.className); + var otherAttributes = {}; + for (var attr in json) { + if (attr !== 'className' && attr !== '__type') { + otherAttributes[attr] = json[attr]; + } + } + if (override) { + // id needs to be set before clearServerData can work + if (otherAttributes.objectId) { + o.id = otherAttributes.objectId; + } + let preserved = null; + if (typeof o._preserveFieldsOnFetch === 'function') { + preserved = o._preserveFieldsOnFetch(); + } + o._clearServerData(); + if (preserved) { + o._finishFetch(preserved); + } + } + o._finishFetch(otherAttributes); + if (json.objectId) { + o._setExisted(true); + } + return o; + } + + /** + * Registers a subclass of Parse.Object with a specific class name. + * When objects of that class are retrieved from a query, they will be + * instantiated with this subclass. + * This is only necessary when using ES6 subclassing. + * @method registerSubclass + * @param {String} className The class name of the subclass + * @param {Class} constructor The subclass + */ + static registerSubclass(className, constructor) { + if (typeof className !== 'string') { + throw new TypeError('The first argument must be a valid class name.'); + } + if (typeof constructor === 'undefined') { + throw new TypeError('You must supply a subclass constructor.'); + } + if (typeof constructor !== 'function') { + throw new TypeError('You must register the subclass constructor. ' + 'Did you attempt to register an instance of the subclass?'); + } + classMap[className] = constructor; + if (!constructor.className) { + constructor.className = className; + } + } + + /** + * Creates a new subclass of Parse.Object for the given Parse class name. + * + *
Every extension of a Parse class will inherit from the most recent + * previous extension of that class. When a Parse.Object is automatically + * created by parsing JSON, it will use the most recent extension of that + * class.
+ * + *You should call either:
+ * var MyClass = Parse.Object.extend("MyClass", { + * Instance methods, + * initialize: function(attrs, options) { + * this.someInstanceProperty = [], + * Other instance properties + * } + * }, { + * Class properties + * });+ * or, for Backbone compatibility:
+ * var MyClass = Parse.Object.extend({ + * className: "MyClass", + * Instance methods, + * initialize: function(attrs, options) { + * this.someInstanceProperty = [], + * Other instance properties + * } + * }, { + * Class properties + * });+ * + * @method extend + * @param {String} className The name of the Parse class backing this model. + * @param {Object} protoProps Instance properties to add to instances of the + * class returned from this method. + * @param {Object} classProps Class properties to add the class returned from + * this method. + * @return {Class} A new subclass of Parse.Object. + */ + static extend(className, protoProps, classProps) { + if (typeof className !== 'string') { + if (className && typeof className.className === 'string') { + return ParseObject.extend(className.className, className, protoProps); + } else { + throw new Error('Parse.Object.extend\'s first argument should be the className.'); + } + } + var adjustedClassName = className; + + if (adjustedClassName === 'User' && CoreManager.get('PERFORM_USER_REWRITE')) { + adjustedClassName = '_User'; + } + + var parentProto = ParseObject.prototype; + if (this.hasOwnProperty('__super__') && this.__super__) { + parentProto = this.prototype; + } else if (classMap[adjustedClassName]) { + parentProto = classMap[adjustedClassName].prototype; + } + var ParseObjectSubclass = function (attributes, options) { + this.className = adjustedClassName; + this._objCount = objectCount++; + // Enable legacy initializers + if (typeof this.initialize === 'function') { + this.initialize.apply(this, arguments); + } + + if (attributes && typeof attributes === 'object') { + if (!this.set(attributes || {}, options)) { + throw new Error('Can\'t create an invalid Parse Object'); + } + } + }; + ParseObjectSubclass.className = adjustedClassName; + ParseObjectSubclass.__super__ = parentProto; + + ParseObjectSubclass.prototype = Object.create(parentProto, { + constructor: { + value: ParseObjectSubclass, + enumerable: false, + writable: true, + configurable: true + } + }); + + if (protoProps) { + for (var prop in protoProps) { + if (prop !== 'className') { + Object.defineProperty(ParseObjectSubclass.prototype, prop, { + value: protoProps[prop], + enumerable: false, + writable: true, + configurable: true + }); + } + } + } + + if (classProps) { + for (var prop in classProps) { + if (prop !== 'className') { + Object.defineProperty(ParseObjectSubclass, prop, { + value: classProps[prop], + enumerable: false, + writable: true, + configurable: true + }); + } + } + } + + ParseObjectSubclass.extend = function (name, protoProps, classProps) { + if (typeof name === 'string') { + return ParseObject.extend.call(ParseObjectSubclass, name, protoProps, classProps); + } + return ParseObject.extend.call(ParseObjectSubclass, adjustedClassName, name, protoProps); + }; + ParseObjectSubclass.createWithoutData = ParseObject.createWithoutData; + + classMap[adjustedClassName] = ParseObjectSubclass; + return ParseObjectSubclass; + } + + /** + * Enable single instance objects, where any local objects with the same Id + * share the same attributes, and stay synchronized with each other. + * This is disabled by default in server environments, since it can lead to + * security issues. + * @method enableSingleInstance + */ + static enableSingleInstance() { + singleInstance = true; + CoreManager.setObjectStateController(SingleInstanceStateController); + } + + /** + * Disable single instance objects, where any local objects with the same Id + * share the same attributes, and stay synchronized with each other. + * When disabled, you can have two instances of the same object in memory + * without them sharing attributes. + * @method disableSingleInstance + */ + static disableSingleInstance() { + singleInstance = false; + CoreManager.setObjectStateController(UniqueInstanceStateController); + } +} + +var DefaultController = { + fetch(target, forceFetch, options) { + if (Array.isArray(target)) { + if (target.length < 1) { + return ParsePromise.as([]); + } + var objs = []; + var ids = []; + var className = null; + var results = []; + var error = null; + target.forEach((el, i) => { + if (error) { + return; + } + if (!className) { + className = el.className; + } + if (className !== el.className) { + error = new ParseError(ParseError.INVALID_CLASS_NAME, 'All objects should be of the same class'); + } + if (!el.id) { + error = new ParseError(ParseError.MISSING_OBJECT_ID, 'All objects must have an ID'); + } + if (forceFetch || Object.keys(el._getServerData()).length === 0) { + ids.push(el.id); + objs.push(el); + } + results.push(el); + }); + if (error) { + return ParsePromise.error(error); + } + var query = new ParseQuery(className); + query.containedIn('objectId', ids); + query._limit = ids.length; + return query.find(options).then(objects => { + var idMap = {}; + objects.forEach(o => { + idMap[o.id] = o; + }); + for (var i = 0; i < objs.length; i++) { + var obj = objs[i]; + if (!obj || !obj.id || !idMap[obj.id]) { + if (forceFetch) { + return ParsePromise.error(new ParseError(ParseError.OBJECT_NOT_FOUND, 'All objects must exist on the server.')); + } + } + } + if (!singleInstance) { + // If single instance objects are disabled, we need to replace the + for (var i = 0; i < results.length; i++) { + var obj = results[i]; + if (obj && obj.id && idMap[obj.id]) { + var id = obj.id; + obj._finishFetch(idMap[id].toJSON()); + results[i] = idMap[id]; + } + } + } + return ParsePromise.as(results); + }); + } else { + var RESTController = CoreManager.getRESTController(); + return RESTController.request('GET', 'classes/' + target.className + '/' + target._getId(), {}, options).then((response, status, xhr) => { + if (target instanceof ParseObject) { + target._clearPendingOps(); + target._clearServerData(); + target._finishFetch(response); + } + return target; + }); + } + }, + + destroy(target, options) { + var RESTController = CoreManager.getRESTController(); + if (Array.isArray(target)) { + if (target.length < 1) { + return ParsePromise.as([]); + } + var batches = [[]]; + target.forEach(obj => { + if (!obj.id) { + return; + } + batches[batches.length - 1].push(obj); + if (batches[batches.length - 1].length >= 20) { + batches.push([]); + } + }); + if (batches[batches.length - 1].length === 0) { + // If the last batch is empty, remove it + batches.pop(); + } + var deleteCompleted = ParsePromise.as(); + var errors = []; + batches.forEach(batch => { + deleteCompleted = deleteCompleted.then(() => { + return RESTController.request('POST', 'batch', { + requests: batch.map(obj => { + return { + method: 'DELETE', + path: getServerUrlPath() + 'classes/' + obj.className + '/' + obj._getId(), + body: {} + }; + }) + }, options).then(results => { + for (var i = 0; i < results.length; i++) { + if (results[i] && results[i].hasOwnProperty('error')) { + var err = new ParseError(results[i].error.code, results[i].error.error); + err.object = batch[i]; + errors.push(err); + } + } + }); + }); + }); + return deleteCompleted.then(() => { + if (errors.length) { + var aggregate = new ParseError(ParseError.AGGREGATE_ERROR); + aggregate.errors = errors; + return ParsePromise.error(aggregate); + } + return ParsePromise.as(target); + }); + } else if (target instanceof ParseObject) { + return RESTController.request('DELETE', 'classes/' + target.className + '/' + target._getId(), {}, options).then(() => { + return ParsePromise.as(target); + }); + } + return ParsePromise.as(target); + }, + + save(target, options) { + var RESTController = CoreManager.getRESTController(); + var stateController = CoreManager.getObjectStateController(); + if (Array.isArray(target)) { + if (target.length < 1) { + return ParsePromise.as([]); + } + + var unsaved = target.concat(); + for (var i = 0; i < target.length; i++) { + if (target[i] instanceof ParseObject) { + unsaved = unsaved.concat(unsavedChildren(target[i], true)); + } + } + unsaved = unique(unsaved); + + var filesSaved = ParsePromise.as(); + var pending = []; + unsaved.forEach(el => { + if (el instanceof ParseFile) { + filesSaved = filesSaved.then(() => { + return el.save(); + }); + } else if (el instanceof ParseObject) { + pending.push(el); + } + }); + + return filesSaved.then(() => { + var objectError = null; + return ParsePromise._continueWhile(() => { + return pending.length > 0; + }, () => { + var batch = []; + var nextPending = []; + pending.forEach(el => { + if (batch.length < 20 && canBeSerialized(el)) { + batch.push(el); + } else { + nextPending.push(el); + } + }); + pending = nextPending; + if (batch.length < 1) { + return ParsePromise.error(new ParseError(ParseError.OTHER_CAUSE, 'Tried to save a batch with a cycle.')); + } + + // Queue up tasks for each object in the batch. + // When every task is ready, the API request will execute + var batchReturned = new ParsePromise(); + var batchReady = []; + var batchTasks = []; + batch.forEach((obj, index) => { + var ready = new ParsePromise(); + batchReady.push(ready); + + stateController.pushPendingState(obj._getStateIdentifier()); + batchTasks.push(stateController.enqueueTask(obj._getStateIdentifier(), function () { + ready.resolve(); + return batchReturned.then((responses, status) => { + if (responses[index].hasOwnProperty('success')) { + obj._handleSaveResponse(responses[index].success, status); + } else { + if (!objectError && responses[index].hasOwnProperty('error')) { + var serverError = responses[index].error; + objectError = new ParseError(serverError.code, serverError.error); + // Cancel the rest of the save + pending = []; + } + obj._handleSaveError(); + } + }); + })); + }); + + ParsePromise.when(batchReady).then(() => { + // Kick off the batch request + return RESTController.request('POST', 'batch', { + requests: batch.map(obj => { + var params = obj._getSaveParams(); + params.path = getServerUrlPath() + params.path; + return params; + }) + }, options); + }).then((response, status, xhr) => { + batchReturned.resolve(response, status); + }); + + return ParsePromise.when(batchTasks); + }).then(() => { + if (objectError) { + return ParsePromise.error(objectError); + } + return ParsePromise.as(target); + }); + }); + } else if (target instanceof ParseObject) { + // copying target lets Flow guarantee the pointer isn't modified elsewhere + var targetCopy = target; + var task = function () { + var params = targetCopy._getSaveParams(); + return RESTController.request(params.method, params.path, params.body, options).then((response, status) => { + targetCopy._handleSaveResponse(response, status); + }, error => { + targetCopy._handleSaveError(); + return ParsePromise.error(error); + }); + }; + + stateController.pushPendingState(target._getStateIdentifier()); + return stateController.enqueueTask(target._getStateIdentifier(), task).then(() => { + return target; + }, error => { + return ParsePromise.error(error); + }); + } + return ParsePromise.as(); + } +}; + +CoreManager.setObjectController(DefaultController); \ No newline at end of file diff --git a/lib/react-native/ParseOp.js b/lib/react-native/ParseOp.js new file mode 100644 index 000000000..db70be82c --- /dev/null +++ b/lib/react-native/ParseOp.js @@ -0,0 +1,434 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + */ + +import arrayContainsObject from './arrayContainsObject'; +import decode from './decode'; +import encode from './encode'; +import ParseObject from './ParseObject'; +import ParseRelation from './ParseRelation'; +import unique from './unique'; + +export function opFromJSON(json) { + if (!json || !json.__op) { + return null; + } + switch (json.__op) { + case 'Delete': + return new UnsetOp(); + case 'Increment': + return new IncrementOp(json.amount); + case 'Add': + return new AddOp(decode(json.objects)); + case 'AddUnique': + return new AddUniqueOp(decode(json.objects)); + case 'Remove': + return new RemoveOp(decode(json.objects)); + case 'AddRelation': + var toAdd = decode(json.objects); + if (!Array.isArray(toAdd)) { + return new RelationOp([], []); + } + return new RelationOp(toAdd, []); + case 'RemoveRelation': + var toRemove = decode(json.objects); + if (!Array.isArray(toRemove)) { + return new RelationOp([], []); + } + return new RelationOp([], toRemove); + case 'Batch': + var toAdd = []; + var toRemove = []; + for (var i = 0; i < json.ops.length; i++) { + if (json.ops[i].__op === 'AddRelation') { + toAdd = toAdd.concat(decode(json.ops[i].objects)); + } else if (json.ops[i].__op === 'RemoveRelation') { + toRemove = toRemove.concat(decode(json.ops[i].objects)); + } + } + return new RelationOp(toAdd, toRemove); + } + return null; +} + +export class Op { + // Empty parent class + applyTo(value) {} + mergeWith(previous) {} + toJSON() {} +} + +export class SetOp extends Op { + + constructor(value) { + super(); + this._value = value; + } + + applyTo(value) { + return this._value; + } + + mergeWith(previous) { + return new SetOp(this._value); + } + + toJSON() { + return encode(this._value, false, true); + } +} + +export class UnsetOp extends Op { + applyTo(value) { + return undefined; + } + + mergeWith(previous) { + return new UnsetOp(); + } + + toJSON() { + return { __op: 'Delete' }; + } +} + +export class IncrementOp extends Op { + + constructor(amount) { + super(); + if (typeof amount !== 'number') { + throw new TypeError('Increment Op must be initialized with a numeric amount.'); + } + this._amount = amount; + } + + applyTo(value) { + if (typeof value === 'undefined') { + return this._amount; + } + if (typeof value !== 'number') { + throw new TypeError('Cannot increment a non-numeric value.'); + } + return this._amount + value; + } + + mergeWith(previous) { + if (!previous) { + return this; + } + if (previous instanceof SetOp) { + return new SetOp(this.applyTo(previous._value)); + } + if (previous instanceof UnsetOp) { + return new SetOp(this._amount); + } + if (previous instanceof IncrementOp) { + return new IncrementOp(this.applyTo(previous._amount)); + } + throw new Error('Cannot merge Increment Op with the previous Op'); + } + + toJSON() { + return { __op: 'Increment', amount: this._amount }; + } +} + +export class AddOp extends Op { + + constructor(value) { + super(); + this._value = Array.isArray(value) ? value : [value]; + } + + applyTo(value) { + if (value == null) { + return this._value; + } + if (Array.isArray(value)) { + return value.concat(this._value); + } + throw new Error('Cannot add elements to a non-array value'); + } + + mergeWith(previous) { + if (!previous) { + return this; + } + if (previous instanceof SetOp) { + return new SetOp(this.applyTo(previous._value)); + } + if (previous instanceof UnsetOp) { + return new SetOp(this._value); + } + if (previous instanceof AddOp) { + return new AddOp(this.applyTo(previous._value)); + } + throw new Error('Cannot merge Add Op with the previous Op'); + } + + toJSON() { + return { __op: 'Add', objects: encode(this._value, false, true) }; + } +} + +export class AddUniqueOp extends Op { + + constructor(value) { + super(); + this._value = unique(Array.isArray(value) ? value : [value]); + } + + applyTo(value) { + if (value == null) { + return this._value || []; + } + if (Array.isArray(value)) { + // copying value lets Flow guarantee the pointer isn't modified elsewhere + var valueCopy = value; + var toAdd = []; + this._value.forEach(v => { + if (v instanceof ParseObject) { + if (!arrayContainsObject(valueCopy, v)) { + toAdd.push(v); + } + } else { + if (valueCopy.indexOf(v) < 0) { + toAdd.push(v); + } + } + }); + return value.concat(toAdd); + } + throw new Error('Cannot add elements to a non-array value'); + } + + mergeWith(previous) { + if (!previous) { + return this; + } + if (previous instanceof SetOp) { + return new SetOp(this.applyTo(previous._value)); + } + if (previous instanceof UnsetOp) { + return new SetOp(this._value); + } + if (previous instanceof AddUniqueOp) { + return new AddUniqueOp(this.applyTo(previous._value)); + } + throw new Error('Cannot merge AddUnique Op with the previous Op'); + } + + toJSON() { + return { __op: 'AddUnique', objects: encode(this._value, false, true) }; + } +} + +export class RemoveOp extends Op { + + constructor(value) { + super(); + this._value = unique(Array.isArray(value) ? value : [value]); + } + + applyTo(value) { + if (value == null) { + return []; + } + if (Array.isArray(value)) { + var i = value.indexOf(this._value); + var removed = value.concat([]); + for (var i = 0; i < this._value.length; i++) { + var index = removed.indexOf(this._value[i]); + while (index > -1) { + removed.splice(index, 1); + index = removed.indexOf(this._value[i]); + } + if (this._value[i] instanceof ParseObject && this._value[i].id) { + for (var j = 0; j < removed.length; j++) { + if (removed[j] instanceof ParseObject && this._value[i].id === removed[j].id) { + removed.splice(j, 1); + j--; + } + } + } + } + return removed; + } + throw new Error('Cannot remove elements from a non-array value'); + } + + mergeWith(previous) { + if (!previous) { + return this; + } + if (previous instanceof SetOp) { + return new SetOp(this.applyTo(previous._value)); + } + if (previous instanceof UnsetOp) { + return new UnsetOp(); + } + if (previous instanceof RemoveOp) { + var uniques = previous._value.concat([]); + for (var i = 0; i < this._value.length; i++) { + if (this._value[i] instanceof ParseObject) { + if (!arrayContainsObject(uniques, this._value[i])) { + uniques.push(this._value[i]); + } + } else { + if (uniques.indexOf(this._value[i]) < 0) { + uniques.push(this._value[i]); + } + } + } + return new RemoveOp(uniques); + } + throw new Error('Cannot merge Remove Op with the previous Op'); + } + + toJSON() { + return { __op: 'Remove', objects: encode(this._value, false, true) }; + } +} + +export class RelationOp extends Op { + + constructor(adds, removes) { + super(); + this._targetClassName = null; + + if (Array.isArray(adds)) { + this.relationsToAdd = unique(adds.map(this._extractId, this)); + } + + if (Array.isArray(removes)) { + this.relationsToRemove = unique(removes.map(this._extractId, this)); + } + } + + _extractId(obj) { + if (typeof obj === 'string') { + return obj; + } + if (!obj.id) { + throw new Error('You cannot add or remove an unsaved Parse Object from a relation'); + } + if (!this._targetClassName) { + this._targetClassName = obj.className; + } + if (this._targetClassName !== obj.className) { + throw new Error('Tried to create a Relation with 2 different object types: ' + this._targetClassName + ' and ' + obj.className + '.'); + } + return obj.id; + } + + applyTo(value, object, key) { + if (!value) { + if (!object || !key) { + throw new Error('Cannot apply a RelationOp without either a previous value, or an object and a key'); + } + var parent = new ParseObject(object.className); + if (object.id && object.id.indexOf('local') === 0) { + parent._localId = object.id; + } else if (object.id) { + parent.id = object.id; + } + var relation = new ParseRelation(parent, key); + relation.targetClassName = this._targetClassName; + return relation; + } + if (value instanceof ParseRelation) { + if (this._targetClassName) { + if (value.targetClassName) { + if (this._targetClassName !== value.targetClassName) { + throw new Error('Related object must be a ' + value.targetClassName + ', but a ' + this._targetClassName + ' was passed in.'); + } + } else { + value.targetClassName = this._targetClassName; + } + } + return value; + } else { + throw new Error('Relation cannot be applied to a non-relation field'); + } + } + + mergeWith(previous) { + if (!previous) { + return this; + } else if (previous instanceof UnsetOp) { + throw new Error('You cannot modify a relation after deleting it.'); + } else if (previous instanceof RelationOp) { + if (previous._targetClassName && previous._targetClassName !== this._targetClassName) { + throw new Error('Related object must be of class ' + previous._targetClassName + ', but ' + (this._targetClassName || 'null') + ' was passed in.'); + } + var newAdd = previous.relationsToAdd.concat([]); + this.relationsToRemove.forEach(r => { + var index = newAdd.indexOf(r); + if (index > -1) { + newAdd.splice(index, 1); + } + }); + this.relationsToAdd.forEach(r => { + var index = newAdd.indexOf(r); + if (index < 0) { + newAdd.push(r); + } + }); + + var newRemove = previous.relationsToRemove.concat([]); + this.relationsToAdd.forEach(r => { + var index = newRemove.indexOf(r); + if (index > -1) { + newRemove.splice(index, 1); + } + }); + this.relationsToRemove.forEach(r => { + var index = newRemove.indexOf(r); + if (index < 0) { + newRemove.push(r); + } + }); + + var newRelation = new RelationOp(newAdd, newRemove); + newRelation._targetClassName = this._targetClassName; + return newRelation; + } + throw new Error('Cannot merge Relation Op with the previous Op'); + } + + toJSON() { + var idToPointer = id => { + return { + __type: 'Pointer', + className: this._targetClassName, + objectId: id + }; + }; + + var adds = null; + var removes = null; + var pointers = null; + + if (this.relationsToAdd.length > 0) { + pointers = this.relationsToAdd.map(idToPointer); + adds = { __op: 'AddRelation', objects: pointers }; + } + if (this.relationsToRemove.length > 0) { + pointers = this.relationsToRemove.map(idToPointer); + removes = { __op: 'RemoveRelation', objects: pointers }; + } + + if (adds && removes) { + return { __op: 'Batch', ops: [adds, removes] }; + } + + return adds || removes || {}; + } +} \ No newline at end of file diff --git a/lib/react-native/ParsePromise.js b/lib/react-native/ParsePromise.js new file mode 100644 index 000000000..ef7626b6d --- /dev/null +++ b/lib/react-native/ParsePromise.js @@ -0,0 +1,575 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +var isPromisesAPlusCompliant = true; + +/** + * A Promise is returned by async methods as a hook to provide callbacks to be + * called when the async task is fulfilled. + * + *
Typical usage would be like:
+ * query.find().then(function(results) { + * results[0].set("foo", "bar"); + * return results[0].saveAsync(); + * }).then(function(result) { + * console.log("Updated " + result.id); + * }); + *+ * + * @class Parse.Promise + * @constructor + */ +export default class ParsePromise { + constructor(executor) { + this._resolved = false; + this._rejected = false; + this._resolvedCallbacks = []; + this._rejectedCallbacks = []; + + if (typeof executor === 'function') { + executor(this.resolve.bind(this), this.reject.bind(this)); + } + } + + /** + * Marks this promise as fulfilled, firing any callbacks waiting on it. + * @method resolve + * @param {Object} result the result to pass to the callbacks. + */ + resolve(...results) { + if (this._resolved || this._rejected) { + throw new Error('A promise was resolved even though it had already been ' + (this._resolved ? 'resolved' : 'rejected') + '.'); + } + this._resolved = true; + this._result = results; + for (var i = 0; i < this._resolvedCallbacks.length; i++) { + this._resolvedCallbacks[i].apply(this, results); + } + + this._resolvedCallbacks = []; + this._rejectedCallbacks = []; + } + + /** + * Marks this promise as fulfilled, firing any callbacks waiting on it. + * @method reject + * @param {Object} error the error to pass to the callbacks. + */ + reject(error) { + if (this._resolved || this._rejected) { + throw new Error('A promise was rejected even though it had already been ' + (this._resolved ? 'resolved' : 'rejected') + '.'); + } + this._rejected = true; + this._error = error; + for (var i = 0; i < this._rejectedCallbacks.length; i++) { + this._rejectedCallbacks[i](error); + } + this._resolvedCallbacks = []; + this._rejectedCallbacks = []; + } + + /** + * Adds callbacks to be called when this promise is fulfilled. Returns a new + * Promise that will be fulfilled when the callback is complete. It allows + * chaining. If the callback itself returns a Promise, then the one returned + * by "then" will not be fulfilled until that one returned by the callback + * is fulfilled. + * @method then + * @param {Function} resolvedCallback Function that is called when this + * Promise is resolved. Once the callback is complete, then the Promise + * returned by "then" will also be fulfilled. + * @param {Function} rejectedCallback Function that is called when this + * Promise is rejected with an error. Once the callback is complete, then + * the promise returned by "then" with be resolved successfully. If + * rejectedCallback is null, or it returns a rejected Promise, then the + * Promise returned by "then" will be rejected with that error. + * @return {Parse.Promise} A new Promise that will be fulfilled after this + * Promise is fulfilled and either callback has completed. If the callback + * returned a Promise, then this Promise will not be fulfilled until that + * one is. + */ + then(resolvedCallback, rejectedCallback) { + var promise = new ParsePromise(); + + var wrappedResolvedCallback = function (...results) { + if (typeof resolvedCallback === 'function') { + if (isPromisesAPlusCompliant) { + try { + results = [resolvedCallback.apply(this, results)]; + } catch (e) { + results = [ParsePromise.error(e)]; + } + } else { + results = [resolvedCallback.apply(this, results)]; + } + } + if (results.length === 1 && ParsePromise.is(results[0])) { + results[0].then(function () { + promise.resolve.apply(promise, arguments); + }, function (error) { + promise.reject(error); + }); + } else { + promise.resolve.apply(promise, results); + } + }; + + var wrappedRejectedCallback = function (error) { + var result = []; + if (typeof rejectedCallback === 'function') { + if (isPromisesAPlusCompliant) { + try { + result = [rejectedCallback(error)]; + } catch (e) { + result = [ParsePromise.error(e)]; + } + } else { + result = [rejectedCallback(error)]; + } + if (result.length === 1 && ParsePromise.is(result[0])) { + result[0].then(function () { + promise.resolve.apply(promise, arguments); + }, function (error) { + promise.reject(error); + }); + } else { + if (isPromisesAPlusCompliant) { + promise.resolve.apply(promise, result); + } else { + promise.reject(result[0]); + } + } + } else { + promise.reject(error); + } + }; + + var runLater = function (fn) { + fn.call(); + }; + if (isPromisesAPlusCompliant) { + if (typeof process !== 'undefined' && typeof process.nextTick === 'function') { + runLater = function (fn) { + process.nextTick(fn); + }; + } else if (typeof setTimeout === 'function') { + runLater = function (fn) { + setTimeout(fn, 0); + }; + } + } + + if (this._resolved) { + runLater(() => { + wrappedResolvedCallback.apply(this, this._result); + }); + } else if (this._rejected) { + runLater(() => { + wrappedRejectedCallback(this._error); + }); + } else { + this._resolvedCallbacks.push(wrappedResolvedCallback); + this._rejectedCallbacks.push(wrappedRejectedCallback); + } + + return promise; + } + + /** + * Add handlers to be called when the promise + * is either resolved or rejected + * @method always + */ + always(callback) { + return this.then(callback, callback); + } + + /** + * Add handlers to be called when the Promise object is resolved + * @method done + */ + done(callback) { + return this.then(callback); + } + + /** + * Add handlers to be called when the Promise object is rejected + * Alias for catch(). + * @method fail + */ + fail(callback) { + return this.then(null, callback); + } + + /** + * Add handlers to be called when the Promise object is rejected + * @method catch + */ + catch(callback) { + return this.then(null, callback); + } + + /** + * Run the given callbacks after this promise is fulfilled. + * @method _thenRunCallbacks + * @param optionsOrCallback {} A Backbone-style options callback, or a + * callback function. If this is an options object and contains a "model" + * attributes, that will be passed to error callbacks as the first argument. + * @param model {} If truthy, this will be passed as the first result of + * error callbacks. This is for Backbone-compatability. + * @return {Parse.Promise} A promise that will be resolved after the + * callbacks are run, with the same result as this. + */ + _thenRunCallbacks(optionsOrCallback, model) { + var options = {}; + if (typeof optionsOrCallback === 'function') { + options.success = function (result) { + optionsOrCallback(result, null); + }; + options.error = function (error) { + optionsOrCallback(null, error); + }; + } else if (typeof optionsOrCallback === 'object') { + if (typeof optionsOrCallback.success === 'function') { + options.success = optionsOrCallback.success; + } + if (typeof optionsOrCallback.error === 'function') { + options.error = optionsOrCallback.error; + } + } + + return this.then(function (...results) { + if (options.success) { + options.success.apply(this, results); + } + return ParsePromise.as.apply(ParsePromise, arguments); + }, function (error) { + if (options.error) { + if (typeof model !== 'undefined') { + options.error(model, error); + } else { + options.error(error); + } + } + // By explicitly returning a rejected Promise, this will work with + // either jQuery or Promises/A+ semantics. + return ParsePromise.error(error); + }); + } + + /** + * Adds a callback function that should be called regardless of whether + * this promise failed or succeeded. The callback will be given either the + * array of results for its first argument, or the error as its second, + * depending on whether this Promise was rejected or resolved. Returns a + * new Promise, like "then" would. + * @method _continueWith + * @param {Function} continuation the callback. + */ + _continueWith(continuation) { + return this.then(function (...args) { + return continuation(args, null); + }, function (error) { + return continuation(null, error); + }); + } + + /** + * Returns true iff the given object fulfils the Promise interface. + * @method is + * @param {Object} promise The object to test + * @static + * @return {Boolean} + */ + static is(promise) { + return promise != null && typeof promise.then === 'function'; + } + + /** + * Returns a new promise that is resolved with a given value. + * @method as + * @param value The value to resolve the promise with + * @static + * @return {Parse.Promise} the new promise. + */ + static as(...values) { + var promise = new ParsePromise(); + promise.resolve.apply(promise, values); + return promise; + } + + /** + * Returns a new promise that is resolved with a given value. + * If that value is a thenable Promise (has a .then() prototype + * method), the new promise will be chained to the end of the + * value. + * @method resolve + * @param value The value to resolve the promise with + * @static + * @return {Parse.Promise} the new promise. + */ + static resolve(value) { + return new ParsePromise((resolve, reject) => { + if (ParsePromise.is(value)) { + value.then(resolve, reject); + } else { + resolve(value); + } + }); + } + + /** + * Returns a new promise that is rejected with a given error. + * @method error + * @param error The error to reject the promise with + * @static + * @return {Parse.Promise} the new promise. + */ + static error(...errors) { + var promise = new ParsePromise(); + promise.reject.apply(promise, errors); + return promise; + } + + /** + * Returns a new promise that is rejected with a given error. + * This is an alias for Parse.Promise.error, for compliance with + * the ES6 implementation. + * @method reject + * @param error The error to reject the promise with + * @static + * @return {Parse.Promise} the new promise. + */ + static reject(...errors) { + return ParsePromise.error.apply(null, errors); + } + + /** + * Returns a new promise that is fulfilled when all of the input promises + * are resolved. If any promise in the list fails, then the returned promise + * will be rejected with an array containing the error from each promise. + * If they all succeed, then the returned promise will succeed, with the + * results being the results of all the input + * promises. For example:
+ * var p1 = Parse.Promise.as(1); + * var p2 = Parse.Promise.as(2); + * var p3 = Parse.Promise.as(3); + * + * Parse.Promise.when(p1, p2, p3).then(function(r1, r2, r3) { + * console.log(r1); // prints 1 + * console.log(r2); // prints 2 + * console.log(r3); // prints 3 + * });+ * + * The input promises can also be specified as an array:
+ * var promises = [p1, p2, p3]; + * Parse.Promise.when(promises).then(function(results) { + * console.log(results); // prints [1,2,3] + * }); + *+ * @method when + * @param {Array} promises a list of promises to wait for. + * @static + * @return {Parse.Promise} the new promise. + */ + static when(promises) { + var objects; + var arrayArgument = Array.isArray(promises); + if (arrayArgument) { + objects = promises; + } else { + objects = arguments; + } + + var total = objects.length; + var hadError = false; + var results = []; + var returnValue = arrayArgument ? [results] : results; + var errors = []; + results.length = objects.length; + errors.length = objects.length; + + if (total === 0) { + return ParsePromise.as.apply(this, returnValue); + } + + var promise = new ParsePromise(); + + var resolveOne = function () { + total--; + if (total <= 0) { + if (hadError) { + promise.reject(errors); + } else { + promise.resolve.apply(promise, returnValue); + } + } + }; + + var chain = function (object, index) { + if (ParsePromise.is(object)) { + object.then(function (result) { + results[index] = result; + resolveOne(); + }, function (error) { + errors[index] = error; + hadError = true; + resolveOne(); + }); + } else { + results[i] = object; + resolveOne(); + } + }; + for (var i = 0; i < objects.length; i++) { + chain(objects[i], i); + } + + return promise; + } + + /** + * Returns a new promise that is fulfilled when all of the promises in the + * iterable argument are resolved. If any promise in the list fails, then + * the returned promise will be immediately rejected with the reason that + * single promise rejected. If they all succeed, then the returned promise + * will succeed, with the results being the results of all the input + * promises. If the iterable provided is empty, the returned promise will + * be immediately resolved. + * + * For example:
+ * var p1 = Parse.Promise.as(1); + * var p2 = Parse.Promise.as(2); + * var p3 = Parse.Promise.as(3); + * + * Parse.Promise.all([p1, p2, p3]).then(function([r1, r2, r3]) { + * console.log(r1); // prints 1 + * console.log(r2); // prints 2 + * console.log(r3); // prints 3 + * });+ * + * @method all + * @param {Iterable} promises an iterable of promises to wait for. + * @static + * @return {Parse.Promise} the new promise. + */ + static all(promises) { + let total = 0; + let objects = []; + + for (let p of promises) { + objects[total++] = p; + } + + if (total === 0) { + return ParsePromise.as([]); + } + + let hadError = false; + let promise = new ParsePromise(); + let resolved = 0; + let results = []; + objects.forEach((object, i) => { + if (ParsePromise.is(object)) { + object.then(result => { + if (hadError) { + return false; + } + results[i] = result; + resolved++; + if (resolved >= total) { + promise.resolve(results); + } + }, error => { + // Reject immediately + promise.reject(error); + hadError = true; + }); + } else { + results[i] = object; + resolved++; + if (!hadError && resolved >= total) { + promise.resolve(results); + } + } + }); + + return promise; + } + + /** + * Returns a new promise that is immediately fulfilled when any of the + * promises in the iterable argument are resolved or rejected. If the + * first promise to complete is resolved, the returned promise will be + * resolved with the same value. Likewise, if the first promise to + * complete is rejected, the returned promise will be rejected with the + * same reason. + * + * @method race + * @param {Iterable} promises an iterable of promises to wait for. + * @static + * @return {Parse.Promise} the new promise. + */ + static race(promises) { + let completed = false; + let promise = new ParsePromise(); + for (let p of promises) { + if (ParsePromise.is(p)) { + p.then(result => { + if (completed) { + return; + } + completed = true; + promise.resolve(result); + }, error => { + if (completed) { + return; + } + completed = true; + promise.reject(error); + }); + } else if (!completed) { + completed = true; + promise.resolve(p); + } + } + + return promise; + } + + /** + * Runs the given asyncFunction repeatedly, as long as the predicate + * function returns a truthy value. Stops repeating if asyncFunction returns + * a rejected promise. + * @method _continueWhile + * @param {Function} predicate should return false when ready to stop. + * @param {Function} asyncFunction should return a Promise. + * @static + */ + static _continueWhile(predicate, asyncFunction) { + if (predicate()) { + return asyncFunction().then(function () { + return ParsePromise._continueWhile(predicate, asyncFunction); + }); + } + return ParsePromise.as(); + } + + static isPromisesAPlusCompliant() { + return isPromisesAPlusCompliant; + } + + static enableAPlusCompliant() { + isPromisesAPlusCompliant = true; + } + + static disableAPlusCompliant() { + isPromisesAPlusCompliant = false; + } +} \ No newline at end of file diff --git a/lib/react-native/ParseQuery.js b/lib/react-native/ParseQuery.js new file mode 100644 index 000000000..a32275dc1 --- /dev/null +++ b/lib/react-native/ParseQuery.js @@ -0,0 +1,1113 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + */ + +import CoreManager from './CoreManager'; +import encode from './encode'; +import ParseError from './ParseError'; +import ParseGeoPoint from './ParseGeoPoint'; +import ParseObject from './ParseObject'; +import ParsePromise from './ParsePromise'; + +/** + * Converts a string into a regex that matches it. + * Surrounding with \Q .. \E does this, we just need to escape any \E's in + * the text separately. + */ +function quote(s) { + return '\\Q' + s.replace('\\E', '\\E\\\\E\\Q') + '\\E'; +} + +/** + * Handles pre-populating the result data of a query with select fields, + * making sure that the data object contains keys for all objects that have + * been requested with a select, so that our cached state updates correctly. + */ +function handleSelectResult(data, select) { + var serverDataMask = {}; + + select.forEach(field => { + let hasSubObjectSelect = field.indexOf(".") !== -1; + if (!hasSubObjectSelect && !data.hasOwnProperty(field)) { + // this field was selected, but is missing from the retrieved data + data[field] = undefined; + } else if (hasSubObjectSelect) { + // this field references a sub-object, + // so we need to walk down the path components + let pathComponents = field.split("."); + var obj = data; + var serverMask = serverDataMask; + + pathComponents.forEach((component, index, arr) => { + // add keys if the expected data is missing + if (!obj[component]) { + obj[component] = index == arr.length - 1 ? undefined : {}; + } + obj = obj[component]; + + //add this path component to the server mask so we can fill it in later if needed + if (index < arr.length - 1) { + if (!serverMask[component]) { + serverMask[component] = {}; + } + } + }); + } + }); + + if (Object.keys(serverDataMask).length > 0) { + // When selecting from sub-objects, we don't want to blow away the missing + // information that we may have retrieved before. We've already added any + // missing selected keys to sub-objects, but we still need to add in the + // data for any previously retrieved sub-objects that were not selected. + + let serverData = CoreManager.getObjectStateController().getServerData({ id: data.objectId, className: data.className }); + + function copyMissingDataWithMask(src, dest, mask, copyThisLevel) { + //copy missing elements at this level + if (copyThisLevel) { + for (var key in src) { + if (src.hasOwnProperty(key) && !dest.hasOwnProperty(key)) { + dest[key] = src[key]; + } + } + } + for (var key in mask) { + //traverse into objects as needed + copyMissingDataWithMask(src[key], dest[key], mask[key], true); + } + } + + copyMissingDataWithMask(serverData, data, serverDataMask, false); + } +} + +/** + * Creates a new parse Parse.Query for the given Parse.Object subclass. + * @class Parse.Query + * @constructor + * @param {} objectClass An instance of a subclass of Parse.Object, or a Parse className string. + * + *
Parse.Query defines a query that is used to fetch Parse.Objects. The
+ * most common use case is finding all objects that match a query through the
+ * find
method. For example, this sample code fetches all objects
+ * of class MyClass
. It calls a different function depending on
+ * whether the fetch succeeded or not.
+ *
+ *
+ * var query = new Parse.Query(MyClass); + * query.find({ + * success: function(results) { + * // results is an array of Parse.Object. + * }, + * + * error: function(error) { + * // error is an instance of Parse.Error. + * } + * });+ * + *
A Parse.Query can also be used to retrieve a single object whose id is
+ * known, through the get method. For example, this sample code fetches an
+ * object of class MyClass
and id myId
. It calls a
+ * different function depending on whether the fetch succeeded or not.
+ *
+ *
+ * var query = new Parse.Query(MyClass); + * query.get(myId, { + * success: function(object) { + * // object is an instance of Parse.Object. + * }, + * + * error: function(object, error) { + * // error is an instance of Parse.Error. + * } + * });+ * + *
A Parse.Query can also be used to count the number of objects that match
+ * the query without retrieving all of those objects. For example, this
+ * sample code counts the number of objects of the class MyClass
+ *
+ * var query = new Parse.Query(MyClass); + * query.count({ + * success: function(number) { + * // There are number instances of MyClass. + * }, + * + * error: function(error) { + * // error is an instance of Parse.Error. + * } + * });+ */ +export default class ParseQuery { + + constructor(objectClass) { + if (typeof objectClass === 'string') { + if (objectClass === 'User' && CoreManager.get('PERFORM_USER_REWRITE')) { + this.className = '_User'; + } else { + this.className = objectClass; + } + } else if (objectClass instanceof ParseObject) { + this.className = objectClass.className; + } else if (typeof objectClass === 'function') { + if (typeof objectClass.className === 'string') { + this.className = objectClass.className; + } else { + var obj = new objectClass(); + this.className = obj.className; + } + } else { + throw new TypeError('A ParseQuery must be constructed with a ParseObject or class name.'); + } + + this._where = {}; + this._include = []; + this._limit = -1; // negative limit is not sent in the server request + this._skip = 0; + this._extraOptions = {}; + } + + /** + * Adds constraint that at least one of the passed in queries matches. + * @method _orQuery + * @param {Array} queries + * @return {Parse.Query} Returns the query, so you can chain this call. + */ + _orQuery(queries) { + var queryJSON = queries.map(q => { + return q.toJSON().where; + }); + + this._where.$or = queryJSON; + return this; + } + + /** + * Helper for condition queries + */ + _addCondition(key, condition, value) { + if (!this._where[key] || typeof this._where[key] === 'string') { + this._where[key] = {}; + } + this._where[key][condition] = encode(value, false, true); + return this; + } + + /** + * Converts string for regular expression at the beginning + */ + _regexStartWith(string) { + return '^' + quote(string); + } + + /** + * Returns a JSON representation of this query. + * @method toJSON + * @return {Object} The JSON representation of the query. + */ + toJSON() { + var params = { + where: this._where + }; + + if (this._include.length) { + params.include = this._include.join(','); + } + if (this._select) { + params.keys = this._select.join(','); + } + if (this._limit >= 0) { + params.limit = this._limit; + } + if (this._skip > 0) { + params.skip = this._skip; + } + if (this._order) { + params.order = this._order.join(','); + } + for (var key in this._extraOptions) { + params[key] = this._extraOptions[key]; + } + + return params; + } + + /** + * Constructs a Parse.Object whose id is already known by fetching data from + * the server. Either options.success or options.error is called when the + * find completes. + * + * @method get + * @param {String} objectId The id of the object to be fetched. + * @param {Object} options A Backbone-style options object. + * Valid options are:
var compoundQuery = Parse.Query.or(query1, query2, query3);+ * + * will create a compoundQuery that is an or of the query1, query2, and + * query3. + * @method or + * @param {...Parse.Query} var_args The list of queries to OR. + * @static + * @return {Parse.Query} The query that is the OR of the passed in queries. + */ + static or(...queries) { + var className = null; + queries.forEach(q => { + if (!className) { + className = q.className; + } + + if (className !== q.className) { + throw new Error('All queries must be for the same class.'); + } + }); + + var query = new ParseQuery(className); + query._orQuery(queries); + return query; + } +} + +var DefaultController = { + find(className, params, options) { + var RESTController = CoreManager.getRESTController(); + + return RESTController.request('GET', 'classes/' + className, params, options); + } +}; + +CoreManager.setQueryController(DefaultController); \ No newline at end of file diff --git a/lib/react-native/ParseRelation.js b/lib/react-native/ParseRelation.js new file mode 100644 index 000000000..c07b48cc1 --- /dev/null +++ b/lib/react-native/ParseRelation.js @@ -0,0 +1,140 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + */ + +import { RelationOp } from './ParseOp'; +import ParseObject from './ParseObject'; +import ParseQuery from './ParseQuery'; + +/** + * Creates a new Relation for the given parent object and key. This + * constructor should rarely be used directly, but rather created by + * Parse.Object.relation. + * @class Parse.Relation + * @constructor + * @param {Parse.Object} parent The parent of this relation. + * @param {String} key The key for this relation on the parent. + * + *
+ * A class that is used to access all of the children of a many-to-many + * relationship. Each instance of Parse.Relation is associated with a + * particular parent object and key. + *
+ */ +export default class ParseRelation { + + constructor(parent, key) { + this.parent = parent; + this.key = key; + this.targetClassName = null; + } + + /** + * Makes sure that this relation has the right parent and key. + */ + _ensureParentAndKey(parent, key) { + this.key = this.key || key; + if (this.key !== key) { + throw new Error('Internal Error. Relation retrieved from two different keys.'); + } + if (this.parent) { + if (this.parent.className !== parent.className) { + throw new Error('Internal Error. Relation retrieved from two different Objects.'); + } + if (this.parent.id) { + if (this.parent.id !== parent.id) { + throw new Error('Internal Error. Relation retrieved from two different Objects.'); + } + } else if (parent.id) { + this.parent = parent; + } + } else { + this.parent = parent; + } + } + + /** + * Adds a Parse.Object or an array of Parse.Objects to the relation. + * @method add + * @param {} objects The item or items to add. + */ + add(objects) { + if (!Array.isArray(objects)) { + objects = [objects]; + } + + var change = new RelationOp(objects, []); + var parent = this.parent; + if (!parent) { + throw new Error('Cannot add to a Relation without a parent'); + } + parent.set(this.key, change); + this.targetClassName = change._targetClassName; + return parent; + } + + /** + * Removes a Parse.Object or an array of Parse.Objects from this relation. + * @method remove + * @param {} objects The item or items to remove. + */ + remove(objects) { + if (!Array.isArray(objects)) { + objects = [objects]; + } + + var change = new RelationOp([], objects); + if (!this.parent) { + throw new Error('Cannot remove from a Relation without a parent'); + } + this.parent.set(this.key, change); + this.targetClassName = change._targetClassName; + } + + /** + * Returns a JSON version of the object suitable for saving to disk. + * @method toJSON + * @return {Object} + */ + toJSON() { + return { + __type: 'Relation', + className: this.targetClassName + }; + } + + /** + * Returns a Parse.Query that is limited to objects in this + * relation. + * @method query + * @return {Parse.Query} + */ + query() { + var query; + var parent = this.parent; + if (!parent) { + throw new Error('Cannot construct a query for a Relation without a parent'); + } + if (!this.targetClassName) { + query = new ParseQuery(parent.className); + query._extraOptions.redirectClassNameForKey = this.key; + } else { + query = new ParseQuery(this.targetClassName); + } + query._addCondition('$relatedTo', 'object', { + __type: 'Pointer', + className: parent.className, + objectId: parent.id + }); + query._addCondition('$relatedTo', 'key', this.key); + + return query; + } +} \ No newline at end of file diff --git a/lib/react-native/ParseRole.js b/lib/react-native/ParseRole.js new file mode 100644 index 000000000..04164b2e4 --- /dev/null +++ b/lib/react-native/ParseRole.js @@ -0,0 +1,133 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + */ + +import ParseACL from './ParseACL'; +import ParseError from './ParseError'; +import ParseObject from './ParseObject'; + +/** + * Represents a Role on the Parse server. Roles represent groupings of + * Users for the purposes of granting permissions (e.g. specifying an ACL + * for an Object). Roles are specified by their sets of child users and + * child roles, all of which are granted any permissions that the parent + * role has. + * + *Roles must have a name (which cannot be changed after creation of the + * role), and must specify an ACL.
+ * @class Parse.Role + * @constructor + * @param {String} name The name of the Role to create. + * @param {Parse.ACL} acl The ACL for this role. Roles must have an ACL. + * A Parse.Role is a local representation of a role persisted to the Parse + * cloud. + */ +export default class ParseRole extends ParseObject { + constructor(name, acl) { + super('_Role'); + if (typeof name === 'string' && acl instanceof ParseACL) { + this.setName(name); + this.setACL(acl); + } + } + + /** + * Gets the name of the role. You can alternatively call role.get("name") + * + * @method getName + * @return {String} the name of the role. + */ + getName() { + const name = this.get('name'); + if (name == null || typeof name === 'string') { + return name; + } + return ''; + } + + /** + * Sets the name for a role. This value must be set before the role has + * been saved to the server, and cannot be set once the role has been + * saved. + * + *+ * A role's name can only contain alphanumeric characters, _, -, and + * spaces. + *
+ * + *This is equivalent to calling role.set("name", name)
+ * + * @method setName + * @param {String} name The name of the role. + * @param {Object} options Standard options object with success and error + * callbacks. + */ + setName(name, options) { + return this.set('name', name, options); + } + + /** + * Gets the Parse.Relation for the Parse.Users that are direct + * children of this role. These users are granted any privileges that this + * role has been granted (e.g. read or write access through ACLs). You can + * add or remove users from the role through this relation. + * + *This is equivalent to calling role.relation("users")
+ * + * @method getUsers + * @return {Parse.Relation} the relation for the users belonging to this + * role. + */ + getUsers() { + return this.relation('users'); + } + + /** + * Gets the Parse.Relation for the Parse.Roles that are direct + * children of this role. These roles' users are granted any privileges that + * this role has been granted (e.g. read or write access through ACLs). You + * can add or remove child roles from this role through this relation. + * + *This is equivalent to calling role.relation("roles")
+ * + * @method getRoles + * @return {Parse.Relation} the relation for the roles belonging to this + * role. + */ + getRoles() { + return this.relation('roles'); + } + + validate(attrs, options) { + var isInvalid = super.validate(attrs, options); + if (isInvalid) { + return isInvalid; + } + + if ('name' in attrs && attrs.name !== this.getName()) { + var newName = attrs.name; + if (this.id && this.id !== attrs.objectId) { + // Check to see if the objectId being set matches this.id + // This happens during a fetch -- the id is set before calling fetch + // Let the name be set in this case + return new ParseError(ParseError.OTHER_CAUSE, 'A role\'s name can only be set before it has been saved.'); + } + if (typeof newName !== 'string') { + return new ParseError(ParseError.OTHER_CAUSE, 'A role\'s name must be a String.'); + } + if (!/^[0-9a-zA-Z\-_ ]+$/.test(newName)) { + return new ParseError(ParseError.OTHER_CAUSE, 'A role\'s name can be only contain alphanumeric characters, _, ' + '-, and spaces.'); + } + } + return false; + } +} + +ParseObject.registerSubclass('_Role', ParseRole); \ No newline at end of file diff --git a/lib/react-native/ParseSession.js b/lib/react-native/ParseSession.js new file mode 100644 index 000000000..f1554fc9a --- /dev/null +++ b/lib/react-native/ParseSession.js @@ -0,0 +1,114 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + */ + +import CoreManager from './CoreManager'; +import isRevocableSession from './isRevocableSession'; +import ParseObject from './ParseObject'; +import ParsePromise from './ParsePromise'; +import ParseUser from './ParseUser'; + +/** + * @class Parse.Session + * @constructor + * + *A Parse.Session object is a local representation of a revocable session. + * This class is a subclass of a Parse.Object, and retains the same + * functionality of a Parse.Object.
+ */ +export default class ParseSession extends ParseObject { + constructor(attributes) { + super('_Session'); + if (attributes && typeof attributes === 'object') { + if (!this.set(attributes || {})) { + throw new Error('Can\'t create an invalid Session'); + } + } + } + + /** + * Returns the session token string. + * @method getSessionToken + * @return {String} + */ + getSessionToken() { + const token = this.get('sessionToken'); + if (typeof token === 'string') { + return token; + } + return ''; + } + + static readOnlyAttributes() { + return ['createdWith', 'expiresAt', 'installationId', 'restricted', 'sessionToken', 'user']; + } + + /** + * Retrieves the Session object for the currently logged in session. + * @method current + * @static + * @return {Parse.Promise} A promise that is resolved with the Parse.Session + * object after it has been fetched. If there is no current user, the + * promise will be rejected. + */ + static current(options) { + options = options || {}; + var controller = CoreManager.getSessionController(); + + var sessionOptions = {}; + if (options.hasOwnProperty('useMasterKey')) { + sessionOptions.useMasterKey = options.useMasterKey; + } + return ParseUser.currentAsync().then(user => { + if (!user) { + return ParsePromise.error('There is no current user.'); + } + user.getSessionToken(); + + sessionOptions.sessionToken = user.getSessionToken(); + return controller.getSession(sessionOptions); + }); + } + + /** + * Determines whether the current session token is revocable. + * This method is useful for migrating Express.js or Node.js web apps to + * use revocable sessions. If you are migrating an app that uses the Parse + * SDK in the browser only, please use Parse.User.enableRevocableSession() + * instead, so that sessions can be automatically upgraded. + * @method isCurrentSessionRevocable + * @static + * @return {Boolean} + */ + static isCurrentSessionRevocable() { + var currentUser = ParseUser.current(); + if (currentUser) { + return isRevocableSession(currentUser.getSessionToken() || ''); + } + return false; + } +} + +ParseObject.registerSubclass('_Session', ParseSession); + +var DefaultController = { + getSession(options) { + var RESTController = CoreManager.getRESTController(); + var session = new ParseSession(); + + return RESTController.request('GET', 'sessions/me', {}, options).then(sessionData => { + session._finishFetch(sessionData); + session._setExisted(true); + return session; + }); + } +}; + +CoreManager.setSessionController(DefaultController); \ No newline at end of file diff --git a/lib/react-native/ParseUser.js b/lib/react-native/ParseUser.js new file mode 100644 index 000000000..129c4cfbb --- /dev/null +++ b/lib/react-native/ParseUser.js @@ -0,0 +1,952 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + */ + +import CoreManager from './CoreManager'; +import isRevocableSession from './isRevocableSession'; +import ParseError from './ParseError'; +import ParseObject from './ParseObject'; +import ParsePromise from './ParsePromise'; +import ParseSession from './ParseSession'; +import Storage from './Storage'; + +var CURRENT_USER_KEY = 'currentUser'; +var canUseCurrentUser = !CoreManager.get('IS_NODE'); +var currentUserCacheMatchesDisk = false; +var currentUserCache = null; + +var authProviders = {}; + +/** + * @class Parse.User + * @constructor + * + *A Parse.User object is a local representation of a user persisted to the + * Parse cloud. This class is a subclass of a Parse.Object, and retains the + * same functionality of a Parse.Object, but also extends it with various + * user specific methods, like authentication, signing up, and validation of + * uniqueness.
+ */ +export default class ParseUser extends ParseObject { + constructor(attributes) { + super('_User'); + if (attributes && typeof attributes === 'object') { + if (!this.set(attributes || {})) { + throw new Error('Can\'t create an invalid Parse User'); + } + } + } + + /** + * Request a revocable session token to replace the older style of token. + * @method _upgradeToRevocableSession + * @param {Object} options A Backbone-style options object. + * @return {Parse.Promise} A promise that is resolved when the replacement + * token has been fetched. + */ + _upgradeToRevocableSession(options) { + options = options || {}; + + var upgradeOptions = {}; + if (options.hasOwnProperty('useMasterKey')) { + upgradeOptions.useMasterKey = options.useMasterKey; + } + + var controller = CoreManager.getUserController(); + return controller.upgradeToRevocableSession(this, upgradeOptions)._thenRunCallbacks(options); + } + + /** + * Unlike in the Android/iOS SDKs, logInWith is unnecessary, since you can + * call linkWith on the user (even if it doesn't exist yet on the server). + * @method _linkWith + */ + _linkWith(provider, options) { + var authType; + if (typeof provider === 'string') { + authType = provider; + provider = authProviders[provider]; + } else { + authType = provider.getAuthType(); + } + if (options && options.hasOwnProperty('authData')) { + var authData = this.get('authData') || {}; + if (typeof authData !== 'object') { + throw new Error('Invalid type: authData field should be an object'); + } + authData[authType] = options.authData; + + var controller = CoreManager.getUserController(); + return controller.linkWith(this, authData)._thenRunCallbacks(options, this); + } else { + var promise = new ParsePromise(); + provider.authenticate({ + success: (provider, result) => { + var opts = {}; + opts.authData = result; + if (options.success) { + opts.success = options.success; + } + if (options.error) { + opts.error = options.error; + } + this._linkWith(provider, opts).then(() => { + promise.resolve(this); + }, error => { + promise.reject(error); + }); + }, + error: (provider, error) => { + if (typeof options.error === 'function') { + options.error(this, error); + } + promise.reject(error); + } + }); + return promise; + } + } + + /** + * Synchronizes auth data for a provider (e.g. puts the access token in the + * right place to be used by the Facebook SDK). + * @method _synchronizeAuthData + */ + _synchronizeAuthData(provider) { + if (!this.isCurrent() || !provider) { + return; + } + var authType; + if (typeof provider === 'string') { + authType = provider; + provider = authProviders[authType]; + } else { + authType = provider.getAuthType(); + } + var authData = this.get('authData'); + if (!provider || !authData || typeof authData !== 'object') { + return; + } + var success = provider.restoreAuthentication(authData[authType]); + if (!success) { + this._unlinkFrom(provider); + } + } + + /** + * Synchronizes authData for all providers. + * @method _synchronizeAllAuthData + */ + _synchronizeAllAuthData() { + var authData = this.get('authData'); + if (typeof authData !== 'object') { + return; + } + + for (var key in authData) { + this._synchronizeAuthData(key); + } + } + + /** + * Removes null values from authData (which exist temporarily for + * unlinking) + * @method _cleanupAuthData + */ + _cleanupAuthData() { + if (!this.isCurrent()) { + return; + } + var authData = this.get('authData'); + if (typeof authData !== 'object') { + return; + } + + for (var key in authData) { + if (!authData[key]) { + delete authData[key]; + } + } + } + + /** + * Unlinks a user from a service. + * @method _unlinkFrom + */ + _unlinkFrom(provider, options) { + if (typeof provider === 'string') { + provider = authProviders[provider]; + } else { + provider.getAuthType(); + } + return this._linkWith(provider, { authData: null }).then(() => { + this._synchronizeAuthData(provider); + return ParsePromise.as(this); + })._thenRunCallbacks(options); + } + + /** + * Checks whether a user is linked to a service. + * @method _isLinked + */ + _isLinked(provider) { + var authType; + if (typeof provider === 'string') { + authType = provider; + } else { + authType = provider.getAuthType(); + } + var authData = this.get('authData') || {}; + if (typeof authData !== 'object') { + return false; + } + return !!authData[authType]; + } + + /** + * Deauthenticates all providers. + * @method _logOutWithAll + */ + _logOutWithAll() { + var authData = this.get('authData'); + if (typeof authData !== 'object') { + return; + } + + for (var key in authData) { + this._logOutWith(key); + } + } + + /** + * Deauthenticates a single provider (e.g. removing access tokens from the + * Facebook SDK). + * @method _logOutWith + */ + _logOutWith(provider) { + if (!this.isCurrent()) { + return; + } + if (typeof provider === 'string') { + provider = authProviders[provider]; + } + if (provider && provider.deauthenticate) { + provider.deauthenticate(); + } + } + + /** + * Class instance method used to maintain specific keys when a fetch occurs. + * Used to ensure that the session token is not lost. + */ + _preserveFieldsOnFetch() { + return { + sessionToken: this.get('sessionToken') + }; + } + + /** + * Returns true ifcurrent
would return this user.
+ * @method isCurrent
+ * @return {Boolean}
+ */
+ isCurrent() {
+ var current = ParseUser.current();
+ return !!current && current.id === this.id;
+ }
+
+ /**
+ * Returns get("username").
+ * @method getUsername
+ * @return {String}
+ */
+ getUsername() {
+ const username = this.get('username');
+ if (username == null || typeof username === 'string') {
+ return username;
+ }
+ return '';
+ }
+
+ /**
+ * Calls set("username", username, options) and returns the result.
+ * @method setUsername
+ * @param {String} username
+ * @param {Object} options A Backbone-style options object.
+ * @return {Boolean}
+ */
+ setUsername(username) {
+ // Strip anonymity, even we do not support anonymous user in js SDK, we may
+ // encounter anonymous user created by android/iOS in cloud code.
+ var authData = this.get('authData');
+ if (authData && typeof authData === 'object' && authData.hasOwnProperty('anonymous')) {
+ // We need to set anonymous to null instead of deleting it in order to remove it from Parse.
+ authData.anonymous = null;
+ }
+ this.set('username', username);
+ }
+
+ /**
+ * Calls set("password", password, options) and returns the result.
+ * @method setPassword
+ * @param {String} password
+ * @param {Object} options A Backbone-style options object.
+ * @return {Boolean}
+ */
+ setPassword(password) {
+ this.set('password', password);
+ }
+
+ /**
+ * Returns get("email").
+ * @method getEmail
+ * @return {String}
+ */
+ getEmail() {
+ const email = this.get('email');
+ if (email == null || typeof email === 'string') {
+ return email;
+ }
+ return '';
+ }
+
+ /**
+ * Calls set("email", email, options) and returns the result.
+ * @method setEmail
+ * @param {String} email
+ * @param {Object} options A Backbone-style options object.
+ * @return {Boolean}
+ */
+ setEmail(email) {
+ this.set('email', email);
+ }
+
+ /**
+ * Returns the session token for this user, if the user has been logged in,
+ * or if it is the result of a query with the master key. Otherwise, returns
+ * undefined.
+ * @method getSessionToken
+ * @return {String} the session token, or undefined
+ */
+ getSessionToken() {
+ const token = this.get('sessionToken');
+ if (token == null || typeof token === 'string') {
+ return token;
+ }
+ return '';
+ }
+
+ /**
+ * Checks whether this user is the current user and has been authenticated.
+ * @method authenticated
+ * @return (Boolean) whether this user is the current user and is logged in.
+ */
+ authenticated() {
+ var current = ParseUser.current();
+ return !!this.get('sessionToken') && !!current && current.id === this.id;
+ }
+
+ /**
+ * Signs up a new user. You should call this instead of save for
+ * new Parse.Users. This will create a new Parse.User on the server, and
+ * also persist the session on disk so that you can access the user using
+ * current
.
+ *
+ * A username and password must be set before calling signUp.
+ * + *Calls options.success or options.error on completion.
+ * + * @method signUp + * @param {Object} attrs Extra fields to set on the new user, or null. + * @param {Object} options A Backbone-style options object. + * @return {Parse.Promise} A promise that is fulfilled when the signup + * finishes. + */ + signUp(attrs, options) { + options = options || {}; + + var signupOptions = {}; + if (options.hasOwnProperty('useMasterKey')) { + signupOptions.useMasterKey = options.useMasterKey; + } + if (options.hasOwnProperty('installationId')) { + signupOptions.installationId = options.installationId; + } + + var controller = CoreManager.getUserController(); + return controller.signUp(this, attrs, signupOptions)._thenRunCallbacks(options, this); + } + + /** + * Logs in a Parse.User. On success, this saves the session to disk, + * so you can retrieve the currently logged in user using + *current
.
+ *
+ * A username and password must be set before calling logIn.
+ * + *Calls options.success or options.error on completion.
+ * + * @method logIn + * @param {Object} options A Backbone-style options object. + * @return {Parse.Promise} A promise that is fulfilled with the user when + * the login is complete. + */ + logIn(options) { + options = options || {}; + + var loginOptions = {}; + if (options.hasOwnProperty('useMasterKey')) { + loginOptions.useMasterKey = options.useMasterKey; + } + if (options.hasOwnProperty('installationId')) { + loginOptions.installationId = options.installationId; + } + + var controller = CoreManager.getUserController(); + return controller.logIn(this, loginOptions)._thenRunCallbacks(options, this); + } + + /** + * Wrap the default save behavior with functionality to save to local + * storage if this is current user. + */ + save(...args) { + return super.save.apply(this, args).then(() => { + if (this.isCurrent()) { + return CoreManager.getUserController().updateUserOnDisk(this); + } + return this; + }); + } + + /** + * Wrap the default destroy behavior with functionality that logs out + * the current user when it is destroyed + */ + destroy(...args) { + return super.destroy.apply(this, args).then(() => { + if (this.isCurrent()) { + return CoreManager.getUserController().removeUserFromDisk(); + } + return this; + }); + } + + /** + * Wrap the default fetch behavior with functionality to save to local + * storage if this is current user. + */ + fetch(...args) { + return super.fetch.apply(this, args).then(() => { + if (this.isCurrent()) { + return CoreManager.getUserController().updateUserOnDisk(this); + } + return this; + }); + } + + static readOnlyAttributes() { + return ['sessionToken']; + } + + /** + * Adds functionality to the existing Parse.User class + * @method extend + * @param {Object} protoProps A set of properties to add to the prototype + * @param {Object} classProps A set of static properties to add to the class + * @static + * @return {Class} The newly extended Parse.User class + */ + static extend(protoProps, classProps) { + if (protoProps) { + for (var prop in protoProps) { + if (prop !== 'className') { + Object.defineProperty(ParseUser.prototype, prop, { + value: protoProps[prop], + enumerable: false, + writable: true, + configurable: true + }); + } + } + } + + if (classProps) { + for (var prop in classProps) { + if (prop !== 'className') { + Object.defineProperty(ParseUser, prop, { + value: classProps[prop], + enumerable: false, + writable: true, + configurable: true + }); + } + } + } + + return ParseUser; + } + + /** + * Retrieves the currently logged in ParseUser with a valid session, + * either from memory or localStorage, if necessary. + * @method current + * @static + * @return {Parse.Object} The currently logged in Parse.User. + */ + static current() { + if (!canUseCurrentUser) { + return null; + } + var controller = CoreManager.getUserController(); + return controller.currentUser(); + } + + /** + * Retrieves the currently logged in ParseUser from asynchronous Storage. + * @method currentAsync + * @static + * @return {Parse.Promise} A Promise that is resolved with the currently + * logged in Parse User + */ + static currentAsync() { + if (!canUseCurrentUser) { + return ParsePromise.as(null); + } + var controller = CoreManager.getUserController(); + return controller.currentUserAsync(); + } + + /** + * Signs up a new user with a username (or email) and password. + * This will create a new Parse.User on the server, and also persist the + * session in localStorage so that you can access the user using + * {@link #current}. + * + *Calls options.success or options.error on completion.
+ * + * @method signUp + * @param {String} username The username (or email) to sign up with. + * @param {String} password The password to sign up with. + * @param {Object} attrs Extra fields to set on the new user. + * @param {Object} options A Backbone-style options object. + * @static + * @return {Parse.Promise} A promise that is fulfilled with the user when + * the signup completes. + */ + static signUp(username, password, attrs, options) { + attrs = attrs || {}; + attrs.username = username; + attrs.password = password; + var user = new ParseUser(attrs); + return user.signUp({}, options); + } + + /** + * Logs in a user with a username (or email) and password. On success, this + * saves the session to disk, so you can retrieve the currently logged in + * user usingcurrent
.
+ *
+ * Calls options.success or options.error on completion.
+ * + * @method logIn + * @param {String} username The username (or email) to log in with. + * @param {String} password The password to log in with. + * @param {Object} options A Backbone-style options object. + * @static + * @return {Parse.Promise} A promise that is fulfilled with the user when + * the login completes. + */ + static logIn(username, password, options) { + if (typeof username !== 'string') { + return ParsePromise.error(new ParseError(ParseError.OTHER_CAUSE, 'Username must be a string.')); + } else if (typeof password !== 'string') { + return ParsePromise.error(new ParseError(ParseError.OTHER_CAUSE, 'Password must be a string.')); + } + var user = new ParseUser(); + user._finishFetch({ username: username, password: password }); + return user.logIn(options); + } + + /** + * Logs in a user with a session token. On success, this saves the session + * to disk, so you can retrieve the currently logged in user using + *current
.
+ *
+ * Calls options.success or options.error on completion.
+ * + * @method become + * @param {String} sessionToken The sessionToken to log in with. + * @param {Object} options A Backbone-style options object. + * @static + * @return {Parse.Promise} A promise that is fulfilled with the user when + * the login completes. + */ + static become(sessionToken, options) { + if (!canUseCurrentUser) { + throw new Error('It is not memory-safe to become a user in a server environment'); + } + options = options || {}; + + var becomeOptions = { + sessionToken: sessionToken + }; + if (options.hasOwnProperty('useMasterKey')) { + becomeOptions.useMasterKey = options.useMasterKey; + } + + var controller = CoreManager.getUserController(); + return controller.become(becomeOptions)._thenRunCallbacks(options); + } + + static logInWith(provider, options) { + return ParseUser._logInWith(provider, options); + } + + /** + * Logs out the currently logged in user session. This will remove the + * session from disk, log out of linked services, and future calls to + *current
will return null
.
+ * @method logOut
+ * @static
+ * @return {Parse.Promise} A promise that is resolved when the session is
+ * destroyed on the server.
+ */
+ static logOut() {
+ if (!canUseCurrentUser) {
+ throw new Error('There is no current user user on a node.js server environment.');
+ }
+
+ var controller = CoreManager.getUserController();
+ return controller.logOut();
+ }
+
+ /**
+ * Requests a password reset email to be sent to the specified email address
+ * associated with the user account. This email allows the user to securely
+ * reset their password on the Parse site.
+ *
+ * Calls options.success or options.error on completion.
+ * + * @method requestPasswordReset + * @param {String} email The email address associated with the user that + * forgot their password. + * @param {Object} options A Backbone-style options object. + * @static + */ + static requestPasswordReset(email, options) { + options = options || {}; + + var requestOptions = {}; + if (options.hasOwnProperty('useMasterKey')) { + requestOptions.useMasterKey = options.useMasterKey; + } + + var controller = CoreManager.getUserController(); + return controller.requestPasswordReset(email, requestOptions)._thenRunCallbacks(options); + } + + /** + * Allow someone to define a custom User class without className + * being rewritten to _User. The default behavior is to rewrite + * User to _User for legacy reasons. This allows developers to + * override that behavior. + * + * @method allowCustomUserClass + * @param {Boolean} isAllowed Whether or not to allow custom User class + * @static + */ + static allowCustomUserClass(isAllowed) { + CoreManager.set('PERFORM_USER_REWRITE', !isAllowed); + } + + /** + * Allows a legacy application to start using revocable sessions. If the + * current session token is not revocable, a request will be made for a new, + * revocable session. + * It is not necessary to call this method from cloud code unless you are + * handling user signup or login from the server side. In a cloud code call, + * this function will not attempt to upgrade the current token. + * @method enableRevocableSession + * @param {Object} options A Backbone-style options object. + * @static + * @return {Parse.Promise} A promise that is resolved when the process has + * completed. If a replacement session token is requested, the promise + * will be resolved after a new token has been fetched. + */ + static enableRevocableSession(options) { + options = options || {}; + CoreManager.set('FORCE_REVOCABLE_SESSION', true); + if (canUseCurrentUser) { + var current = ParseUser.current(); + if (current) { + return current._upgradeToRevocableSession(options); + } + } + return ParsePromise.as()._thenRunCallbacks(options); + } + + /** + * Enables the use of become or the current user in a server + * environment. These features are disabled by default, since they depend on + * global objects that are not memory-safe for most servers. + * @method enableUnsafeCurrentUser + * @static + */ + static enableUnsafeCurrentUser() { + canUseCurrentUser = true; + } + + /** + * Disables the use of become or the current user in any environment. + * These features are disabled on servers by default, since they depend on + * global objects that are not memory-safe for most servers. + * @method disableUnsafeCurrentUser + * @static + */ + static disableUnsafeCurrentUser() { + canUseCurrentUser = false; + } + + static _registerAuthenticationProvider(provider) { + authProviders[provider.getAuthType()] = provider; + // Synchronize the current user with the auth provider. + ParseUser.currentAsync().then(current => { + if (current) { + current._synchronizeAuthData(provider.getAuthType()); + } + }); + } + + static _logInWith(provider, options) { + var user = new ParseUser(); + return user._linkWith(provider, options); + } + + static _clearCache() { + currentUserCache = null; + currentUserCacheMatchesDisk = false; + } + + static _setCurrentUserCache(user) { + currentUserCache = user; + } +} + +ParseObject.registerSubclass('_User', ParseUser); + +var DefaultController = { + updateUserOnDisk(user) { + var path = Storage.generatePath(CURRENT_USER_KEY); + var json = user.toJSON(); + json.className = '_User'; + return Storage.setItemAsync(path, JSON.stringify(json)).then(() => { + return user; + }); + }, + + removeUserFromDisk() { + let path = Storage.generatePath(CURRENT_USER_KEY); + currentUserCacheMatchesDisk = true; + currentUserCache = null; + return Storage.removeItemAsync(path); + }, + + setCurrentUser(user) { + currentUserCache = user; + user._cleanupAuthData(); + user._synchronizeAllAuthData(); + return DefaultController.updateUserOnDisk(user); + }, + + currentUser() { + if (currentUserCache) { + return currentUserCache; + } + if (currentUserCacheMatchesDisk) { + return null; + } + if (Storage.async()) { + throw new Error('Cannot call currentUser() when using a platform with an async ' + 'storage system. Call currentUserAsync() instead.'); + } + var path = Storage.generatePath(CURRENT_USER_KEY); + var userData = Storage.getItem(path); + currentUserCacheMatchesDisk = true; + if (!userData) { + currentUserCache = null; + return null; + } + userData = JSON.parse(userData); + if (!userData.className) { + userData.className = '_User'; + } + if (userData._id) { + if (userData.objectId !== userData._id) { + userData.objectId = userData._id; + } + delete userData._id; + } + if (userData._sessionToken) { + userData.sessionToken = userData._sessionToken; + delete userData._sessionToken; + } + var current = ParseObject.fromJSON(userData); + currentUserCache = current; + current._synchronizeAllAuthData(); + return current; + }, + + currentUserAsync() { + if (currentUserCache) { + return ParsePromise.as(currentUserCache); + } + if (currentUserCacheMatchesDisk) { + return ParsePromise.as(null); + } + var path = Storage.generatePath(CURRENT_USER_KEY); + return Storage.getItemAsync(path).then(userData => { + currentUserCacheMatchesDisk = true; + if (!userData) { + currentUserCache = null; + return ParsePromise.as(null); + } + userData = JSON.parse(userData); + if (!userData.className) { + userData.className = '_User'; + } + if (userData._id) { + if (userData.objectId !== userData._id) { + userData.objectId = userData._id; + } + delete userData._id; + } + if (userData._sessionToken) { + userData.sessionToken = userData._sessionToken; + delete userData._sessionToken; + } + var current = ParseObject.fromJSON(userData); + currentUserCache = current; + current._synchronizeAllAuthData(); + return ParsePromise.as(current); + }); + }, + + signUp(user, attrs, options) { + var username = attrs && attrs.username || user.get('username'); + var password = attrs && attrs.password || user.get('password'); + + if (!username || !username.length) { + return ParsePromise.error(new ParseError(ParseError.OTHER_CAUSE, 'Cannot sign up user with an empty name.')); + } + if (!password || !password.length) { + return ParsePromise.error(new ParseError(ParseError.OTHER_CAUSE, 'Cannot sign up user with an empty password.')); + } + + return user.save(attrs, options).then(() => { + // Clear the password field + user._finishFetch({ password: undefined }); + + if (canUseCurrentUser) { + return DefaultController.setCurrentUser(user); + } + return user; + }); + }, + + logIn(user, options) { + var RESTController = CoreManager.getRESTController(); + var stateController = CoreManager.getObjectStateController(); + var auth = { + username: user.get('username'), + password: user.get('password') + }; + return RESTController.request('GET', 'login', auth, options).then((response, status) => { + user._migrateId(response.objectId); + user._setExisted(true); + stateController.setPendingOp(user._getStateIdentifier(), 'username', undefined); + stateController.setPendingOp(user._getStateIdentifier(), 'password', undefined); + response.password = undefined; + user._finishFetch(response); + if (!canUseCurrentUser) { + // We can't set the current user, so just return the one we logged in + return ParsePromise.as(user); + } + return DefaultController.setCurrentUser(user); + }); + }, + + become(options) { + var user = new ParseUser(); + var RESTController = CoreManager.getRESTController(); + return RESTController.request('GET', 'users/me', {}, options).then((response, status) => { + user._finishFetch(response); + user._setExisted(true); + return DefaultController.setCurrentUser(user); + }); + }, + + logOut() { + return DefaultController.currentUserAsync().then(currentUser => { + var path = Storage.generatePath(CURRENT_USER_KEY); + var promise = Storage.removeItemAsync(path); + var RESTController = CoreManager.getRESTController(); + if (currentUser !== null) { + var currentSession = currentUser.getSessionToken(); + if (currentSession && isRevocableSession(currentSession)) { + promise = promise.then(() => { + return RESTController.request('POST', 'logout', {}, { sessionToken: currentSession }); + }); + } + currentUser._logOutWithAll(); + currentUser._finishFetch({ sessionToken: undefined }); + } + currentUserCacheMatchesDisk = true; + currentUserCache = null; + + return promise; + }); + }, + + requestPasswordReset(email, options) { + var RESTController = CoreManager.getRESTController(); + return RESTController.request('POST', 'requestPasswordReset', { email: email }, options); + }, + + upgradeToRevocableSession(user, options) { + var token = user.getSessionToken(); + if (!token) { + return ParsePromise.error(new ParseError(ParseError.SESSION_MISSING, 'Cannot upgrade a user with no session token')); + } + + options.sessionToken = token; + + var RESTController = CoreManager.getRESTController(); + return RESTController.request('POST', 'upgradeToRevocableSession', {}, options).then(result => { + var session = new ParseSession(); + session._finishFetch(result); + user._finishFetch({ sessionToken: session.getSessionToken() }); + if (user.isCurrent()) { + return DefaultController.setCurrentUser(user); + } + return ParsePromise.as(user); + }); + }, + + linkWith(user, authData) { + return user.save({ authData }).then(() => { + if (canUseCurrentUser) { + return DefaultController.setCurrentUser(user); + } + return user; + }); + } +}; + +CoreManager.setUserController(DefaultController); \ No newline at end of file diff --git a/lib/react-native/Push.js b/lib/react-native/Push.js new file mode 100644 index 000000000..2710abe4d --- /dev/null +++ b/lib/react-native/Push.js @@ -0,0 +1,77 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + */ + +import CoreManager from './CoreManager'; +import ParseQuery from './ParseQuery'; + +/** + * Contains functions to deal with Push in Parse. + * @class Parse.Push + * @static + */ + +/** + * Sends a push notification. + * @method send + * @param {Object} data - The data of the push notification. Valid fields + * are: + *- * Some functions are only available from Cloud Code. - *
- * - * @class Parse.Cloud - * @static - */ - -/** - * Makes a call to a cloud function. - * @method run - * @param {String} name The function name. - * @param {Object} data The parameters to send to the cloud function. - * @param {Object} options A Backbone-style options object - * options.success, if set, should be a function to handle a successful - * call to a cloud function. options.error should be a function that - * handles an error running the cloud function. Both functions are - * optional. Both functions take a single argument. - * @return {Parse.Promise} A promise that will be resolved with the result - * of the function. - */ -function run(name, data, options) { - options = options || {}; - - if (typeof name !== 'string' || name.length === 0) { - throw new TypeError('Cloud function name must be a string.'); - } - - var requestOptions = {}; - if (options.useMasterKey) { - requestOptions.useMasterKey = options.useMasterKey; - } - if (options.sessionToken) { - requestOptions.sessionToken = options.sessionToken; - } - - return _CoreManager2.default.getCloudController().run(name, data, requestOptions)._thenRunCallbacks(options); -} /** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * - */ - -var DefaultController = { - run: function (name, data, options) { - var RESTController = _CoreManager2.default.getRESTController(); - - var payload = (0, _encode2.default)(data, true); - - var requestOptions = {}; - if (options.hasOwnProperty('useMasterKey')) { - requestOptions.useMasterKey = options.useMasterKey; - } - if (options.hasOwnProperty('sessionToken')) { - requestOptions.sessionToken = options.sessionToken; - } - - var request = RESTController.request('POST', 'functions/' + name, payload, requestOptions); - - return request.then(function (res) { - var decoded = (0, _decode2.default)(res); - if (decoded && decoded.hasOwnProperty('result')) { - return _ParsePromise2.default.as(decoded.result); - } - return _ParsePromise2.default.error(new _ParseError2.default(_ParseError2.default.INVALID_JSON, 'The server returned an invalid response.')); - })._thenRunCallbacks(options); - } -}; - -_CoreManager2.default.setCloudController(DefaultController); -},{"./CoreManager":3,"./ParseError":13,"./ParsePromise":20,"./decode":35,"./encode":36}],3:[function(_dereq_,module,exports){ -(function (process){ -'use strict'; - -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * - */ - -var config = { - // Defaults - IS_NODE: typeof process !== 'undefined' && !!process.versions && !!process.versions.node && !process.versions.electron, - REQUEST_ATTEMPT_LIMIT: 5, - SERVER_URL: 'https://api.parse.com/1', - LIVEQUERY_SERVER_URL: null, - VERSION: 'js' + '1.9.2', - APPLICATION_ID: null, - JAVASCRIPT_KEY: null, - MASTER_KEY: null, - USE_MASTER_KEY: false, - PERFORM_USER_REWRITE: true, - FORCE_REVOCABLE_SESSION: false -}; - -function requireMethods(name, methods, controller) { - methods.forEach(function (func) { - if (typeof controller[func] !== 'function') { - throw new Error(name + ' must implement ' + func + '()'); - } - }); -} - -module.exports = { - get: function (key) { - if (config.hasOwnProperty(key)) { - return config[key]; - } - throw new Error('Configuration key not found: ' + key); - }, - - set: function (key, value) { - config[key] = value; - }, - - /* Specialized Controller Setters/Getters */ - - setAnalyticsController: function (controller) { - requireMethods('AnalyticsController', ['track'], controller); - config['AnalyticsController'] = controller; - }, - getAnalyticsController: function () { - return config['AnalyticsController']; - }, - setCloudController: function (controller) { - requireMethods('CloudController', ['run'], controller); - config['CloudController'] = controller; - }, - getCloudController: function () { - return config['CloudController']; - }, - setConfigController: function (controller) { - requireMethods('ConfigController', ['current', 'get'], controller); - config['ConfigController'] = controller; - }, - getConfigController: function () { - return config['ConfigController']; - }, - setFileController: function (controller) { - requireMethods('FileController', ['saveFile', 'saveBase64'], controller); - config['FileController'] = controller; - }, - getFileController: function () { - return config['FileController']; - }, - setInstallationController: function (controller) { - requireMethods('InstallationController', ['currentInstallationId'], controller); - config['InstallationController'] = controller; - }, - getInstallationController: function () { - return config['InstallationController']; - }, - setObjectController: function (controller) { - requireMethods('ObjectController', ['save', 'fetch', 'destroy'], controller); - config['ObjectController'] = controller; - }, - getObjectController: function () { - return config['ObjectController']; - }, - setObjectStateController: function (controller) { - requireMethods('ObjectStateController', ['getState', 'initializeState', 'removeState', 'getServerData', 'setServerData', 'getPendingOps', 'setPendingOp', 'pushPendingState', 'popPendingState', 'mergeFirstPendingState', 'getObjectCache', 'estimateAttribute', 'estimateAttributes', 'commitServerChanges', 'enqueueTask', 'clearAllState'], controller); - - config['ObjectStateController'] = controller; - }, - getObjectStateController: function () { - return config['ObjectStateController']; - }, - setPushController: function (controller) { - requireMethods('PushController', ['send'], controller); - config['PushController'] = controller; - }, - getPushController: function () { - return config['PushController']; - }, - setQueryController: function (controller) { - requireMethods('QueryController', ['find'], controller); - config['QueryController'] = controller; - }, - getQueryController: function () { - return config['QueryController']; - }, - setRESTController: function (controller) { - requireMethods('RESTController', ['request', 'ajax'], controller); - config['RESTController'] = controller; - }, - getRESTController: function () { - return config['RESTController']; - }, - setSessionController: function (controller) { - requireMethods('SessionController', ['getSession'], controller); - config['SessionController'] = controller; - }, - getSessionController: function () { - return config['SessionController']; - }, - setStorageController: function (controller) { - if (controller.async) { - requireMethods('An async StorageController', ['getItemAsync', 'setItemAsync', 'removeItemAsync'], controller); - } else { - requireMethods('A synchronous StorageController', ['getItem', 'setItem', 'removeItem'], controller); - } - config['StorageController'] = controller; - }, - getStorageController: function () { - return config['StorageController']; - }, - setUserController: function (controller) { - requireMethods('UserController', ['setCurrentUser', 'currentUser', 'currentUserAsync', 'signUp', 'logIn', 'become', 'logOut', 'requestPasswordReset', 'upgradeToRevocableSession', 'linkWith'], controller); - config['UserController'] = controller; - }, - getUserController: function () { - return config['UserController']; - }, - setLiveQueryController: function (controller) { - requireMethods('LiveQueryController', ['subscribe', 'unsubscribe', 'open', 'close'], controller); - config['LiveQueryController'] = controller; - }, - getLiveQueryController: function () { - return config['LiveQueryController']; - }, - setHooksController: function (controller) { - requireMethods('HooksController', ['create', 'get', 'update', 'remove'], controller); - config['HooksController'] = controller; - }, - getHooksController: function () { - return config['HooksController']; - } -}; -}).call(this,_dereq_('_process')) -},{"_process":168}],4:[function(_dereq_,module,exports){ -'use strict'; - -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * This is a simple wrapper to unify EventEmitter implementations across platforms. - */ - -module.exports = _dereq_('events').EventEmitter; -var EventEmitter; -},{"events":169}],5:[function(_dereq_,module,exports){ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _parseDate = _dereq_('./parseDate'); - -var _parseDate2 = _interopRequireDefault(_parseDate); - -var _ParseUser = _dereq_('./ParseUser'); - -var _ParseUser2 = _interopRequireDefault(_ParseUser); - -function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { default: obj }; -} - -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * -weak - */ - -var PUBLIC_KEY = "*"; - -var initialized = false; -var requestedPermissions; -var initOptions; -var provider = { - authenticate: function (options) { - var _this = this; - - if (typeof FB === 'undefined') { - options.error(this, 'Facebook SDK not found.'); - } - FB.login(function (response) { - if (response.authResponse) { - if (options.success) { - options.success(_this, { - id: response.authResponse.userID, - access_token: response.authResponse.accessToken, - expiration_date: new Date(response.authResponse.expiresIn * 1000 + new Date().getTime()).toJSON() - }); - } - } else { - if (options.error) { - options.error(_this, response); - } - } - }, { - scope: requestedPermissions - }); - }, - restoreAuthentication: function (authData) { - if (authData) { - var expiration = (0, _parseDate2.default)(authData.expiration_date); - var expiresIn = expiration ? (expiration.getTime() - new Date().getTime()) / 1000 : 0; - - var authResponse = { - userID: authData.id, - accessToken: authData.access_token, - expiresIn: expiresIn - }; - var newOptions = {}; - if (initOptions) { - for (var key in initOptions) { - newOptions[key] = initOptions[key]; - } - } - newOptions.authResponse = authResponse; - - // Suppress checks for login status from the browser. - newOptions.status = false; - - // If the user doesn't match the one known by the FB SDK, log out. - // Most of the time, the users will match -- it's only in cases where - // the FB SDK knows of a different user than the one being restored - // from a Parse User that logged in with username/password. - var existingResponse = FB.getAuthResponse(); - if (existingResponse && existingResponse.userID !== authResponse.userID) { - FB.logout(); - } - - FB.init(newOptions); - } - return true; - }, - getAuthType: function () { - return 'facebook'; - }, - deauthenticate: function () { - this.restoreAuthentication(null); - } -}; - -/** - * Provides a set of utilities for using Parse with Facebook. - * @class Parse.FacebookUtils - * @static - */ -var FacebookUtils = { - /** - * Initializes Parse Facebook integration. Call this function after you - * have loaded the Facebook Javascript SDK with the same parameters - * as you would pass to
- *
- * FB.init()
. Parse.FacebookUtils will invoke FB.init() for you
- * with these arguments.
- *
- * @method init
- * @param {Object} options Facebook options argument as described here:
- *
- * FB.init(). The status flag will be coerced to 'false' because it
- * interferes with Parse Facebook integration. Call FB.getLoginStatus()
- * explicitly if this behavior is required by your application.
- */
- init: function (options) {
- if (typeof FB === 'undefined') {
- throw new Error('The Facebook JavaScript SDK must be loaded before calling init.');
- }
- initOptions = {};
- if (options) {
- for (var key in options) {
- initOptions[key] = options[key];
- }
- }
- if (initOptions.status && typeof console !== 'undefined') {
- var warn = console.warn || console.log || function () {};
- warn.call(console, 'The "status" flag passed into' + ' FB.init, when set to true, can interfere with Parse Facebook' + ' integration, so it has been suppressed. Please call' + ' FB.getLoginStatus() explicitly if you require this behavior.');
- }
- initOptions.status = false;
- FB.init(initOptions);
- _ParseUser2.default._registerAuthenticationProvider(provider);
- initialized = true;
- },
-
- /**
- * Gets whether the user has their account linked to Facebook.
- *
- * @method isLinked
- * @param {Parse.User} user User to check for a facebook link.
- * The user must be logged in on this device.
- * @return {Boolean} true
if the user has their account
- * linked to Facebook.
- */
- isLinked: function (user) {
- return user._isLinked('facebook');
- },
-
- /**
- * Logs in a user using Facebook. This method delegates to the Facebook
- * SDK to authenticate the user, and then automatically logs in (or
- * creates, in the case where it is a new user) a Parse.User.
- *
- * @method logIn
- * @param {String, Object} permissions The permissions required for Facebook
- * log in. This is a comma-separated string of permissions.
- * Alternatively, supply a Facebook authData object as described in our
- * REST API docs if you want to handle getting facebook auth tokens
- * yourself.
- * @param {Object} options Standard options object with success and error
- * callbacks.
- */
- logIn: function (permissions, options) {
- if (!permissions || typeof permissions === 'string') {
- if (!initialized) {
- throw new Error('You must initialize FacebookUtils before calling logIn.');
- }
- requestedPermissions = permissions;
- return _ParseUser2.default._logInWith('facebook', options);
- } else {
- var newOptions = {};
- if (options) {
- for (var key in options) {
- newOptions[key] = options[key];
- }
- }
- newOptions.authData = permissions;
- return _ParseUser2.default._logInWith('facebook', newOptions);
- }
- },
-
- /**
- * Links Facebook to an existing PFUser. This method delegates to the
- * Facebook SDK to authenticate the user, and then automatically links
- * the account to the Parse.User.
- *
- * @method link
- * @param {Parse.User} user User to link to Facebook. This must be the
- * current user.
- * @param {String, Object} permissions The permissions required for Facebook
- * log in. This is a comma-separated string of permissions.
- * Alternatively, supply a Facebook authData object as described in our
- * REST API docs if you want to handle getting facebook auth tokens
- * yourself.
- * @param {Object} options Standard options object with success and error
- * callbacks.
- */
- link: function (user, permissions, options) {
- if (!permissions || typeof permissions === 'string') {
- if (!initialized) {
- throw new Error('You must initialize FacebookUtils before calling link.');
- }
- requestedPermissions = permissions;
- return user._linkWith('facebook', options);
- } else {
- var newOptions = {};
- if (options) {
- for (var key in options) {
- newOptions[key] = options[key];
- }
- }
- newOptions.authData = permissions;
- return user._linkWith('facebook', newOptions);
- }
- },
-
- /**
- * Unlinks the Parse.User from a Facebook account.
- *
- * @method unlink
- * @param {Parse.User} user User to unlink from Facebook. This must be the
- * current user.
- * @param {Object} options Standard options object with success and error
- * callbacks.
- */
- unlink: function (user, options) {
- if (!initialized) {
- throw new Error('You must initialize FacebookUtils before calling unlink.');
- }
- return user._unlinkFrom('facebook', options);
- }
-};
-
-exports.default = FacebookUtils;
-},{"./ParseUser":25,"./parseDate":40}],6:[function(_dereq_,module,exports){
-'use strict';
-
-var _CoreManager = _dereq_('./CoreManager');
-
-var _CoreManager2 = _interopRequireDefault(_CoreManager);
-
-var _ParsePromise = _dereq_('./ParsePromise');
-
-var _ParsePromise2 = _interopRequireDefault(_ParsePromise);
-
-var _Storage = _dereq_('./Storage');
-
-var _Storage2 = _interopRequireDefault(_Storage);
-
-function _interopRequireDefault(obj) {
- return obj && obj.__esModule ? obj : { default: obj };
-}
-
-var iidCache = null; /**
- * Copyright (c) 2015-present, Parse, LLC.
- * All rights reserved.
- *
- * This source code is licensed under the BSD-style license found in the
- * LICENSE file in the root directory of this source tree. An additional grant
- * of patent rights can be found in the PATENTS file in the same directory.
- *
- *
- */
-
-function hexOctet() {
- return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
-}
-
-function generateId() {
- return hexOctet() + hexOctet() + '-' + hexOctet() + '-' + hexOctet() + '-' + hexOctet() + '-' + hexOctet() + hexOctet() + hexOctet();
-}
-
-var InstallationController = {
- currentInstallationId: function () {
- if (typeof iidCache === 'string') {
- return _ParsePromise2.default.as(iidCache);
- }
- var path = _Storage2.default.generatePath('installationId');
- return _Storage2.default.getItemAsync(path).then(function (iid) {
- if (!iid) {
- iid = generateId();
- return _Storage2.default.setItemAsync(path, iid).then(function () {
- iidCache = iid;
- return iid;
- });
- }
- iidCache = iid;
- return iid;
- });
- },
- _clearCache: function () {
- iidCache = null;
- },
- _setInstallationIdCache: function (iid) {
- iidCache = iid;
- }
-};
-
-module.exports = InstallationController;
-},{"./CoreManager":3,"./ParsePromise":20,"./Storage":29}],7:[function(_dereq_,module,exports){
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-
-var _typeof2 = _dereq_('babel-runtime/helpers/typeof');
-
-var _typeof3 = _interopRequireDefault(_typeof2);
-
-var _getIterator2 = _dereq_('babel-runtime/core-js/get-iterator');
-
-var _getIterator3 = _interopRequireDefault(_getIterator2);
-
-var _stringify = _dereq_('babel-runtime/core-js/json/stringify');
-
-var _stringify2 = _interopRequireDefault(_stringify);
-
-var _map = _dereq_('babel-runtime/core-js/map');
-
-var _map2 = _interopRequireDefault(_map);
-
-var _getPrototypeOf = _dereq_('babel-runtime/core-js/object/get-prototype-of');
-
-var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
-
-var _classCallCheck2 = _dereq_('babel-runtime/helpers/classCallCheck');
-
-var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
-
-var _createClass2 = _dereq_('babel-runtime/helpers/createClass');
-
-var _createClass3 = _interopRequireDefault(_createClass2);
-
-var _possibleConstructorReturn2 = _dereq_('babel-runtime/helpers/possibleConstructorReturn');
-
-var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
-
-var _inherits2 = _dereq_('babel-runtime/helpers/inherits');
-
-var _inherits3 = _interopRequireDefault(_inherits2);
-
-var _EventEmitter2 = _dereq_('./EventEmitter');
-
-var _EventEmitter3 = _interopRequireDefault(_EventEmitter2);
-
-var _ParsePromise = _dereq_('./ParsePromise');
-
-var _ParsePromise2 = _interopRequireDefault(_ParsePromise);
-
-var _ParseObject = _dereq_('./ParseObject');
-
-var _ParseObject2 = _interopRequireDefault(_ParseObject);
-
-var _LiveQuerySubscription = _dereq_('./LiveQuerySubscription');
-
-var _LiveQuerySubscription2 = _interopRequireDefault(_LiveQuerySubscription);
-
-function _interopRequireDefault(obj) {
- return obj && obj.__esModule ? obj : { default: obj };
-}
-
-// The LiveQuery client inner state
-/**
- * Copyright (c) 2015-present, Parse, LLC.
- * All rights reserved.
- *
- * This source code is licensed under the BSD-style license found in the
- * LICENSE file in the root directory of this source tree. An additional grant
- * of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-var CLIENT_STATE = {
- INITIALIZED: 'initialized',
- CONNECTING: 'connecting',
- CONNECTED: 'connected',
- CLOSED: 'closed',
- RECONNECTING: 'reconnecting',
- DISCONNECTED: 'disconnected'
-};
-
-// The event type the LiveQuery client should sent to server
-var OP_TYPES = {
- CONNECT: 'connect',
- SUBSCRIBE: 'subscribe',
- UNSUBSCRIBE: 'unsubscribe',
- ERROR: 'error'
-};
-
-// The event we get back from LiveQuery server
-var OP_EVENTS = {
- CONNECTED: 'connected',
- SUBSCRIBED: 'subscribed',
- UNSUBSCRIBED: 'unsubscribed',
- ERROR: 'error',
- CREATE: 'create',
- UPDATE: 'update',
- ENTER: 'enter',
- LEAVE: 'leave',
- DELETE: 'delete'
-};
-
-// The event the LiveQuery client should emit
-var CLIENT_EMMITER_TYPES = {
- CLOSE: 'close',
- ERROR: 'error',
- OPEN: 'open'
-};
-
-// The event the LiveQuery subscription should emit
-var SUBSCRIPTION_EMMITER_TYPES = {
- OPEN: 'open',
- CLOSE: 'close',
- ERROR: 'error',
- CREATE: 'create',
- UPDATE: 'update',
- ENTER: 'enter',
- LEAVE: 'leave',
- DELETE: 'delete'
-};
-
-var generateInterval = function (k) {
- return Math.random() * Math.min(30, Math.pow(2, k) - 1) * 1000;
-};
-
-/**
- * Creates a new LiveQueryClient.
- * Extends events.EventEmitter
- * cloud functions.
- *
- * A wrapper of a standard WebSocket client. We add several useful methods to
- * help you connect/disconnect to LiveQueryServer, subscribe/unsubscribe a ParseQuery easily.
- *
- * javascriptKey and masterKey are used for verifying the LiveQueryClient when it tries
- * to connect to the LiveQuery server
- *
- * @class Parse.LiveQueryClient
- * @constructor
- * @param {Object} options
- * @param {string} options.applicationId - applicationId of your Parse app
- * @param {string} options.serverURL - the URL of your LiveQuery server
- * @param {string} options.javascriptKey (optional)
- * @param {string} options.masterKey (optional) Your Parse Master Key. (Node.js only!)
- * @param {string} options.sessionToken (optional)
- *
- *
- * We expose three events to help you monitor the status of the LiveQueryClient.
- *
- * - * let Parse = require('parse/node'); - * let LiveQueryClient = Parse.LiveQueryClient; - * let client = new LiveQueryClient({ - * applicationId: '', - * serverURL: '', - * javascriptKey: '', - * masterKey: '' - * }); - *- * - * Open - When we establish the WebSocket connection to the LiveQuery server, you'll get this event. - *
- * client.on('open', () => { - * - * });- * - * Close - When we lose the WebSocket connection to the LiveQuery server, you'll get this event. - *
- * client.on('close', () => { - * - * });- * - * Error - When some network error or LiveQuery server error happens, you'll get this event. - *
- * client.on('error', (error) => { - * - * });- * - * - */ - -var LiveQueryClient = function (_EventEmitter) { - (0, _inherits3.default)(LiveQueryClient, _EventEmitter); - - function LiveQueryClient(_ref) { - var applicationId = _ref.applicationId, - serverURL = _ref.serverURL, - javascriptKey = _ref.javascriptKey, - masterKey = _ref.masterKey, - sessionToken = _ref.sessionToken; - (0, _classCallCheck3.default)(this, LiveQueryClient); - - var _this = (0, _possibleConstructorReturn3.default)(this, (LiveQueryClient.__proto__ || (0, _getPrototypeOf2.default)(LiveQueryClient)).call(this)); - - if (!serverURL || serverURL.indexOf('ws') !== 0) { - throw new Error('You need to set a proper Parse LiveQuery server url before using LiveQueryClient'); - } - - _this.reconnectHandle = null; - _this.attempts = 1;; - _this.id = 0; - _this.requestId = 1; - _this.serverURL = serverURL; - _this.applicationId = applicationId; - _this.javascriptKey = javascriptKey; - _this.masterKey = masterKey; - _this.sessionToken = sessionToken; - _this.connectPromise = new _ParsePromise2.default(); - _this.subscriptions = new _map2.default(); - _this.state = CLIENT_STATE.INITIALIZED; - return _this; - } - - (0, _createClass3.default)(LiveQueryClient, [{ - key: 'shouldOpen', - value: function () { - return this.state === CLIENT_STATE.INITIALIZED || this.state === CLIENT_STATE.DISCONNECTED; - } - - /** - * Subscribes to a ParseQuery - * - * If you provide the sessionToken, when the LiveQuery server gets ParseObject's - * updates from parse server, it'll try to check whether the sessionToken fulfills - * the ParseObject's ACL. The LiveQuery server will only send updates to clients whose - * sessionToken is fit for the ParseObject's ACL. You can check the LiveQuery protocol - * here for more details. The subscription you get is the same subscription you get - * from our Standard API. - * - * @method subscribe - * @param {Object} query - the ParseQuery you want to subscribe to - * @param {string} sessionToken (optional) - * @return {Object} subscription - */ - - }, { - key: 'subscribe', - value: function (query, sessionToken) { - var _this2 = this; - - if (!query) { - return; - } - var where = query.toJSON().where; - var className = query.className; - var subscribeRequest = { - op: OP_TYPES.SUBSCRIBE, - requestId: this.requestId, - query: { - className: className, - where: where - } - }; - - if (sessionToken) { - subscribeRequest.sessionToken = sessionToken; - } - - var subscription = new _LiveQuerySubscription2.default(this.requestId, query, sessionToken); - this.subscriptions.set(this.requestId, subscription); - this.requestId += 1; - this.connectPromise.then(function () { - _this2.socket.send((0, _stringify2.default)(subscribeRequest)); - }); - - // adding listener so process does not crash - // best practice is for developer to register their own listener - subscription.on('error', function () {}); - - return subscription; - } - - /** - * After calling unsubscribe you'll stop receiving events from the subscription object. - * - * @method unsubscribe - * @param {Object} subscription - subscription you would like to unsubscribe from. - */ - - }, { - key: 'unsubscribe', - value: function (subscription) { - var _this3 = this; - - if (!subscription) { - return; - } - - this.subscriptions.delete(subscription.id); - var unsubscribeRequest = { - op: OP_TYPES.UNSUBSCRIBE, - requestId: subscription.id - }; - this.connectPromise.then(function () { - _this3.socket.send((0, _stringify2.default)(unsubscribeRequest)); - }); - } - - /** - * After open is called, the LiveQueryClient will try to send a connect request - * to the LiveQuery server. - * - * @method open - */ - - }, { - key: 'open', - value: function () { - var _this4 = this; - - var WebSocketImplementation = this._getWebSocketImplementation(); - if (!WebSocketImplementation) { - this.emit(CLIENT_EMMITER_TYPES.ERROR, 'Can not find WebSocket implementation'); - return; - } - - if (this.state !== CLIENT_STATE.RECONNECTING) { - this.state = CLIENT_STATE.CONNECTING; - } - - // Get WebSocket implementation - this.socket = new WebSocketImplementation(this.serverURL); - - // Bind WebSocket callbacks - this.socket.onopen = function () { - _this4._handleWebSocketOpen(); - }; - - this.socket.onmessage = function (event) { - _this4._handleWebSocketMessage(event); - }; - - this.socket.onclose = function () { - _this4._handleWebSocketClose(); - }; - - this.socket.onerror = function (error) { - _this4._handleWebSocketError(error); - }; - } - }, { - key: 'resubscribe', - value: function () { - var _this5 = this; - - this.subscriptions.forEach(function (subscription, requestId) { - var query = subscription.query; - var where = query.toJSON().where; - var className = query.className; - var sessionToken = subscription.sessionToken; - var subscribeRequest = { - op: OP_TYPES.SUBSCRIBE, - requestId: requestId, - query: { - className: className, - where: where - } - }; - - if (sessionToken) { - subscribeRequest.sessionToken = sessionToken; - } - - _this5.connectPromise.then(function () { - _this5.socket.send((0, _stringify2.default)(subscribeRequest)); - }); - }); - } - - /** - * This method will close the WebSocket connection to this LiveQueryClient, - * cancel the auto reconnect and unsubscribe all subscriptions based on it. - * - * @method close - */ - - }, { - key: 'close', - value: function () { - if (this.state === CLIENT_STATE.INITIALIZED || this.state === CLIENT_STATE.DISCONNECTED) { - return; - } - this.state = CLIENT_STATE.DISCONNECTED; - this.socket.close(); - // Notify each subscription about the close - var _iteratorNormalCompletion = true; - var _didIteratorError = false; - var _iteratorError = undefined; - - try { - for (var _iterator = (0, _getIterator3.default)(this.subscriptions.values()), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { - var subscription = _step.value; - - subscription.emit(SUBSCRIPTION_EMMITER_TYPES.CLOSE); - } - } catch (err) { - _didIteratorError = true; - _iteratorError = err; - } finally { - try { - if (!_iteratorNormalCompletion && _iterator.return) { - _iterator.return(); - } - } finally { - if (_didIteratorError) { - throw _iteratorError; - } - } - } - - this._handleReset(); - this.emit(CLIENT_EMMITER_TYPES.CLOSE); - } - }, { - key: '_getWebSocketImplementation', - value: function () { - return typeof WebSocket === 'function' || (typeof WebSocket === 'undefined' ? 'undefined' : (0, _typeof3.default)(WebSocket)) === 'object' ? WebSocket : null; - } - - // ensure we start with valid state if connect is called again after close - - }, { - key: '_handleReset', - value: function () { - this.attempts = 1;; - this.id = 0; - this.requestId = 1; - this.connectPromise = new _ParsePromise2.default(); - this.subscriptions = new _map2.default(); - } - }, { - key: '_handleWebSocketOpen', - value: function () { - this.attempts = 1; - var connectRequest = { - op: OP_TYPES.CONNECT, - applicationId: this.applicationId, - javascriptKey: this.javascriptKey, - masterKey: this.masterKey, - sessionToken: this.sessionToken - }; - this.socket.send((0, _stringify2.default)(connectRequest)); - } - }, { - key: '_handleWebSocketMessage', - value: function (event) { - var data = event.data; - if (typeof data === 'string') { - data = JSON.parse(data); - } - var subscription = null; - if (data.requestId) { - subscription = this.subscriptions.get(data.requestId); - } - switch (data.op) { - case OP_EVENTS.CONNECTED: - if (this.state === CLIENT_STATE.RECONNECTING) { - this.resubscribe(); - } - this.emit(CLIENT_EMMITER_TYPES.OPEN); - this.id = data.clientId; - this.connectPromise.resolve(); - this.state = CLIENT_STATE.CONNECTED; - break; - case OP_EVENTS.SUBSCRIBED: - if (subscription) { - subscription.emit(SUBSCRIPTION_EMMITER_TYPES.OPEN); - } - break; - case OP_EVENTS.ERROR: - if (data.requestId) { - if (subscription) { - subscription.emit(SUBSCRIPTION_EMMITER_TYPES.ERROR, data.error); - } - } else { - this.emit(CLIENT_EMMITER_TYPES.ERROR, data.error); - } - break; - case OP_EVENTS.UNSUBSCRIBED: - // We have already deleted subscription in unsubscribe(), do nothing here - break; - default: - // create, update, enter, leave, delete cases - var className = data.object.className; - // Delete the extrea __type and className fields during transfer to full JSON - delete data.object.__type; - delete data.object.className; - var parseObject = new _ParseObject2.default(className); - parseObject._finishFetch(data.object); - if (!subscription) { - break; - } - subscription.emit(data.op, parseObject); - } - } - }, { - key: '_handleWebSocketClose', - value: function () { - if (this.state === CLIENT_STATE.DISCONNECTED) { - return; - } - this.state = CLIENT_STATE.CLOSED; - this.emit(CLIENT_EMMITER_TYPES.CLOSE); - // Notify each subscription about the close - var _iteratorNormalCompletion2 = true; - var _didIteratorError2 = false; - var _iteratorError2 = undefined; - - try { - for (var _iterator2 = (0, _getIterator3.default)(this.subscriptions.values()), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { - var subscription = _step2.value; - - subscription.emit(SUBSCRIPTION_EMMITER_TYPES.CLOSE); - } - } catch (err) { - _didIteratorError2 = true; - _iteratorError2 = err; - } finally { - try { - if (!_iteratorNormalCompletion2 && _iterator2.return) { - _iterator2.return(); - } - } finally { - if (_didIteratorError2) { - throw _iteratorError2; - } - } - } - - this._handleReconnect(); - } - }, { - key: '_handleWebSocketError', - value: function (error) { - this.emit(CLIENT_EMMITER_TYPES.ERROR, error); - var _iteratorNormalCompletion3 = true; - var _didIteratorError3 = false; - var _iteratorError3 = undefined; - - try { - for (var _iterator3 = (0, _getIterator3.default)(this.subscriptions.values()), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) { - var subscription = _step3.value; - - subscription.emit(SUBSCRIPTION_EMMITER_TYPES.ERROR); - } - } catch (err) { - _didIteratorError3 = true; - _iteratorError3 = err; - } finally { - try { - if (!_iteratorNormalCompletion3 && _iterator3.return) { - _iterator3.return(); - } - } finally { - if (_didIteratorError3) { - throw _iteratorError3; - } - } - } - - this._handleReconnect(); - } - }, { - key: '_handleReconnect', - value: function () { - var _this6 = this; - - // if closed or currently reconnecting we stop attempting to reconnect - if (this.state === CLIENT_STATE.DISCONNECTED) { - return; - } - - this.state = CLIENT_STATE.RECONNECTING; - var time = generateInterval(this.attempts); - - // handle case when both close/error occur at frequent rates we ensure we do not reconnect unnecessarily. - // we're unable to distinguish different between close/error when we're unable to reconnect therefore - // we try to reonnect in both cases - // server side ws and browser WebSocket behave differently in when close/error get triggered - - if (this.reconnectHandle) { - clearTimeout(this.reconnectHandle); - } - - this.reconnectHandle = setTimeout(function () { - _this6.attempts++; - _this6.connectPromise = new _ParsePromise2.default(); - _this6.open(); - }.bind(this), time); - } - }]); - return LiveQueryClient; -}(_EventEmitter3.default); - -exports.default = LiveQueryClient; -},{"./EventEmitter":4,"./LiveQuerySubscription":8,"./ParseObject":18,"./ParsePromise":20,"babel-runtime/core-js/get-iterator":43,"babel-runtime/core-js/json/stringify":44,"babel-runtime/core-js/map":45,"babel-runtime/core-js/object/get-prototype-of":50,"babel-runtime/helpers/classCallCheck":56,"babel-runtime/helpers/createClass":57,"babel-runtime/helpers/inherits":59,"babel-runtime/helpers/possibleConstructorReturn":60,"babel-runtime/helpers/typeof":61}],8:[function(_dereq_,module,exports){ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _getPrototypeOf = _dereq_('babel-runtime/core-js/object/get-prototype-of'); - -var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); - -var _classCallCheck2 = _dereq_('babel-runtime/helpers/classCallCheck'); - -var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); - -var _createClass2 = _dereq_('babel-runtime/helpers/createClass'); - -var _createClass3 = _interopRequireDefault(_createClass2); - -var _possibleConstructorReturn2 = _dereq_('babel-runtime/helpers/possibleConstructorReturn'); - -var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); - -var _inherits2 = _dereq_('babel-runtime/helpers/inherits'); - -var _inherits3 = _interopRequireDefault(_inherits2); - -var _EventEmitter2 = _dereq_('./EventEmitter'); - -var _EventEmitter3 = _interopRequireDefault(_EventEmitter2); - -var _CoreManager = _dereq_('./CoreManager'); - -var _CoreManager2 = _interopRequireDefault(_CoreManager); - -function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { default: obj }; -} - -/** - * Creates a new LiveQuery Subscription. - * Extends events.EventEmitter - * cloud functions. - * - * @constructor - * @param {string} id - subscription id - * @param {string} query - query to subscribe to - * @param {string} sessionToken - optional session token - * - *
Open Event - When you call query.subscribe(), we send a subscribe request to - * the LiveQuery server, when we get the confirmation from the LiveQuery server, - * this event will be emitted. When the client loses WebSocket connection to the - * LiveQuery server, we will try to auto reconnect the LiveQuery server. If we - * reconnect the LiveQuery server and successfully resubscribe the ParseQuery, - * you'll also get this event. - * - *
- * subscription.on('open', () => { - * - * });- * - *
Create Event - When a new ParseObject is created and it fulfills the ParseQuery you subscribe, - * you'll get this event. The object is the ParseObject which is created. - * - *
- * subscription.on('create', (object) => { - * - * });- * - *
Update Event - When an existing ParseObject which fulfills the ParseQuery you subscribe - * is updated (The ParseObject fulfills the ParseQuery before and after changes), - * you'll get this event. The object is the ParseObject which is updated. - * Its content is the latest value of the ParseObject. - * - *
- * subscription.on('update', (object) => { - * - * });- * - *
Enter Event - When an existing ParseObject's old value doesn't fulfill the ParseQuery - * but its new value fulfills the ParseQuery, you'll get this event. The object is the - * ParseObject which enters the ParseQuery. Its content is the latest value of the ParseObject. - * - *
- * subscription.on('enter', (object) => { - * - * });- * - * - *
Update Event - When an existing ParseObject's old value fulfills the ParseQuery but its new value - * doesn't fulfill the ParseQuery, you'll get this event. The object is the ParseObject - * which leaves the ParseQuery. Its content is the latest value of the ParseObject. - * - *
- * subscription.on('leave', (object) => { - * - * });- * - * - *
Delete Event - When an existing ParseObject which fulfills the ParseQuery is deleted, you'll - * get this event. The object is the ParseObject which is deleted. - * - *
- * subscription.on('delete', (object) => { - * - * });- * - * - *
Close Event - When the client loses the WebSocket connection to the LiveQuery - * server and we stop receiving events, you'll get this event. - * - *
- * subscription.on('close', () => { - * - * });- * - * - */ -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -var Subscription = function (_EventEmitter) { - (0, _inherits3.default)(Subscription, _EventEmitter); - - function Subscription(id, query, sessionToken) { - (0, _classCallCheck3.default)(this, Subscription); - - var _this2 = (0, _possibleConstructorReturn3.default)(this, (Subscription.__proto__ || (0, _getPrototypeOf2.default)(Subscription)).call(this)); - - _this2.id = id; - _this2.query = query; - _this2.sessionToken = sessionToken; - return _this2; - } - - /** - * @method unsubscribe - */ - - (0, _createClass3.default)(Subscription, [{ - key: 'unsubscribe', - value: function () { - var _this3 = this; - - var _this = this; - _CoreManager2.default.getLiveQueryController().getDefaultLiveQueryClient().then(function (liveQueryClient) { - liveQueryClient.unsubscribe(_this); - _this.emit('close'); - _this3.resolve(); - }); - } - }]); - return Subscription; -}(_EventEmitter3.default); - -exports.default = Subscription; -},{"./CoreManager":3,"./EventEmitter":4,"babel-runtime/core-js/object/get-prototype-of":50,"babel-runtime/helpers/classCallCheck":56,"babel-runtime/helpers/createClass":57,"babel-runtime/helpers/inherits":59,"babel-runtime/helpers/possibleConstructorReturn":60}],9:[function(_dereq_,module,exports){ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _stringify = _dereq_('babel-runtime/core-js/json/stringify'); - -var _stringify2 = _interopRequireDefault(_stringify); - -var _typeof2 = _dereq_('babel-runtime/helpers/typeof'); - -var _typeof3 = _interopRequireDefault(_typeof2); - -exports.defaultState = defaultState; -exports.setServerData = setServerData; -exports.setPendingOp = setPendingOp; -exports.pushPendingState = pushPendingState; -exports.popPendingState = popPendingState; -exports.mergeFirstPendingState = mergeFirstPendingState; -exports.estimateAttribute = estimateAttribute; -exports.estimateAttributes = estimateAttributes; -exports.commitServerChanges = commitServerChanges; - -var _encode = _dereq_('./encode'); - -var _encode2 = _interopRequireDefault(_encode); - -var _ParseFile = _dereq_('./ParseFile'); - -var _ParseFile2 = _interopRequireDefault(_ParseFile); - -var _ParseObject = _dereq_('./ParseObject'); - -var _ParseObject2 = _interopRequireDefault(_ParseObject); - -var _ParsePromise = _dereq_('./ParsePromise'); - -var _ParsePromise2 = _interopRequireDefault(_ParsePromise); - -var _ParseRelation = _dereq_('./ParseRelation'); - -var _ParseRelation2 = _interopRequireDefault(_ParseRelation); - -var _TaskQueue = _dereq_('./TaskQueue'); - -var _TaskQueue2 = _interopRequireDefault(_TaskQueue); - -var _ParseOp = _dereq_('./ParseOp'); - -function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { default: obj }; -} - -function defaultState() { - return { - serverData: {}, - pendingOps: [{}], - objectCache: {}, - tasks: new _TaskQueue2.default(), - existed: false - }; -} /** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * - */ - -function setServerData(serverData, attributes) { - for (var _attr in attributes) { - if (typeof attributes[_attr] !== 'undefined') { - serverData[_attr] = attributes[_attr]; - } else { - delete serverData[_attr]; - } - } -} - -function setPendingOp(pendingOps, attr, op) { - var last = pendingOps.length - 1; - if (op) { - pendingOps[last][attr] = op; - } else { - delete pendingOps[last][attr]; - } -} - -function pushPendingState(pendingOps) { - pendingOps.push({}); -} - -function popPendingState(pendingOps) { - var first = pendingOps.shift(); - if (!pendingOps.length) { - pendingOps[0] = {}; - } - return first; -} - -function mergeFirstPendingState(pendingOps) { - var first = popPendingState(pendingOps); - var next = pendingOps[0]; - for (var _attr2 in first) { - if (next[_attr2] && first[_attr2]) { - var merged = next[_attr2].mergeWith(first[_attr2]); - if (merged) { - next[_attr2] = merged; - } - } else { - next[_attr2] = first[_attr2]; - } - } -} - -function estimateAttribute(serverData, pendingOps, className, id, attr) { - var value = serverData[attr]; - for (var i = 0; i < pendingOps.length; i++) { - if (pendingOps[i][attr]) { - if (pendingOps[i][attr] instanceof _ParseOp.RelationOp) { - if (id) { - value = pendingOps[i][attr].applyTo(value, { className: className, id: id }, attr); - } - } else { - value = pendingOps[i][attr].applyTo(value); - } - } - } - return value; -} - -function estimateAttributes(serverData, pendingOps, className, id) { - var data = {}; - var attr = void 0; - for (attr in serverData) { - data[attr] = serverData[attr]; - } - for (var i = 0; i < pendingOps.length; i++) { - for (attr in pendingOps[i]) { - if (pendingOps[i][attr] instanceof _ParseOp.RelationOp) { - if (id) { - data[attr] = pendingOps[i][attr].applyTo(data[attr], { className: className, id: id }, attr); - } - } else { - data[attr] = pendingOps[i][attr].applyTo(data[attr]); - } - } - } - return data; -} - -function commitServerChanges(serverData, objectCache, changes) { - for (var _attr3 in changes) { - var val = changes[_attr3]; - serverData[_attr3] = val; - if (val && (typeof val === 'undefined' ? 'undefined' : (0, _typeof3.default)(val)) === 'object' && !(val instanceof _ParseObject2.default) && !(val instanceof _ParseFile2.default) && !(val instanceof _ParseRelation2.default)) { - var json = (0, _encode2.default)(val, false, true); - objectCache[_attr3] = (0, _stringify2.default)(json); - } - } -} -},{"./ParseFile":14,"./ParseObject":18,"./ParseOp":19,"./ParsePromise":20,"./ParseRelation":22,"./TaskQueue":31,"./encode":36,"babel-runtime/core-js/json/stringify":44,"babel-runtime/helpers/typeof":61}],10:[function(_dereq_,module,exports){ -'use strict'; - -var _decode = _dereq_('./decode'); - -var _decode2 = _interopRequireDefault(_decode); - -var _encode = _dereq_('./encode'); - -var _encode2 = _interopRequireDefault(_encode); - -var _CoreManager = _dereq_('./CoreManager'); - -var _CoreManager2 = _interopRequireDefault(_CoreManager); - -var _InstallationController = _dereq_('./InstallationController'); - -var _InstallationController2 = _interopRequireDefault(_InstallationController); - -var _ParseOp = _dereq_('./ParseOp'); - -var ParseOp = _interopRequireWildcard(_ParseOp); - -var _RESTController = _dereq_('./RESTController'); - -var _RESTController2 = _interopRequireDefault(_RESTController); - -function _interopRequireWildcard(obj) { - if (obj && obj.__esModule) { - return obj; - } else { - var newObj = {};if (obj != null) { - for (var key in obj) { - if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; - } - }newObj.default = obj;return newObj; - } -} - -function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { default: obj }; -} - -/** - * Contains all Parse API classes and functions. - * @class Parse - * @static - */ -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -var Parse = { - /** - * Call this method first to set up your authentication tokens for Parse. - * You can get your keys from the Data Browser on parse.com. - * @method initialize - * @param {String} applicationId Your Parse Application ID. - * @param {String} javaScriptKey (optional) Your Parse JavaScript Key (Not needed for parse-server) - * @param {String} masterKey (optional) Your Parse Master Key. (Node.js only!) - * @static - */ - initialize: function (applicationId, javaScriptKey) { - if ('browser' === 'browser' && _CoreManager2.default.get('IS_NODE')) { - console.log('It looks like you\'re using the browser version of the SDK in a ' + 'node.js environment. You should require(\'parse/node\') instead.'); - } - Parse._initialize(applicationId, javaScriptKey); - }, - _initialize: function (applicationId, javaScriptKey, masterKey) { - _CoreManager2.default.set('APPLICATION_ID', applicationId); - _CoreManager2.default.set('JAVASCRIPT_KEY', javaScriptKey); - _CoreManager2.default.set('MASTER_KEY', masterKey); - _CoreManager2.default.set('USE_MASTER_KEY', false); - } -}; - -/** These legacy setters may eventually be deprecated **/ -Object.defineProperty(Parse, 'applicationId', { - get: function () { - return _CoreManager2.default.get('APPLICATION_ID'); - }, - set: function (value) { - _CoreManager2.default.set('APPLICATION_ID', value); - } -}); -Object.defineProperty(Parse, 'javaScriptKey', { - get: function () { - return _CoreManager2.default.get('JAVASCRIPT_KEY'); - }, - set: function (value) { - _CoreManager2.default.set('JAVASCRIPT_KEY', value); - } -}); -Object.defineProperty(Parse, 'masterKey', { - get: function () { - return _CoreManager2.default.get('MASTER_KEY'); - }, - set: function (value) { - _CoreManager2.default.set('MASTER_KEY', value); - } -}); -Object.defineProperty(Parse, 'serverURL', { - get: function () { - return _CoreManager2.default.get('SERVER_URL'); - }, - set: function (value) { - _CoreManager2.default.set('SERVER_URL', value); - } -}); -Object.defineProperty(Parse, 'liveQueryServerURL', { - get: function () { - return _CoreManager2.default.get('LIVEQUERY_SERVER_URL'); - }, - set: function (value) { - _CoreManager2.default.set('LIVEQUERY_SERVER_URL', value); - } -}); -/** End setters **/ - -Parse.ACL = _dereq_('./ParseACL').default; -Parse.Analytics = _dereq_('./Analytics'); -Parse.Cloud = _dereq_('./Cloud'); -Parse.CoreManager = _dereq_('./CoreManager'); -Parse.Config = _dereq_('./ParseConfig').default; -Parse.Error = _dereq_('./ParseError').default; -Parse.FacebookUtils = _dereq_('./FacebookUtils').default; -Parse.File = _dereq_('./ParseFile').default; -Parse.GeoPoint = _dereq_('./ParseGeoPoint').default; -Parse.Installation = _dereq_('./ParseInstallation').default; -Parse.Object = _dereq_('./ParseObject').default; -Parse.Op = { - Set: ParseOp.SetOp, - Unset: ParseOp.UnsetOp, - Increment: ParseOp.IncrementOp, - Add: ParseOp.AddOp, - Remove: ParseOp.RemoveOp, - AddUnique: ParseOp.AddUniqueOp, - Relation: ParseOp.RelationOp -}; -Parse.Promise = _dereq_('./ParsePromise').default; -Parse.Push = _dereq_('./Push'); -Parse.Query = _dereq_('./ParseQuery').default; -Parse.Relation = _dereq_('./ParseRelation').default; -Parse.Role = _dereq_('./ParseRole').default; -Parse.Session = _dereq_('./ParseSession').default; -Parse.Storage = _dereq_('./Storage'); -Parse.User = _dereq_('./ParseUser').default; -Parse.LiveQuery = _dereq_('./ParseLiveQuery').default; -Parse.LiveQueryClient = _dereq_('./LiveQueryClient').default; - -Parse._request = function () { - for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { - args[_key] = arguments[_key]; - } - - return _CoreManager2.default.getRESTController().request.apply(null, args); -}; -Parse._ajax = function () { - for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { - args[_key2] = arguments[_key2]; - } - - return _CoreManager2.default.getRESTController().ajax.apply(null, args); -}; -// We attempt to match the signatures of the legacy versions of these methods -Parse._decode = function (_, value) { - return (0, _decode2.default)(value); -}; -Parse._encode = function (value, _, disallowObjects) { - return (0, _encode2.default)(value, disallowObjects); -}; -Parse._getInstallationId = function () { - return _CoreManager2.default.getInstallationController().currentInstallationId(); -}; - -_CoreManager2.default.setInstallationController(_InstallationController2.default); -_CoreManager2.default.setRESTController(_RESTController2.default); - -// For legacy requires, of the form `var Parse = require('parse').Parse` -Parse.Parse = Parse; - -module.exports = Parse; -},{"./Analytics":1,"./Cloud":2,"./CoreManager":3,"./FacebookUtils":5,"./InstallationController":6,"./LiveQueryClient":7,"./ParseACL":11,"./ParseConfig":12,"./ParseError":13,"./ParseFile":14,"./ParseGeoPoint":15,"./ParseInstallation":16,"./ParseLiveQuery":17,"./ParseObject":18,"./ParseOp":19,"./ParsePromise":20,"./ParseQuery":21,"./ParseRelation":22,"./ParseRole":23,"./ParseSession":24,"./ParseUser":25,"./Push":26,"./RESTController":27,"./Storage":29,"./decode":35,"./encode":36}],11:[function(_dereq_,module,exports){ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _keys = _dereq_('babel-runtime/core-js/object/keys'); - -var _keys2 = _interopRequireDefault(_keys); - -var _typeof2 = _dereq_('babel-runtime/helpers/typeof'); - -var _typeof3 = _interopRequireDefault(_typeof2); - -var _classCallCheck2 = _dereq_('babel-runtime/helpers/classCallCheck'); - -var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); - -var _createClass2 = _dereq_('babel-runtime/helpers/createClass'); - -var _createClass3 = _interopRequireDefault(_createClass2); - -var _ParseRole = _dereq_('./ParseRole'); - -var _ParseRole2 = _interopRequireDefault(_ParseRole); - -var _ParseUser = _dereq_('./ParseUser'); - -var _ParseUser2 = _interopRequireDefault(_ParseUser); - -function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { default: obj }; -} - -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * - */ - -var PUBLIC_KEY = '*'; - -/** - * Creates a new ACL. - * If no argument is given, the ACL has no permissions for anyone. - * If the argument is a Parse.User, the ACL will have read and write - * permission for only that user. - * If the argument is any other JSON object, that object will be interpretted - * as a serialized ACL created with toJSON(). - * @class Parse.ACL - * @constructor - * - *
An ACL, or Access Control List can be added to any
- * Parse.Object
to restrict access to only a subset of users
- * of your application.
Parse.Error
.
- * @param {String} message A detailed description of the error.
- */
-var ParseError = function ParseError(code, message) {
- (0, _classCallCheck3.default)(this, ParseError);
-
- this.code = code;
- this.message = message;
-};
-
-/**
- * Error code indicating some error other than those enumerated here.
- * @property OTHER_CAUSE
- * @static
- * @final
- */
-
-exports.default = ParseError;
-ParseError.OTHER_CAUSE = -1;
-
-/**
- * Error code indicating that something has gone wrong with the server.
- * If you get this error code, it is Parse's fault. Contact us at
- * https://parse.com/help
- * @property INTERNAL_SERVER_ERROR
- * @static
- * @final
- */
-ParseError.INTERNAL_SERVER_ERROR = 1;
-
-/**
- * Error code indicating the connection to the Parse servers failed.
- * @property CONNECTION_FAILED
- * @static
- * @final
- */
-ParseError.CONNECTION_FAILED = 100;
-
-/**
- * Error code indicating the specified object doesn't exist.
- * @property OBJECT_NOT_FOUND
- * @static
- * @final
- */
-ParseError.OBJECT_NOT_FOUND = 101;
-
-/**
- * Error code indicating you tried to query with a datatype that doesn't
- * support it, like exact matching an array or object.
- * @property INVALID_QUERY
- * @static
- * @final
- */
-ParseError.INVALID_QUERY = 102;
-
-/**
- * Error code indicating a missing or invalid classname. Classnames are
- * case-sensitive. They must start with a letter, and a-zA-Z0-9_ are the
- * only valid characters.
- * @property INVALID_CLASS_NAME
- * @static
- * @final
- */
-ParseError.INVALID_CLASS_NAME = 103;
-
-/**
- * Error code indicating an unspecified object id.
- * @property MISSING_OBJECT_ID
- * @static
- * @final
- */
-ParseError.MISSING_OBJECT_ID = 104;
-
-/**
- * Error code indicating an invalid key name. Keys are case-sensitive. They
- * must start with a letter, and a-zA-Z0-9_ are the only valid characters.
- * @property INVALID_KEY_NAME
- * @static
- * @final
- */
-ParseError.INVALID_KEY_NAME = 105;
-
-/**
- * Error code indicating a malformed pointer. You should not see this unless
- * you have been mucking about changing internal Parse code.
- * @property INVALID_POINTER
- * @static
- * @final
- */
-ParseError.INVALID_POINTER = 106;
-
-/**
- * Error code indicating that badly formed JSON was received upstream. This
- * either indicates you have done something unusual with modifying how
- * things encode to JSON, or the network is failing badly.
- * @property INVALID_JSON
- * @static
- * @final
- */
-ParseError.INVALID_JSON = 107;
-
-/**
- * Error code indicating that the feature you tried to access is only
- * available internally for testing purposes.
- * @property COMMAND_UNAVAILABLE
- * @static
- * @final
- */
-ParseError.COMMAND_UNAVAILABLE = 108;
-
-/**
- * You must call Parse.initialize before using the Parse library.
- * @property NOT_INITIALIZED
- * @static
- * @final
- */
-ParseError.NOT_INITIALIZED = 109;
-
-/**
- * Error code indicating that a field was set to an inconsistent type.
- * @property INCORRECT_TYPE
- * @static
- * @final
- */
-ParseError.INCORRECT_TYPE = 111;
-
-/**
- * Error code indicating an invalid channel name. A channel name is either
- * an empty string (the broadcast channel) or contains only a-zA-Z0-9_
- * characters and starts with a letter.
- * @property INVALID_CHANNEL_NAME
- * @static
- * @final
- */
-ParseError.INVALID_CHANNEL_NAME = 112;
-
-/**
- * Error code indicating that push is misconfigured.
- * @property PUSH_MISCONFIGURED
- * @static
- * @final
- */
-ParseError.PUSH_MISCONFIGURED = 115;
-
-/**
- * Error code indicating that the object is too large.
- * @property OBJECT_TOO_LARGE
- * @static
- * @final
- */
-ParseError.OBJECT_TOO_LARGE = 116;
-
-/**
- * Error code indicating that the operation isn't allowed for clients.
- * @property OPERATION_FORBIDDEN
- * @static
- * @final
- */
-ParseError.OPERATION_FORBIDDEN = 119;
-
-/**
- * Error code indicating the result was not found in the cache.
- * @property CACHE_MISS
- * @static
- * @final
- */
-ParseError.CACHE_MISS = 120;
-
-/**
- * Error code indicating that an invalid key was used in a nested
- * JSONObject.
- * @property INVALID_NESTED_KEY
- * @static
- * @final
- */
-ParseError.INVALID_NESTED_KEY = 121;
-
-/**
- * Error code indicating that an invalid filename was used for ParseFile.
- * A valid file name contains only a-zA-Z0-9_. characters and is between 1
- * and 128 characters.
- * @property INVALID_FILE_NAME
- * @static
- * @final
- */
-ParseError.INVALID_FILE_NAME = 122;
-
-/**
- * Error code indicating an invalid ACL was provided.
- * @property INVALID_ACL
- * @static
- * @final
- */
-ParseError.INVALID_ACL = 123;
-
-/**
- * Error code indicating that the request timed out on the server. Typically
- * this indicates that the request is too expensive to run.
- * @property TIMEOUT
- * @static
- * @final
- */
-ParseError.TIMEOUT = 124;
-
-/**
- * Error code indicating that the email address was invalid.
- * @property INVALID_EMAIL_ADDRESS
- * @static
- * @final
- */
-ParseError.INVALID_EMAIL_ADDRESS = 125;
-
-/**
- * Error code indicating a missing content type.
- * @property MISSING_CONTENT_TYPE
- * @static
- * @final
- */
-ParseError.MISSING_CONTENT_TYPE = 126;
-
-/**
- * Error code indicating a missing content length.
- * @property MISSING_CONTENT_LENGTH
- * @static
- * @final
- */
-ParseError.MISSING_CONTENT_LENGTH = 127;
-
-/**
- * Error code indicating an invalid content length.
- * @property INVALID_CONTENT_LENGTH
- * @static
- * @final
- */
-ParseError.INVALID_CONTENT_LENGTH = 128;
-
-/**
- * Error code indicating a file that was too large.
- * @property FILE_TOO_LARGE
- * @static
- * @final
- */
-ParseError.FILE_TOO_LARGE = 129;
-
-/**
- * Error code indicating an error saving a file.
- * @property FILE_SAVE_ERROR
- * @static
- * @final
- */
-ParseError.FILE_SAVE_ERROR = 130;
-
-/**
- * Error code indicating that a unique field was given a value that is
- * already taken.
- * @property DUPLICATE_VALUE
- * @static
- * @final
- */
-ParseError.DUPLICATE_VALUE = 137;
-
-/**
- * Error code indicating that a role's name is invalid.
- * @property INVALID_ROLE_NAME
- * @static
- * @final
- */
-ParseError.INVALID_ROLE_NAME = 139;
-
-/**
- * Error code indicating that an application quota was exceeded. Upgrade to
- * resolve.
- * @property EXCEEDED_QUOTA
- * @static
- * @final
- */
-ParseError.EXCEEDED_QUOTA = 140;
-
-/**
- * Error code indicating that a Cloud Code script failed.
- * @property SCRIPT_FAILED
- * @static
- * @final
- */
-ParseError.SCRIPT_FAILED = 141;
-
-/**
- * Error code indicating that a Cloud Code validation failed.
- * @property VALIDATION_ERROR
- * @static
- * @final
- */
-ParseError.VALIDATION_ERROR = 142;
-
-/**
- * Error code indicating that invalid image data was provided.
- * @property INVALID_IMAGE_DATA
- * @static
- * @final
- */
-ParseError.INVALID_IMAGE_DATA = 143;
-
-/**
- * Error code indicating an unsaved file.
- * @property UNSAVED_FILE_ERROR
- * @static
- * @final
- */
-ParseError.UNSAVED_FILE_ERROR = 151;
-
-/**
- * Error code indicating an invalid push time.
- * @property INVALID_PUSH_TIME_ERROR
- * @static
- * @final
- */
-ParseError.INVALID_PUSH_TIME_ERROR = 152;
-
-/**
- * Error code indicating an error deleting a file.
- * @property FILE_DELETE_ERROR
- * @static
- * @final
- */
-ParseError.FILE_DELETE_ERROR = 153;
-
-/**
- * Error code indicating that the application has exceeded its request
- * limit.
- * @property REQUEST_LIMIT_EXCEEDED
- * @static
- * @final
- */
-ParseError.REQUEST_LIMIT_EXCEEDED = 155;
-
-/**
- * Error code indicating an invalid event name.
- * @property INVALID_EVENT_NAME
- * @static
- * @final
- */
-ParseError.INVALID_EVENT_NAME = 160;
-
-/**
- * Error code indicating that the username is missing or empty.
- * @property USERNAME_MISSING
- * @static
- * @final
- */
-ParseError.USERNAME_MISSING = 200;
-
-/**
- * Error code indicating that the password is missing or empty.
- * @property PASSWORD_MISSING
- * @static
- * @final
- */
-ParseError.PASSWORD_MISSING = 201;
-
-/**
- * Error code indicating that the username has already been taken.
- * @property USERNAME_TAKEN
- * @static
- * @final
- */
-ParseError.USERNAME_TAKEN = 202;
-
-/**
- * Error code indicating that the email has already been taken.
- * @property EMAIL_TAKEN
- * @static
- * @final
- */
-ParseError.EMAIL_TAKEN = 203;
-
-/**
- * Error code indicating that the email is missing, but must be specified.
- * @property EMAIL_MISSING
- * @static
- * @final
- */
-ParseError.EMAIL_MISSING = 204;
-
-/**
- * Error code indicating that a user with the specified email was not found.
- * @property EMAIL_NOT_FOUND
- * @static
- * @final
- */
-ParseError.EMAIL_NOT_FOUND = 205;
-
-/**
- * Error code indicating that a user object without a valid session could
- * not be altered.
- * @property SESSION_MISSING
- * @static
- * @final
- */
-ParseError.SESSION_MISSING = 206;
-
-/**
- * Error code indicating that a user can only be created through signup.
- * @property MUST_CREATE_USER_THROUGH_SIGNUP
- * @static
- * @final
- */
-ParseError.MUST_CREATE_USER_THROUGH_SIGNUP = 207;
-
-/**
- * Error code indicating that an an account being linked is already linked
- * to another user.
- * @property ACCOUNT_ALREADY_LINKED
- * @static
- * @final
- */
-ParseError.ACCOUNT_ALREADY_LINKED = 208;
-
-/**
- * Error code indicating that the current session token is invalid.
- * @property INVALID_SESSION_TOKEN
- * @static
- * @final
- */
-ParseError.INVALID_SESSION_TOKEN = 209;
-
-/**
- * Error code indicating that a user cannot be linked to an account because
- * that account's id could not be found.
- * @property LINKED_ID_MISSING
- * @static
- * @final
- */
-ParseError.LINKED_ID_MISSING = 250;
-
-/**
- * Error code indicating that a user with a linked (e.g. Facebook) account
- * has an invalid session.
- * @property INVALID_LINKED_SESSION
- * @static
- * @final
- */
-ParseError.INVALID_LINKED_SESSION = 251;
-
-/**
- * Error code indicating that a service being linked (e.g. Facebook or
- * Twitter) is unsupported.
- * @property UNSUPPORTED_SERVICE
- * @static
- * @final
- */
-ParseError.UNSUPPORTED_SERVICE = 252;
-
-/**
- * Error code indicating that there were multiple errors. Aggregate errors
- * have an "errors" property, which is an array of error objects with more
- * detail about each error that occurred.
- * @property AGGREGATE_ERROR
- * @static
- * @final
- */
-ParseError.AGGREGATE_ERROR = 600;
-
-/**
- * Error code indicating the client was unable to read an input file.
- * @property FILE_READ_ERROR
- * @static
- * @final
- */
-ParseError.FILE_READ_ERROR = 601;
-
-/**
- * Error code indicating a real error code is unavailable because
- * we had to use an XDomainRequest object to allow CORS requests in
- * Internet Explorer, which strips the body from HTTP responses that have
- * a non-2XX status code.
- * @property X_DOMAIN_REQUEST
- * @static
- * @final
- */
-ParseError.X_DOMAIN_REQUEST = 602;
-},{"babel-runtime/helpers/classCallCheck":56}],14:[function(_dereq_,module,exports){
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-
-var _classCallCheck2 = _dereq_('babel-runtime/helpers/classCallCheck');
-
-var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
-
-var _createClass2 = _dereq_('babel-runtime/helpers/createClass');
-
-var _createClass3 = _interopRequireDefault(_createClass2);
-
-var _CoreManager = _dereq_('./CoreManager');
-
-var _CoreManager2 = _interopRequireDefault(_CoreManager);
-
-var _ParsePromise = _dereq_('./ParsePromise');
-
-var _ParsePromise2 = _interopRequireDefault(_ParsePromise);
-
-function _interopRequireDefault(obj) {
- return obj && obj.__esModule ? obj : { default: obj };
-}
-
-/**
- * Copyright (c) 2015-present, Parse, LLC.
- * All rights reserved.
- *
- * This source code is licensed under the BSD-style license found in the
- * LICENSE file in the root directory of this source tree. An additional grant
- * of patent rights can be found in the PATENTS file in the same directory.
- *
- *
- */
-
-var dataUriRegexp = /^data:([a-zA-Z]*\/[a-zA-Z+.-]*);(charset=[a-zA-Z0-9\-\/\s]*,)?base64,/;
-
-function b64Digit(number) {
- if (number < 26) {
- return String.fromCharCode(65 + number);
- }
- if (number < 52) {
- return String.fromCharCode(97 + (number - 26));
- }
- if (number < 62) {
- return String.fromCharCode(48 + (number - 52));
- }
- if (number === 62) {
- return '+';
- }
- if (number === 63) {
- return '/';
- }
- throw new TypeError('Tried to encode large digit ' + number + ' in base64.');
-}
-
-/**
- * A Parse.File is a local representation of a file that is saved to the Parse
- * cloud.
- * @class Parse.File
- * @constructor
- * @param name {String} The file's name. This will be prefixed by a unique
- * value once the file has finished saving. The file name must begin with
- * an alphanumeric character, and consist of alphanumeric characters,
- * periods, spaces, underscores, or dashes.
- * @param data {Array} The data for the file, as either:
- * 1. an Array of byte value Numbers, or
- * 2. an Object like { base64: "..." } with a base64-encoded String.
- * 3. a File object selected with a file upload control. (3) only works
- * in Firefox 3.6+, Safari 6.0.2+, Chrome 7+, and IE 10+.
- * For example:- * var fileUploadControl = $("#profilePhotoFileUpload")[0]; - * if (fileUploadControl.files.length > 0) { - * var file = fileUploadControl.files[0]; - * var name = "photo.jpg"; - * var parseFile = new Parse.File(name, file); - * parseFile.save().then(function() { - * // The file has been saved to Parse. - * }, function(error) { - * // The file either could not be read, or could not be saved to Parse. - * }); - * }- * @param type {String} Optional Content-Type header to use for the file. If - * this is omitted, the content type will be inferred from the name's - * extension. - */ - -var ParseFile = function () { - function ParseFile(name, data, type) { - (0, _classCallCheck3.default)(this, ParseFile); - - var specifiedType = type || ''; - - this._name = name; - - if (data !== undefined) { - if (Array.isArray(data)) { - this._source = { - format: 'base64', - base64: ParseFile.encodeBase64(data), - type: specifiedType - }; - } else if (typeof File !== 'undefined' && data instanceof File) { - this._source = { - format: 'file', - file: data, - type: specifiedType - }; - } else if (data && typeof data.base64 === 'string') { - var _base = data.base64; - var commaIndex = _base.indexOf(','); - - if (commaIndex !== -1) { - var matches = dataUriRegexp.exec(_base.slice(0, commaIndex + 1)); - // if data URI with type and charset, there will be 4 matches. - this._source = { - format: 'base64', - base64: _base.slice(commaIndex + 1), - type: matches[1] - }; - } else { - this._source = { - format: 'base64', - base64: _base, - type: specifiedType - }; - } - } else { - throw new TypeError('Cannot create a Parse.File with that data.'); - } - } - } - - /** - * Gets the name of the file. Before save is called, this is the filename - * given by the user. After save is called, that name gets prefixed with a - * unique identifier. - * @method name - * @return {String} - */ - - (0, _createClass3.default)(ParseFile, [{ - key: 'name', - value: function () { - return this._name; - } - - /** - * Gets the url of the file. It is only available after you save the file or - * after you get the file from a Parse.Object. - * @method url - * @param {Object} options An object to specify url options - * @return {String} - */ - - }, { - key: 'url', - value: function (options) { - options = options || {}; - if (!this._url) { - return; - } - if (options.forceSecure) { - return this._url.replace(/^http:\/\//i, 'https://'); - } else { - return this._url; - } - } - - /** - * Saves the file to the Parse cloud. - * @method save - * @param {Object} options A Backbone-style options object. - * @return {Parse.Promise} Promise that is resolved when the save finishes. - */ - - }, { - key: 'save', - value: function (options) { - var _this = this; - - options = options || {}; - var controller = _CoreManager2.default.getFileController(); - if (!this._previousSave) { - if (this._source.format === 'file') { - this._previousSave = controller.saveFile(this._name, this._source).then(function (res) { - _this._name = res.name; - _this._url = res.url; - return _this; - }); - } else { - this._previousSave = controller.saveBase64(this._name, this._source).then(function (res) { - _this._name = res.name; - _this._url = res.url; - return _this; - }); - } - } - if (this._previousSave) { - return this._previousSave._thenRunCallbacks(options); - } - } - }, { - key: 'toJSON', - value: function () { - return { - __type: 'File', - name: this._name, - url: this._url - }; - } - }, { - key: 'equals', - value: function (other) { - if (this === other) { - return true; - } - // Unsaved Files are never equal, since they will be saved to different URLs - return other instanceof ParseFile && this.name() === other.name() && this.url() === other.url() && typeof this.url() !== 'undefined'; - } - }], [{ - key: 'fromJSON', - value: function (obj) { - if (obj.__type !== 'File') { - throw new TypeError('JSON object does not represent a ParseFile'); - } - var file = new ParseFile(obj.name); - file._url = obj.url; - return file; - } - }, { - key: 'encodeBase64', - value: function (bytes) { - var chunks = []; - chunks.length = Math.ceil(bytes.length / 3); - for (var i = 0; i < chunks.length; i++) { - var b1 = bytes[i * 3]; - var b2 = bytes[i * 3 + 1] || 0; - var b3 = bytes[i * 3 + 2] || 0; - - var has2 = i * 3 + 1 < bytes.length; - var has3 = i * 3 + 2 < bytes.length; - - chunks[i] = [b64Digit(b1 >> 2 & 0x3F), b64Digit(b1 << 4 & 0x30 | b2 >> 4 & 0x0F), has2 ? b64Digit(b2 << 2 & 0x3C | b3 >> 6 & 0x03) : '=', has3 ? b64Digit(b3 & 0x3F) : '='].join(''); - } - - return chunks.join(''); - } - }]); - return ParseFile; -}(); - -exports.default = ParseFile; - -var DefaultController = { - saveFile: function (name, source) { - if (source.format !== 'file') { - throw new Error('saveFile can only be used with File-type sources.'); - } - // To directly upload a File, we use a REST-style AJAX request - var headers = { - 'X-Parse-Application-ID': _CoreManager2.default.get('APPLICATION_ID'), - 'X-Parse-JavaScript-Key': _CoreManager2.default.get('JAVASCRIPT_KEY'), - 'Content-Type': source.type || (source.file ? source.file.type : null) - }; - var url = _CoreManager2.default.get('SERVER_URL'); - if (url[url.length - 1] !== '/') { - url += '/'; - } - url += 'files/' + name; - return _CoreManager2.default.getRESTController().ajax('POST', url, source.file, headers); - }, - - saveBase64: function (name, source) { - if (source.format !== 'base64') { - throw new Error('saveBase64 can only be used with Base64-type sources.'); - } - var data = { - base64: source.base64 - }; - if (source.type) { - data._ContentType = source.type; - } - - return _CoreManager2.default.getRESTController().request('POST', 'files/' + name, data); - } -}; - -_CoreManager2.default.setFileController(DefaultController); -},{"./CoreManager":3,"./ParsePromise":20,"babel-runtime/helpers/classCallCheck":56,"babel-runtime/helpers/createClass":57}],15:[function(_dereq_,module,exports){ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _typeof2 = _dereq_('babel-runtime/helpers/typeof'); - -var _typeof3 = _interopRequireDefault(_typeof2); - -var _classCallCheck2 = _dereq_('babel-runtime/helpers/classCallCheck'); - -var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); - -var _createClass2 = _dereq_('babel-runtime/helpers/createClass'); - -var _createClass3 = _interopRequireDefault(_createClass2); - -var _ParsePromise = _dereq_('./ParsePromise'); - -var _ParsePromise2 = _interopRequireDefault(_ParsePromise); - -function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { default: obj }; -} - -/** - * Creates a new GeoPoint with any of the following forms:
- * new GeoPoint(otherGeoPoint) - * new GeoPoint(30, 30) - * new GeoPoint([30, 30]) - * new GeoPoint({latitude: 30, longitude: 30}) - * new GeoPoint() // defaults to (0, 0) - *- * @class Parse.GeoPoint - * @constructor - * - *
Represents a latitude / longitude point that may be associated - * with a key in a ParseObject or used as a reference point for geo queries. - * This allows proximity-based queries on the key.
- * - *Only one key in a class may contain a GeoPoint.
- * - *Example:
- * var point = new Parse.GeoPoint(30.0, -20.0); - * var object = new Parse.Object("PlaceObject"); - * object.set("location", point); - * object.save();- */ -var ParseGeoPoint = function () { - function ParseGeoPoint(arg1, arg2) { - (0, _classCallCheck3.default)(this, ParseGeoPoint); - - if (Array.isArray(arg1)) { - ParseGeoPoint._validate(arg1[0], arg1[1]); - this._latitude = arg1[0]; - this._longitude = arg1[1]; - } else if ((typeof arg1 === 'undefined' ? 'undefined' : (0, _typeof3.default)(arg1)) === 'object') { - ParseGeoPoint._validate(arg1.latitude, arg1.longitude); - this._latitude = arg1.latitude; - this._longitude = arg1.longitude; - } else if (typeof arg1 === 'number' && typeof arg2 === 'number') { - ParseGeoPoint._validate(arg1, arg2); - this._latitude = arg1; - this._longitude = arg2; - } else { - this._latitude = 0; - this._longitude = 0; - } - } - - /** - * North-south portion of the coordinate, in range [-90, 90]. - * Throws an exception if set out of range in a modern browser. - * @property latitude - * @type Number - */ - - (0, _createClass3.default)(ParseGeoPoint, [{ - key: 'toJSON', - - /** - * Returns a JSON representation of the GeoPoint, suitable for Parse. - * @method toJSON - * @return {Object} - */ - value: function () { - ParseGeoPoint._validate(this._latitude, this._longitude); - return { - __type: 'GeoPoint', - latitude: this._latitude, - longitude: this._longitude - }; - } - }, { - key: 'equals', - value: function (other) { - return other instanceof ParseGeoPoint && this.latitude === other.latitude && this.longitude === other.longitude; - } - - /** - * Returns the distance from this GeoPoint to another in radians. - * @method radiansTo - * @param {Parse.GeoPoint} point the other Parse.GeoPoint. - * @return {Number} - */ - - }, { - key: 'radiansTo', - value: function (point) { - var d2r = Math.PI / 180.0; - var lat1rad = this.latitude * d2r; - var long1rad = this.longitude * d2r; - var lat2rad = point.latitude * d2r; - var long2rad = point.longitude * d2r; - - var sinDeltaLatDiv2 = Math.sin((lat1rad - lat2rad) / 2); - var sinDeltaLongDiv2 = Math.sin((long1rad - long2rad) / 2); - // Square of half the straight line chord distance between both points. - var a = sinDeltaLatDiv2 * sinDeltaLatDiv2 + Math.cos(lat1rad) * Math.cos(lat2rad) * sinDeltaLongDiv2 * sinDeltaLongDiv2; - a = Math.min(1.0, a); - return 2 * Math.asin(Math.sqrt(a)); - } - - /** - * Returns the distance from this GeoPoint to another in kilometers. - * @method kilometersTo - * @param {Parse.GeoPoint} point the other Parse.GeoPoint. - * @return {Number} - */ - - }, { - key: 'kilometersTo', - value: function (point) { - return this.radiansTo(point) * 6371.0; - } - - /** - * Returns the distance from this GeoPoint to another in miles. - * @method milesTo - * @param {Parse.GeoPoint} point the other Parse.GeoPoint. - * @return {Number} - */ - - }, { - key: 'milesTo', - value: function (point) { - return this.radiansTo(point) * 3958.8; - } - - /** - * Throws an exception if the given lat-long is out of bounds. - */ - - }, { - key: 'latitude', - get: function () { - return this._latitude; - }, - set: function (val) { - ParseGeoPoint._validate(val, this.longitude); - this._latitude = val; - } - - /** - * East-west portion of the coordinate, in range [-180, 180]. - * Throws if set out of range in a modern browser. - * @property longitude - * @type Number - */ - - }, { - key: 'longitude', - get: function () { - return this._longitude; - }, - set: function (val) { - ParseGeoPoint._validate(this.latitude, val); - this._longitude = val; - } - }], [{ - key: '_validate', - value: function (latitude, longitude) { - if (latitude !== latitude || longitude !== longitude) { - throw new TypeError('GeoPoint latitude and longitude must be valid numbers'); - } - if (latitude < -90.0) { - throw new TypeError('GeoPoint latitude out of bounds: ' + latitude + ' < -90.0.'); - } - if (latitude > 90.0) { - throw new TypeError('GeoPoint latitude out of bounds: ' + latitude + ' > 90.0.'); - } - if (longitude < -180.0) { - throw new TypeError('GeoPoint longitude out of bounds: ' + longitude + ' < -180.0.'); - } - if (longitude > 180.0) { - throw new TypeError('GeoPoint longitude out of bounds: ' + longitude + ' > 180.0.'); - } - } - - /** - * Creates a GeoPoint with the user's current location, if available. - * Calls options.success with a new GeoPoint instance or calls options.error. - * @method current - * @param {Object} options An object with success and error callbacks. - * @static - */ - - }, { - key: 'current', - value: function (options) { - var promise = new _ParsePromise2.default(); - navigator.geolocation.getCurrentPosition(function (location) { - promise.resolve(new ParseGeoPoint(location.coords.latitude, location.coords.longitude)); - }, function (error) { - promise.reject(error); - }); - - return promise._thenRunCallbacks(options); - } - }]); - return ParseGeoPoint; -}(); /** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * - */ - -exports.default = ParseGeoPoint; -},{"./ParsePromise":20,"babel-runtime/helpers/classCallCheck":56,"babel-runtime/helpers/createClass":57,"babel-runtime/helpers/typeof":61}],16:[function(_dereq_,module,exports){ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _typeof2 = _dereq_('babel-runtime/helpers/typeof'); - -var _typeof3 = _interopRequireDefault(_typeof2); - -var _getPrototypeOf = _dereq_('babel-runtime/core-js/object/get-prototype-of'); - -var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); - -var _classCallCheck2 = _dereq_('babel-runtime/helpers/classCallCheck'); - -var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); - -var _possibleConstructorReturn2 = _dereq_('babel-runtime/helpers/possibleConstructorReturn'); - -var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); - -var _inherits2 = _dereq_('babel-runtime/helpers/inherits'); - -var _inherits3 = _interopRequireDefault(_inherits2); - -var _ParseObject2 = _dereq_('./ParseObject'); - -var _ParseObject3 = _interopRequireDefault(_ParseObject2); - -function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { default: obj }; -} - -var Installation = function (_ParseObject) { - (0, _inherits3.default)(Installation, _ParseObject); - - function Installation(attributes) { - (0, _classCallCheck3.default)(this, Installation); - - var _this = (0, _possibleConstructorReturn3.default)(this, (Installation.__proto__ || (0, _getPrototypeOf2.default)(Installation)).call(this, '_Installation')); - - if (attributes && (typeof attributes === 'undefined' ? 'undefined' : (0, _typeof3.default)(attributes)) === 'object') { - if (!_this.set(attributes || {})) { - throw new Error('Can\'t create an invalid Session'); - } - } - return _this; - } - - return Installation; -}(_ParseObject3.default); /** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * - */ - -exports.default = Installation; - -_ParseObject3.default.registerSubclass('_Installation', Installation); -},{"./ParseObject":18,"babel-runtime/core-js/object/get-prototype-of":50,"babel-runtime/helpers/classCallCheck":56,"babel-runtime/helpers/inherits":59,"babel-runtime/helpers/possibleConstructorReturn":60,"babel-runtime/helpers/typeof":61}],17:[function(_dereq_,module,exports){ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _EventEmitter = _dereq_('./EventEmitter'); - -var _EventEmitter2 = _interopRequireDefault(_EventEmitter); - -var _LiveQueryClient = _dereq_('./LiveQueryClient'); - -var _LiveQueryClient2 = _interopRequireDefault(_LiveQueryClient); - -var _CoreManager = _dereq_('./CoreManager'); - -var _CoreManager2 = _interopRequireDefault(_CoreManager); - -var _ParsePromise = _dereq_('./ParsePromise'); - -var _ParsePromise2 = _interopRequireDefault(_ParsePromise); - -function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { default: obj }; -} - -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * - */ - -function open() { - var LiveQueryController = _CoreManager2.default.getLiveQueryController(); - LiveQueryController.open(); -} - -function close() { - var LiveQueryController = _CoreManager2.default.getLiveQueryController(); - LiveQueryController.close(); -} - -/** - * - * We expose three events to help you monitor the status of the WebSocket connection: - * - *
Open - When we establish the WebSocket connection to the LiveQuery server, you'll get this event. - * - *
- * Parse.LiveQuery.on('open', () => { - * - * });- * - *
Close - When we lose the WebSocket connection to the LiveQuery server, you'll get this event. - * - *
- * Parse.LiveQuery.on('close', () => { - * - * });- * - *
Error - When some network error or LiveQuery server error happens, you'll get this event. - * - *
- * Parse.LiveQuery.on('error', (error) => { - * - * });- * - * @class Parse.LiveQuery - * @static - * - */ -var LiveQuery = new _EventEmitter2.default(); - -/** - * After open is called, the LiveQuery will try to send a connect request - * to the LiveQuery server. - * - * @method open - */ -LiveQuery.open = open; - -/** - * When you're done using LiveQuery, you can call Parse.LiveQuery.close(). - * This function will close the WebSocket connection to the LiveQuery server, - * cancel the auto reconnect, and unsubscribe all subscriptions based on it. - * If you call query.subscribe() after this, we'll create a new WebSocket - * connection to the LiveQuery server. - * - * @method close - */ - -LiveQuery.close = close; -// Register a default onError callback to make sure we do not crash on error -LiveQuery.on('error', function () {}); - -exports.default = LiveQuery; - -function getSessionToken() { - var controller = _CoreManager2.default.getUserController(); - return controller.currentUserAsync().then(function (currentUser) { - return currentUser ? currentUser.getSessionToken() : undefined; - }); -} - -function getLiveQueryClient() { - return _CoreManager2.default.getLiveQueryController().getDefaultLiveQueryClient(); -} - -var defaultLiveQueryClient = void 0; -var DefaultLiveQueryController = { - setDefaultLiveQueryClient: function (liveQueryClient) { - defaultLiveQueryClient = liveQueryClient; - }, - getDefaultLiveQueryClient: function () { - if (defaultLiveQueryClient) { - return _ParsePromise2.default.as(defaultLiveQueryClient); - } - - return getSessionToken().then(function (sessionToken) { - var liveQueryServerURL = _CoreManager2.default.get('LIVEQUERY_SERVER_URL'); - - if (liveQueryServerURL && liveQueryServerURL.indexOf('ws') !== 0) { - throw new Error('You need to set a proper Parse LiveQuery server url before using LiveQueryClient'); - } - - // If we can not find Parse.liveQueryServerURL, we try to extract it from Parse.serverURL - if (!liveQueryServerURL) { - var tempServerURL = _CoreManager2.default.get('SERVER_URL'); - var protocol = 'ws://'; - // If Parse is being served over SSL/HTTPS, ensure LiveQuery Server uses 'wss://' prefix - if (tempServerURL.indexOf('https') === 0) { - protocol = 'wss://'; - } - var host = tempServerURL.replace(/^https?:\/\//, ''); - liveQueryServerURL = protocol + host; - _CoreManager2.default.set('LIVEQUERY_SERVER_URL', liveQueryServerURL); - } - - var applicationId = _CoreManager2.default.get('APPLICATION_ID'); - var javascriptKey = _CoreManager2.default.get('JAVASCRIPT_KEY'); - var masterKey = _CoreManager2.default.get('MASTER_KEY'); - // Get currentUser sessionToken if possible - defaultLiveQueryClient = new _LiveQueryClient2.default({ - applicationId: applicationId, - serverURL: liveQueryServerURL, - javascriptKey: javascriptKey, - masterKey: masterKey, - sessionToken: sessionToken - }); - // Register a default onError callback to make sure we do not crash on error - // Cannot create these events on a nested way because of EventEmiiter from React Native - defaultLiveQueryClient.on('error', function (error) { - LiveQuery.emit('error', error); - }); - defaultLiveQueryClient.on('open', function () { - LiveQuery.emit('open'); - }); - defaultLiveQueryClient.on('close', function () { - LiveQuery.emit('close'); - }); - - return defaultLiveQueryClient; - }); - }, - open: function () { - var _this = this; - - getLiveQueryClient().then(function (liveQueryClient) { - _this.resolve(liveQueryClient.open()); - }); - }, - close: function () { - var _this2 = this; - - getLiveQueryClient().then(function (liveQueryClient) { - _this2.resolve(liveQueryClient.close()); - }); - }, - subscribe: function (query) { - var _this3 = this; - - var subscriptionWrap = new _EventEmitter2.default(); - - getLiveQueryClient().then(function (liveQueryClient) { - if (liveQueryClient.shouldOpen()) { - liveQueryClient.open(); - } - var promiseSessionToken = getSessionToken(); - // new event emitter - return promiseSessionToken.then(function (sessionToken) { - - var subscription = liveQueryClient.subscribe(query, sessionToken); - // enter, leave create, etc - - subscriptionWrap.id = subscription.id; - subscriptionWrap.query = subscription.query; - subscriptionWrap.sessionToken = subscription.sessionToken; - subscriptionWrap.unsubscribe = subscription.unsubscribe; - // Cannot create these events on a nested way because of EventEmiiter from React Native - subscription.on('open', function () { - subscriptionWrap.emit('open'); - }); - subscription.on('create', function (object) { - subscriptionWrap.emit('create', object); - }); - subscription.on('update', function (object) { - subscriptionWrap.emit('update', object); - }); - subscription.on('enter', function (object) { - subscriptionWrap.emit('enter', object); - }); - subscription.on('leave', function (object) { - subscriptionWrap.emit('leave', object); - }); - subscription.on('delete', function (object) { - subscriptionWrap.emit('delete', object); - }); - - _this3.resolve(); - }); - }); - return subscriptionWrap; - }, - unsubscribe: function (subscription) { - var _this4 = this; - - getLiveQueryClient().then(function (liveQueryClient) { - _this4.resolve(liveQueryClient.unsubscribe(subscription)); - }); - }, - _clearCachedDefaultClient: function () { - defaultLiveQueryClient = null; - } -}; - -_CoreManager2.default.setLiveQueryController(DefaultLiveQueryController); -},{"./CoreManager":3,"./EventEmitter":4,"./LiveQueryClient":7,"./ParsePromise":20}],18:[function(_dereq_,module,exports){ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _defineProperty = _dereq_('babel-runtime/core-js/object/define-property'); - -var _defineProperty2 = _interopRequireDefault(_defineProperty); - -var _create = _dereq_('babel-runtime/core-js/object/create'); - -var _create2 = _interopRequireDefault(_create); - -var _freeze = _dereq_('babel-runtime/core-js/object/freeze'); - -var _freeze2 = _interopRequireDefault(_freeze); - -var _stringify = _dereq_('babel-runtime/core-js/json/stringify'); - -var _stringify2 = _interopRequireDefault(_stringify); - -var _keys = _dereq_('babel-runtime/core-js/object/keys'); - -var _keys2 = _interopRequireDefault(_keys); - -var _typeof2 = _dereq_('babel-runtime/helpers/typeof'); - -var _typeof3 = _interopRequireDefault(_typeof2); - -var _classCallCheck2 = _dereq_('babel-runtime/helpers/classCallCheck'); - -var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); - -var _createClass2 = _dereq_('babel-runtime/helpers/createClass'); - -var _createClass3 = _interopRequireDefault(_createClass2); - -var _CoreManager = _dereq_('./CoreManager'); - -var _CoreManager2 = _interopRequireDefault(_CoreManager); - -var _canBeSerialized = _dereq_('./canBeSerialized'); - -var _canBeSerialized2 = _interopRequireDefault(_canBeSerialized); - -var _decode = _dereq_('./decode'); - -var _decode2 = _interopRequireDefault(_decode); - -var _encode = _dereq_('./encode'); - -var _encode2 = _interopRequireDefault(_encode); - -var _equals = _dereq_('./equals'); - -var _equals2 = _interopRequireDefault(_equals); - -var _escape2 = _dereq_('./escape'); - -var _escape3 = _interopRequireDefault(_escape2); - -var _ParseACL = _dereq_('./ParseACL'); - -var _ParseACL2 = _interopRequireDefault(_ParseACL); - -var _parseDate = _dereq_('./parseDate'); - -var _parseDate2 = _interopRequireDefault(_parseDate); - -var _ParseError = _dereq_('./ParseError'); - -var _ParseError2 = _interopRequireDefault(_ParseError); - -var _ParseFile = _dereq_('./ParseFile'); - -var _ParseFile2 = _interopRequireDefault(_ParseFile); - -var _ParseOp = _dereq_('./ParseOp'); - -var _ParsePromise = _dereq_('./ParsePromise'); - -var _ParsePromise2 = _interopRequireDefault(_ParsePromise); - -var _ParseQuery = _dereq_('./ParseQuery'); - -var _ParseQuery2 = _interopRequireDefault(_ParseQuery); - -var _ParseRelation = _dereq_('./ParseRelation'); - -var _ParseRelation2 = _interopRequireDefault(_ParseRelation); - -var _SingleInstanceStateController = _dereq_('./SingleInstanceStateController'); - -var SingleInstanceStateController = _interopRequireWildcard(_SingleInstanceStateController); - -var _unique = _dereq_('./unique'); - -var _unique2 = _interopRequireDefault(_unique); - -var _UniqueInstanceStateController = _dereq_('./UniqueInstanceStateController'); - -var UniqueInstanceStateController = _interopRequireWildcard(_UniqueInstanceStateController); - -var _unsavedChildren = _dereq_('./unsavedChildren'); - -var _unsavedChildren2 = _interopRequireDefault(_unsavedChildren); - -function _interopRequireWildcard(obj) { - if (obj && obj.__esModule) { - return obj; - } else { - var newObj = {};if (obj != null) { - for (var key in obj) { - if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; - } - }newObj.default = obj;return newObj; - } -} - -function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { default: obj }; -} - -// Mapping of class names to constructors, so we can populate objects from the -// server with appropriate subclasses of ParseObject -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * - */ - -var classMap = {}; - -// Global counter for generating unique local Ids -var localCount = 0; -// Global counter for generating unique Ids for non-single-instance objects -var objectCount = 0; -// On web clients, objects are single-instance: any two objects with the same Id -// will have the same attributes. However, this may be dangerous default -// behavior in a server scenario -var singleInstance = !_CoreManager2.default.get('IS_NODE'); -if (singleInstance) { - _CoreManager2.default.setObjectStateController(SingleInstanceStateController); -} else { - _CoreManager2.default.setObjectStateController(UniqueInstanceStateController); -} - -function getServerUrlPath() { - var serverUrl = _CoreManager2.default.get('SERVER_URL'); - if (serverUrl[serverUrl.length - 1] !== '/') { - serverUrl += '/'; - } - var url = serverUrl.replace(/https?:\/\//, ''); - return url.substr(url.indexOf('/')); -} - -/** - * Creates a new model with defined attributes. - * - *
You won't normally call this method directly. It is recommended that
- * you use a subclass of Parse.Object
instead, created by calling
- * extend
.
However, if you don't want to use a subclass, or aren't sure which - * subclass is appropriate, you can use this form:
- * var object = new Parse.Object("ClassName"); - *- * That is basically equivalent to:
- * var MyClass = Parse.Object.extend("ClassName"); - * var object = new MyClass(); - *- * - * @class Parse.Object - * @constructor - * @param {String} className The class name for the object - * @param {Object} attributes The initial set of data to store in the object. - * @param {Object} options The options for this object instance. - */ - -var ParseObject = function () { - /** - * The ID of this object, unique within its class. - * @property id - * @type String - */ - function ParseObject(className, attributes, options) { - (0, _classCallCheck3.default)(this, ParseObject); - - // Enable legacy initializers - if (typeof this.initialize === 'function') { - this.initialize.apply(this, arguments); - } - - var toSet = null; - this._objCount = objectCount++; - if (typeof className === 'string') { - this.className = className; - if (attributes && (typeof attributes === 'undefined' ? 'undefined' : (0, _typeof3.default)(attributes)) === 'object') { - toSet = attributes; - } - } else if (className && (typeof className === 'undefined' ? 'undefined' : (0, _typeof3.default)(className)) === 'object') { - this.className = className.className; - toSet = {}; - for (var attr in className) { - if (attr !== 'className') { - toSet[attr] = className[attr]; - } - } - if (attributes && (typeof attributes === 'undefined' ? 'undefined' : (0, _typeof3.default)(attributes)) === 'object') { - options = attributes; - } - } - if (toSet && !this.set(toSet, options)) { - throw new Error('Can\'t create an invalid Parse Object'); - } - } - - /** Prototype getters / setters **/ - - (0, _createClass3.default)(ParseObject, [{ - key: '_getId', - - /** Private methods **/ - - /** - * Returns a local or server Id used uniquely identify this object - */ - value: function () { - if (typeof this.id === 'string') { - return this.id; - } - if (typeof this._localId === 'string') { - return this._localId; - } - var localId = 'local' + String(localCount++); - this._localId = localId; - return localId; - } - - /** - * Returns a unique identifier used to pull data from the State Controller. - */ - - }, { - key: '_getStateIdentifier', - value: function () { - if (singleInstance) { - var _id = this.id; - if (!_id) { - _id = this._getId(); - } - return { - id: _id, - className: this.className - }; - } else { - return this; - } - } - }, { - key: '_getServerData', - value: function () { - var stateController = _CoreManager2.default.getObjectStateController(); - return stateController.getServerData(this._getStateIdentifier()); - } - }, { - key: '_clearServerData', - value: function () { - var serverData = this._getServerData(); - var unset = {}; - for (var attr in serverData) { - unset[attr] = undefined; - } - var stateController = _CoreManager2.default.getObjectStateController(); - stateController.setServerData(this._getStateIdentifier(), unset); - } - }, { - key: '_getPendingOps', - value: function () { - var stateController = _CoreManager2.default.getObjectStateController(); - return stateController.getPendingOps(this._getStateIdentifier()); - } - }, { - key: '_clearPendingOps', - value: function () { - var pending = this._getPendingOps(); - var latest = pending[pending.length - 1]; - var keys = (0, _keys2.default)(latest); - keys.forEach(function (key) { - delete latest[key]; - }); - } - }, { - key: '_getDirtyObjectAttributes', - value: function () { - var attributes = this.attributes; - var stateController = _CoreManager2.default.getObjectStateController(); - var objectCache = stateController.getObjectCache(this._getStateIdentifier()); - var dirty = {}; - for (var attr in attributes) { - var val = attributes[attr]; - if (val && (typeof val === 'undefined' ? 'undefined' : (0, _typeof3.default)(val)) === 'object' && !(val instanceof ParseObject) && !(val instanceof _ParseFile2.default) && !(val instanceof _ParseRelation2.default)) { - // Due to the way browsers construct maps, the key order will not change - // unless the object is changed - try { - var json = (0, _encode2.default)(val, false, true); - var stringified = (0, _stringify2.default)(json); - if (objectCache[attr] !== stringified) { - dirty[attr] = val; - } - } catch (e) { - // Error occurred, possibly by a nested unsaved pointer in a mutable container - // No matter how it happened, it indicates a change in the attribute - dirty[attr] = val; - } - } - } - return dirty; - } - }, { - key: '_toFullJSON', - value: function (seen) { - var json = this.toJSON(seen); - json.__type = 'Object'; - json.className = this.className; - return json; - } - }, { - key: '_getSaveJSON', - value: function () { - var pending = this._getPendingOps(); - var dirtyObjects = this._getDirtyObjectAttributes(); - var json = {}; - - for (var attr in dirtyObjects) { - json[attr] = new _ParseOp.SetOp(dirtyObjects[attr]).toJSON(); - } - for (attr in pending[0]) { - json[attr] = pending[0][attr].toJSON(); - } - return json; - } - }, { - key: '_getSaveParams', - value: function () { - var method = this.id ? 'PUT' : 'POST'; - var body = this._getSaveJSON(); - var path = 'classes/' + this.className; - if (this.id) { - path += '/' + this.id; - } else if (this.className === '_User') { - path = 'users'; - } - return { - method: method, - body: body, - path: path - }; - } - }, { - key: '_finishFetch', - value: function (serverData) { - if (!this.id && serverData.objectId) { - this.id = serverData.objectId; - } - var stateController = _CoreManager2.default.getObjectStateController(); - stateController.initializeState(this._getStateIdentifier()); - var decoded = {}; - for (var attr in serverData) { - if (attr === 'ACL') { - decoded[attr] = new _ParseACL2.default(serverData[attr]); - } else if (attr !== 'objectId') { - decoded[attr] = (0, _decode2.default)(serverData[attr]); - if (decoded[attr] instanceof _ParseRelation2.default) { - decoded[attr]._ensureParentAndKey(this, attr); - } - } - } - if (decoded.createdAt && typeof decoded.createdAt === 'string') { - decoded.createdAt = (0, _parseDate2.default)(decoded.createdAt); - } - if (decoded.updatedAt && typeof decoded.updatedAt === 'string') { - decoded.updatedAt = (0, _parseDate2.default)(decoded.updatedAt); - } - if (!decoded.updatedAt && decoded.createdAt) { - decoded.updatedAt = decoded.createdAt; - } - stateController.commitServerChanges(this._getStateIdentifier(), decoded); - } - }, { - key: '_setExisted', - value: function (existed) { - var stateController = _CoreManager2.default.getObjectStateController(); - var state = stateController.getState(this._getStateIdentifier()); - if (state) { - state.existed = existed; - } - } - }, { - key: '_migrateId', - value: function (serverId) { - if (this._localId && serverId) { - if (singleInstance) { - var stateController = _CoreManager2.default.getObjectStateController(); - var oldState = stateController.removeState(this._getStateIdentifier()); - this.id = serverId; - delete this._localId; - if (oldState) { - stateController.initializeState(this._getStateIdentifier(), oldState); - } - } else { - this.id = serverId; - delete this._localId; - } - } - } - }, { - key: '_handleSaveResponse', - value: function (response, status) { - var changes = {}; - - var stateController = _CoreManager2.default.getObjectStateController(); - var pending = stateController.popPendingState(this._getStateIdentifier()); - for (var attr in pending) { - if (pending[attr] instanceof _ParseOp.RelationOp) { - changes[attr] = pending[attr].applyTo(undefined, this, attr); - } else if (!(attr in response)) { - // Only SetOps and UnsetOps should not come back with results - changes[attr] = pending[attr].applyTo(undefined); - } - } - for (attr in response) { - if ((attr === 'createdAt' || attr === 'updatedAt') && typeof response[attr] === 'string') { - changes[attr] = (0, _parseDate2.default)(response[attr]); - } else if (attr === 'ACL') { - changes[attr] = new _ParseACL2.default(response[attr]); - } else if (attr !== 'objectId') { - changes[attr] = (0, _decode2.default)(response[attr]); - if (changes[attr] instanceof _ParseOp.UnsetOp) { - changes[attr] = undefined; - } - } - } - if (changes.createdAt && !changes.updatedAt) { - changes.updatedAt = changes.createdAt; - } - - this._migrateId(response.objectId); - - if (status !== 201) { - this._setExisted(true); - } - - stateController.commitServerChanges(this._getStateIdentifier(), changes); - } - }, { - key: '_handleSaveError', - value: function () { - this._getPendingOps(); - - var stateController = _CoreManager2.default.getObjectStateController(); - stateController.mergeFirstPendingState(this._getStateIdentifier()); - } - - /** Public methods **/ - - }, { - key: 'initialize', - value: function () {} - // NOOP - - - /** - * Returns a JSON version of the object suitable for saving to Parse. - * @method toJSON - * @return {Object} - */ - - }, { - key: 'toJSON', - value: function (seen) { - var seenEntry = this.id ? this.className + ':' + this.id : this; - var seen = seen || [seenEntry]; - var json = {}; - var attrs = this.attributes; - for (var attr in attrs) { - if ((attr === 'createdAt' || attr === 'updatedAt') && attrs[attr].toJSON) { - json[attr] = attrs[attr].toJSON(); - } else { - json[attr] = (0, _encode2.default)(attrs[attr], false, false, seen); - } - } - var pending = this._getPendingOps(); - for (var attr in pending[0]) { - json[attr] = pending[0][attr].toJSON(); - } - - if (this.id) { - json.objectId = this.id; - } - return json; - } - - /** - * Determines whether this ParseObject is equal to another ParseObject - * @method equals - * @return {Boolean} - */ - - }, { - key: 'equals', - value: function (other) { - if (this === other) { - return true; - } - return other instanceof ParseObject && this.className === other.className && this.id === other.id && typeof this.id !== 'undefined'; - } - - /** - * Returns true if this object has been modified since its last - * save/refresh. If an attribute is specified, it returns true only if that - * particular attribute has been modified since the last save/refresh. - * @method dirty - * @param {String} attr An attribute name (optional). - * @return {Boolean} - */ - - }, { - key: 'dirty', - value: function (attr) { - if (!this.id) { - return true; - } - var pendingOps = this._getPendingOps(); - var dirtyObjects = this._getDirtyObjectAttributes(); - if (attr) { - if (dirtyObjects.hasOwnProperty(attr)) { - return true; - } - for (var i = 0; i < pendingOps.length; i++) { - if (pendingOps[i].hasOwnProperty(attr)) { - return true; - } - } - return false; - } - if ((0, _keys2.default)(pendingOps[0]).length !== 0) { - return true; - } - if ((0, _keys2.default)(dirtyObjects).length !== 0) { - return true; - } - return false; - } - - /** - * Returns an array of keys that have been modified since last save/refresh - * @method dirtyKeys - * @return {Array of string} - */ - - }, { - key: 'dirtyKeys', - value: function () { - var pendingOps = this._getPendingOps(); - var keys = {}; - for (var i = 0; i < pendingOps.length; i++) { - for (var attr in pendingOps[i]) { - keys[attr] = true; - } - } - var dirtyObjects = this._getDirtyObjectAttributes(); - for (var attr in dirtyObjects) { - keys[attr] = true; - } - return (0, _keys2.default)(keys); - } - - /** - * Gets a Pointer referencing this Object. - * @method toPointer - * @return {Object} - */ - - }, { - key: 'toPointer', - value: function () { - if (!this.id) { - throw new Error('Cannot create a pointer to an unsaved ParseObject'); - } - return { - __type: 'Pointer', - className: this.className, - objectId: this.id - }; - } - - /** - * Gets the value of an attribute. - * @method get - * @param {String} attr The string name of an attribute. - */ - - }, { - key: 'get', - value: function (attr) { - return this.attributes[attr]; - } - - /** - * Gets a relation on the given class for the attribute. - * @method relation - * @param String attr The attribute to get the relation for. - */ - - }, { - key: 'relation', - value: function (attr) { - var value = this.get(attr); - if (value) { - if (!(value instanceof _ParseRelation2.default)) { - throw new Error('Called relation() on non-relation field ' + attr); - } - value._ensureParentAndKey(this, attr); - return value; - } - return new _ParseRelation2.default(this, attr); - } - - /** - * Gets the HTML-escaped value of an attribute. - * @method escape - * @param {String} attr The string name of an attribute. - */ - - }, { - key: 'escape', - value: function (attr) { - var val = this.attributes[attr]; - if (val == null) { - return ''; - } - - if (typeof val !== 'string') { - if (typeof val.toString !== 'function') { - return ''; - } - val = val.toString(); - } - return (0, _escape3.default)(val); - } - - /** - * Returns
true
if the attribute contains a value that is not
- * null or undefined.
- * @method has
- * @param {String} attr The string name of the attribute.
- * @return {Boolean}
- */
-
- }, {
- key: 'has',
- value: function (attr) {
- var attributes = this.attributes;
- if (attributes.hasOwnProperty(attr)) {
- return attributes[attr] != null;
- }
- return false;
- }
-
- /**
- * Sets a hash of model attributes on the object.
- *
- * You can call it with an object containing keys and values, or with one - * key and value. For example:
- * gameTurn.set({ - * player: player1, - * diceRoll: 2 - * }, { - * error: function(gameTurnAgain, error) { - * // The set failed validation. - * } - * }); - * - * game.set("currentPlayer", player2, { - * error: function(gameTurnAgain, error) { - * // The set failed validation. - * } - * }); - * - * game.set("finished", true);- * - * @method set - * @param {String} key The key to set. - * @param {} value The value to give it. - * @param {Object} options A set of options for the set. - * The only supported option is
error
.
- * @return {Boolean} true if the set succeeded.
- */
-
- }, {
- key: 'set',
- value: function (key, value, options) {
- var changes = {};
- var newOps = {};
- if (key && (typeof key === 'undefined' ? 'undefined' : (0, _typeof3.default)(key)) === 'object') {
- changes = key;
- options = value;
- } else if (typeof key === 'string') {
- changes[key] = value;
- } else {
- return this;
- }
-
- options = options || {};
- var readonly = [];
- if (typeof this.constructor.readOnlyAttributes === 'function') {
- readonly = readonly.concat(this.constructor.readOnlyAttributes());
- }
- for (var k in changes) {
- if (k === 'createdAt' || k === 'updatedAt') {
- // This property is read-only, but for legacy reasons we silently
- // ignore it
- continue;
- }
- if (readonly.indexOf(k) > -1) {
- throw new Error('Cannot modify readonly attribute: ' + k);
- }
- if (options.unset) {
- newOps[k] = new _ParseOp.UnsetOp();
- } else if (changes[k] instanceof _ParseOp.Op) {
- newOps[k] = changes[k];
- } else if (changes[k] && (0, _typeof3.default)(changes[k]) === 'object' && typeof changes[k].__op === 'string') {
- newOps[k] = (0, _ParseOp.opFromJSON)(changes[k]);
- } else if (k === 'objectId' || k === 'id') {
- if (typeof changes[k] === 'string') {
- this.id = changes[k];
- }
- } else if (k === 'ACL' && (0, _typeof3.default)(changes[k]) === 'object' && !(changes[k] instanceof _ParseACL2.default)) {
- newOps[k] = new _ParseOp.SetOp(new _ParseACL2.default(changes[k]));
- } else {
- newOps[k] = new _ParseOp.SetOp(changes[k]);
- }
- }
-
- // Calculate new values
- var currentAttributes = this.attributes;
- var newValues = {};
- for (var attr in newOps) {
- if (newOps[attr] instanceof _ParseOp.RelationOp) {
- newValues[attr] = newOps[attr].applyTo(currentAttributes[attr], this, attr);
- } else if (!(newOps[attr] instanceof _ParseOp.UnsetOp)) {
- newValues[attr] = newOps[attr].applyTo(currentAttributes[attr]);
- }
- }
-
- // Validate changes
- if (!options.ignoreValidation) {
- var validation = this.validate(newValues);
- if (validation) {
- if (typeof options.error === 'function') {
- options.error(this, validation);
- }
- return false;
- }
- }
-
- // Consolidate Ops
- var pendingOps = this._getPendingOps();
- var last = pendingOps.length - 1;
- var stateController = _CoreManager2.default.getObjectStateController();
- for (var attr in newOps) {
- var nextOp = newOps[attr].mergeWith(pendingOps[last][attr]);
- stateController.setPendingOp(this._getStateIdentifier(), attr, nextOp);
- }
-
- return this;
- }
-
- /**
- * Remove an attribute from the model. This is a noop if the attribute doesn't
- * exist.
- * @method unset
- * @param {String} attr The string name of an attribute.
- */
-
- }, {
- key: 'unset',
- value: function (attr, options) {
- options = options || {};
- options.unset = true;
- return this.set(attr, null, options);
- }
-
- /**
- * Atomically increments the value of the given attribute the next time the
- * object is saved. If no amount is specified, 1 is used by default.
- *
- * @method increment
- * @param attr {String} The key.
- * @param amount {Number} The amount to increment by (optional).
- */
-
- }, {
- key: 'increment',
- value: function (attr, amount) {
- if (typeof amount === 'undefined') {
- amount = 1;
- }
- if (typeof amount !== 'number') {
- throw new Error('Cannot increment by a non-numeric amount.');
- }
- return this.set(attr, new _ParseOp.IncrementOp(amount));
- }
-
- /**
- * Atomically add an object to the end of the array associated with a given
- * key.
- * @method add
- * @param attr {String} The key.
- * @param item {} The item to add.
- */
-
- }, {
- key: 'add',
- value: function (attr, item) {
- return this.set(attr, new _ParseOp.AddOp([item]));
- }
-
- /**
- * Atomically add an object to the array associated with a given key, only
- * if it is not already present in the array. The position of the insert is
- * not guaranteed.
- *
- * @method addUnique
- * @param attr {String} The key.
- * @param item {} The object to add.
- */
-
- }, {
- key: 'addUnique',
- value: function (attr, item) {
- return this.set(attr, new _ParseOp.AddUniqueOp([item]));
- }
-
- /**
- * Atomically remove all instances of an object from the array associated
- * with a given key.
- *
- * @method remove
- * @param attr {String} The key.
- * @param item {} The object to remove.
- */
-
- }, {
- key: 'remove',
- value: function (attr, item) {
- return this.set(attr, new _ParseOp.RemoveOp([item]));
- }
-
- /**
- * Returns an instance of a subclass of Parse.Op describing what kind of
- * modification has been performed on this field since the last time it was
- * saved. For example, after calling object.increment("x"), calling
- * object.op("x") would return an instance of Parse.Op.Increment.
- *
- * @method op
- * @param attr {String} The key.
- * @returns {Parse.Op} The operation, or undefined if none.
- */
-
- }, {
- key: 'op',
- value: function (attr) {
- var pending = this._getPendingOps();
- for (var i = pending.length; i--;) {
- if (pending[i][attr]) {
- return pending[i][attr];
- }
- }
- }
-
- /**
- * Creates a new model with identical attributes to this one, similar to Backbone.Model's clone()
- * @method clone
- * @return {Parse.Object}
- */
-
- }, {
- key: 'clone',
- value: function () {
- var clone = new this.constructor();
- if (!clone.className) {
- clone.className = this.className;
- }
- var attributes = this.attributes;
- if (typeof this.constructor.readOnlyAttributes === 'function') {
- var readonly = this.constructor.readOnlyAttributes() || [];
- // Attributes are frozen, so we have to rebuild an object,
- // rather than delete readonly keys
- var copy = {};
- for (var a in attributes) {
- if (readonly.indexOf(a) < 0) {
- copy[a] = attributes[a];
- }
- }
- attributes = copy;
- }
- if (clone.set) {
- clone.set(attributes);
- }
- return clone;
- }
-
- /**
- * Creates a new instance of this object. Not to be confused with clone()
- * @method newInstance
- * @return {Parse.Object}
- */
-
- }, {
- key: 'newInstance',
- value: function () {
- var clone = new this.constructor();
- if (!clone.className) {
- clone.className = this.className;
- }
- clone.id = this.id;
- if (singleInstance) {
- // Just return an object with the right id
- return clone;
- }
-
- var stateController = _CoreManager2.default.getObjectStateController();
- if (stateController) {
- stateController.duplicateState(this._getStateIdentifier(), clone._getStateIdentifier());
- }
- return clone;
- }
-
- /**
- * Returns true if this object has never been saved to Parse.
- * @method isNew
- * @return {Boolean}
- */
-
- }, {
- key: 'isNew',
- value: function () {
- return !this.id;
- }
-
- /**
- * Returns true if this object was created by the Parse server when the
- * object might have already been there (e.g. in the case of a Facebook
- * login)
- * @method existed
- * @return {Boolean}
- */
-
- }, {
- key: 'existed',
- value: function () {
- if (!this.id) {
- return false;
- }
- var stateController = _CoreManager2.default.getObjectStateController();
- var state = stateController.getState(this._getStateIdentifier());
- if (state) {
- return state.existed;
- }
- return false;
- }
-
- /**
- * Checks if the model is currently in a valid state.
- * @method isValid
- * @return {Boolean}
- */
-
- }, {
- key: 'isValid',
- value: function () {
- return !this.validate(this.attributes);
- }
-
- /**
- * You should not call this function directly unless you subclass
- * Parse.Object
, in which case you can override this method
- * to provide additional validation on set
and
- * save
. Your implementation should return
- *
- * @method validate
- * @param {Object} attrs The current data to validate.
- * @return {} False if the data is valid. An error object otherwise.
- * @see Parse.Object#set
- */
-
- }, {
- key: 'validate',
- value: function (attrs) {
- if (attrs.hasOwnProperty('ACL') && !(attrs.ACL instanceof _ParseACL2.default)) {
- return new _ParseError2.default(_ParseError2.default.OTHER_CAUSE, 'ACL must be a Parse ACL.');
- }
- for (var key in attrs) {
- if (!/^[A-Za-z][0-9A-Za-z_]*$/.test(key)) {
- return new _ParseError2.default(_ParseError2.default.INVALID_KEY_NAME);
- }
- }
- return false;
- }
-
- /**
- * Returns the ACL for this object.
- * @method getACL
- * @returns {Parse.ACL} An instance of Parse.ACL.
- * @see Parse.Object#get
- */
-
- }, {
- key: 'getACL',
- value: function () {
- var acl = this.get('ACL');
- if (acl instanceof _ParseACL2.default) {
- return acl;
- }
- return null;
- }
-
- /**
- * Sets the ACL to be used for this object.
- * @method setACL
- * @param {Parse.ACL} acl An instance of Parse.ACL.
- * @param {Object} options Optional Backbone-like options object to be
- * passed in to set.
- * @return {Boolean} Whether the set passed validation.
- * @see Parse.Object#set
- */
-
- }, {
- key: 'setACL',
- value: function (acl, options) {
- return this.set('ACL', acl, options);
- }
-
- /**
- * Clears any changes to this object made since the last call to save()
- * @method revert
- */
-
- }, {
- key: 'revert',
- value: function () {
- this._clearPendingOps();
- }
-
- /**
- * Clears all attributes on a model
- * @method clear
- */
-
- }, {
- key: 'clear',
- value: function () {
- var attributes = this.attributes;
- var erasable = {};
- var readonly = ['createdAt', 'updatedAt'];
- if (typeof this.constructor.readOnlyAttributes === 'function') {
- readonly = readonly.concat(this.constructor.readOnlyAttributes());
- }
- for (var attr in attributes) {
- if (readonly.indexOf(attr) < 0) {
- erasable[attr] = true;
- }
- }
- return this.set(erasable, { unset: true });
- }
-
- /**
- * Fetch the model from the server. If the server's representation of the
- * model differs from its current attributes, they will be overriden.
- *
- * @method fetch
- * @param {Object} options A Backbone-style callback object.
- * Valid options are:- * object.save();- * or
- * object.save(null, options);- * or
- * object.save(attrs, options);- * or
- * object.save(key, value, options);- * - * For example,
- * gameTurn.save({ - * player: "Jake Cutter", - * diceRoll: 2 - * }, { - * success: function(gameTurnAgain) { - * // The save was successful. - * }, - * error: function(gameTurnAgain, error) { - * // The save failed. Error is an instance of Parse.Error. - * } - * });- * or with promises:
- * gameTurn.save({ - * player: "Jake Cutter", - * diceRoll: 2 - * }).then(function(gameTurnAgain) { - * // The save was successful. - * }, function(error) { - * // The save failed. Error is an instance of Parse.Error. - * });- * - * @method save - * @param {Object} options A Backbone-style callback object. - * Valid options are:
- * Parse.Object.fetchAll([object1, object2, ...], { - * success: function(list) { - * // All the objects were fetched. - * }, - * error: function(error) { - * // An error occurred while fetching one of the objects. - * }, - * }); - *- * - * @method fetchAll - * @param {Array} list A list of
Parse.Object
.
- * @param {Object} options A Backbone-style callback object.
- * @static
- * Valid options are:- * Parse.Object.fetchAllIfNeeded([object1, ...], { - * success: function(list) { - * // Objects were fetched and updated. - * }, - * error: function(error) { - * // An error occurred while fetching one of the objects. - * }, - * }); - *- * - * @method fetchAllIfNeeded - * @param {Array} list A list of
Parse.Object
.
- * @param {Object} options A Backbone-style callback object.
- * @static
- * Valid options are:Unlike saveAll, if an error occurs while deleting an individual model, - * this method will continue trying to delete the rest of the models if - * possible, except in the case of a fatal error like a connection error. - * - *
In particular, the Parse.Error object returned in the case of error may - * be one of two types: - * - *
- * Parse.Object.destroyAll([object1, object2, ...], { - * success: function() { - * // All the objects were deleted. - * }, - * error: function(error) { - * // An error occurred while deleting one or more of the objects. - * // If this is an aggregate error, then we can inspect each error - * // object individually to determine the reason why a particular - * // object was not deleted. - * if (error.code === Parse.Error.AGGREGATE_ERROR) { - * for (var i = 0; i < error.errors.length; i++) { - * console.log("Couldn't delete " + error.errors[i].object.id + - * "due to " + error.errors[i].message); - * } - * } else { - * console.log("Delete aborted because of " + error.message); - * } - * }, - * }); - *- * - * @method destroyAll - * @param {Array} list A list of
Parse.Object
.
- * @param {Object} options A Backbone-style callback object.
- * @static
- * Valid options are:- * Parse.Object.saveAll([object1, object2, ...], { - * success: function(list) { - * // All the objects were saved. - * }, - * error: function(error) { - * // An error occurred while saving one of the objects. - * }, - * }); - *- * - * @method saveAll - * @param {Array} list A list of
Parse.Object
.
- * @param {Object} options A Backbone-style callback object.
- * @static
- * Valid options are:A shortcut for:
- * var Foo = Parse.Object.extend("Foo"); - * var pointerToFoo = new Foo(); - * pointerToFoo.id = "myObjectId"; - *- * - * @method createWithoutData - * @param {String} id The ID of the object to create a reference to. - * @static - * @return {Parse.Object} A Parse.Object reference. - */ - - }, { - key: 'createWithoutData', - value: function (id) { - var obj = new this(); - obj.id = id; - return obj; - } - - /** - * Creates a new instance of a Parse Object from a JSON representation. - * @method fromJSON - * @param {Object} json The JSON map of the Object's data - * @param {boolean} override In single instance mode, all old server data - * is overwritten if this is set to true - * @static - * @return {Parse.Object} A Parse.Object reference - */ - - }, { - key: 'fromJSON', - value: function (json, override) { - if (!json.className) { - throw new Error('Cannot create an object without a className'); - } - var constructor = classMap[json.className]; - var o = constructor ? new constructor() : new ParseObject(json.className); - var otherAttributes = {}; - for (var attr in json) { - if (attr !== 'className' && attr !== '__type') { - otherAttributes[attr] = json[attr]; - } - } - if (override) { - // id needs to be set before clearServerData can work - if (otherAttributes.objectId) { - o.id = otherAttributes.objectId; - } - var preserved = null; - if (typeof o._preserveFieldsOnFetch === 'function') { - preserved = o._preserveFieldsOnFetch(); - } - o._clearServerData(); - if (preserved) { - o._finishFetch(preserved); - } - } - o._finishFetch(otherAttributes); - if (json.objectId) { - o._setExisted(true); - } - return o; - } - - /** - * Registers a subclass of Parse.Object with a specific class name. - * When objects of that class are retrieved from a query, they will be - * instantiated with this subclass. - * This is only necessary when using ES6 subclassing. - * @method registerSubclass - * @param {String} className The class name of the subclass - * @param {Class} constructor The subclass - */ - - }, { - key: 'registerSubclass', - value: function (className, constructor) { - if (typeof className !== 'string') { - throw new TypeError('The first argument must be a valid class name.'); - } - if (typeof constructor === 'undefined') { - throw new TypeError('You must supply a subclass constructor.'); - } - if (typeof constructor !== 'function') { - throw new TypeError('You must register the subclass constructor. ' + 'Did you attempt to register an instance of the subclass?'); - } - classMap[className] = constructor; - if (!constructor.className) { - constructor.className = className; - } - } - - /** - * Creates a new subclass of Parse.Object for the given Parse class name. - * - *
Every extension of a Parse class will inherit from the most recent - * previous extension of that class. When a Parse.Object is automatically - * created by parsing JSON, it will use the most recent extension of that - * class.
- * - *You should call either:
- * var MyClass = Parse.Object.extend("MyClass", { - * Instance methods, - * initialize: function(attrs, options) { - * this.someInstanceProperty = [], - * Other instance properties - * } - * }, { - * Class properties - * });- * or, for Backbone compatibility:
- * var MyClass = Parse.Object.extend({ - * className: "MyClass", - * Instance methods, - * initialize: function(attrs, options) { - * this.someInstanceProperty = [], - * Other instance properties - * } - * }, { - * Class properties - * });- * - * @method extend - * @param {String} className The name of the Parse class backing this model. - * @param {Object} protoProps Instance properties to add to instances of the - * class returned from this method. - * @param {Object} classProps Class properties to add the class returned from - * this method. - * @return {Class} A new subclass of Parse.Object. - */ - - }, { - key: 'extend', - value: function (className, protoProps, classProps) { - if (typeof className !== 'string') { - if (className && typeof className.className === 'string') { - return ParseObject.extend(className.className, className, protoProps); - } else { - throw new Error('Parse.Object.extend\'s first argument should be the className.'); - } - } - var adjustedClassName = className; - - if (adjustedClassName === 'User' && _CoreManager2.default.get('PERFORM_USER_REWRITE')) { - adjustedClassName = '_User'; - } - - var parentProto = ParseObject.prototype; - if (this.hasOwnProperty('__super__') && this.__super__) { - parentProto = this.prototype; - } else if (classMap[adjustedClassName]) { - parentProto = classMap[adjustedClassName].prototype; - } - var ParseObjectSubclass = function (attributes, options) { - this.className = adjustedClassName; - this._objCount = objectCount++; - // Enable legacy initializers - if (typeof this.initialize === 'function') { - this.initialize.apply(this, arguments); - } - - if (attributes && (typeof attributes === 'undefined' ? 'undefined' : (0, _typeof3.default)(attributes)) === 'object') { - if (!this.set(attributes || {}, options)) { - throw new Error('Can\'t create an invalid Parse Object'); - } - } - }; - ParseObjectSubclass.className = adjustedClassName; - ParseObjectSubclass.__super__ = parentProto; - - ParseObjectSubclass.prototype = (0, _create2.default)(parentProto, { - constructor: { - value: ParseObjectSubclass, - enumerable: false, - writable: true, - configurable: true - } - }); - - if (protoProps) { - for (var prop in protoProps) { - if (prop !== 'className') { - (0, _defineProperty2.default)(ParseObjectSubclass.prototype, prop, { - value: protoProps[prop], - enumerable: false, - writable: true, - configurable: true - }); - } - } - } - - if (classProps) { - for (var prop in classProps) { - if (prop !== 'className') { - (0, _defineProperty2.default)(ParseObjectSubclass, prop, { - value: classProps[prop], - enumerable: false, - writable: true, - configurable: true - }); - } - } - } - - ParseObjectSubclass.extend = function (name, protoProps, classProps) { - if (typeof name === 'string') { - return ParseObject.extend.call(ParseObjectSubclass, name, protoProps, classProps); - } - return ParseObject.extend.call(ParseObjectSubclass, adjustedClassName, name, protoProps); - }; - ParseObjectSubclass.createWithoutData = ParseObject.createWithoutData; - - classMap[adjustedClassName] = ParseObjectSubclass; - return ParseObjectSubclass; - } - - /** - * Enable single instance objects, where any local objects with the same Id - * share the same attributes, and stay synchronized with each other. - * This is disabled by default in server environments, since it can lead to - * security issues. - * @method enableSingleInstance - */ - - }, { - key: 'enableSingleInstance', - value: function () { - singleInstance = true; - _CoreManager2.default.setObjectStateController(SingleInstanceStateController); - } - - /** - * Disable single instance objects, where any local objects with the same Id - * share the same attributes, and stay synchronized with each other. - * When disabled, you can have two instances of the same object in memory - * without them sharing attributes. - * @method disableSingleInstance - */ - - }, { - key: 'disableSingleInstance', - value: function () { - singleInstance = false; - _CoreManager2.default.setObjectStateController(UniqueInstanceStateController); - } - }]); - return ParseObject; -}(); - -exports.default = ParseObject; - -var DefaultController = { - fetch: function (target, forceFetch, options) { - if (Array.isArray(target)) { - if (target.length < 1) { - return _ParsePromise2.default.as([]); - } - var objs = []; - var ids = []; - var className = null; - var results = []; - var error = null; - target.forEach(function (el, i) { - if (error) { - return; - } - if (!className) { - className = el.className; - } - if (className !== el.className) { - error = new _ParseError2.default(_ParseError2.default.INVALID_CLASS_NAME, 'All objects should be of the same class'); - } - if (!el.id) { - error = new _ParseError2.default(_ParseError2.default.MISSING_OBJECT_ID, 'All objects must have an ID'); - } - if (forceFetch || (0, _keys2.default)(el._getServerData()).length === 0) { - ids.push(el.id); - objs.push(el); - } - results.push(el); - }); - if (error) { - return _ParsePromise2.default.error(error); - } - var query = new _ParseQuery2.default(className); - query.containedIn('objectId', ids); - query._limit = ids.length; - return query.find(options).then(function (objects) { - var idMap = {}; - objects.forEach(function (o) { - idMap[o.id] = o; - }); - for (var i = 0; i < objs.length; i++) { - var obj = objs[i]; - if (!obj || !obj.id || !idMap[obj.id]) { - if (forceFetch) { - return _ParsePromise2.default.error(new _ParseError2.default(_ParseError2.default.OBJECT_NOT_FOUND, 'All objects must exist on the server.')); - } - } - } - if (!singleInstance) { - // If single instance objects are disabled, we need to replace the - for (var i = 0; i < results.length; i++) { - var obj = results[i]; - if (obj && obj.id && idMap[obj.id]) { - var id = obj.id; - obj._finishFetch(idMap[id].toJSON()); - results[i] = idMap[id]; - } - } - } - return _ParsePromise2.default.as(results); - }); - } else { - var RESTController = _CoreManager2.default.getRESTController(); - return RESTController.request('GET', 'classes/' + target.className + '/' + target._getId(), {}, options).then(function (response, status, xhr) { - if (target instanceof ParseObject) { - target._clearPendingOps(); - target._clearServerData(); - target._finishFetch(response); - } - return target; - }); - } - }, - destroy: function (target, options) { - var RESTController = _CoreManager2.default.getRESTController(); - if (Array.isArray(target)) { - if (target.length < 1) { - return _ParsePromise2.default.as([]); - } - var batches = [[]]; - target.forEach(function (obj) { - if (!obj.id) { - return; - } - batches[batches.length - 1].push(obj); - if (batches[batches.length - 1].length >= 20) { - batches.push([]); - } - }); - if (batches[batches.length - 1].length === 0) { - // If the last batch is empty, remove it - batches.pop(); - } - var deleteCompleted = _ParsePromise2.default.as(); - var errors = []; - batches.forEach(function (batch) { - deleteCompleted = deleteCompleted.then(function () { - return RESTController.request('POST', 'batch', { - requests: batch.map(function (obj) { - return { - method: 'DELETE', - path: getServerUrlPath() + 'classes/' + obj.className + '/' + obj._getId(), - body: {} - }; - }) - }, options).then(function (results) { - for (var i = 0; i < results.length; i++) { - if (results[i] && results[i].hasOwnProperty('error')) { - var err = new _ParseError2.default(results[i].error.code, results[i].error.error); - err.object = batch[i]; - errors.push(err); - } - } - }); - }); - }); - return deleteCompleted.then(function () { - if (errors.length) { - var aggregate = new _ParseError2.default(_ParseError2.default.AGGREGATE_ERROR); - aggregate.errors = errors; - return _ParsePromise2.default.error(aggregate); - } - return _ParsePromise2.default.as(target); - }); - } else if (target instanceof ParseObject) { - return RESTController.request('DELETE', 'classes/' + target.className + '/' + target._getId(), {}, options).then(function () { - return _ParsePromise2.default.as(target); - }); - } - return _ParsePromise2.default.as(target); - }, - save: function (target, options) { - var RESTController = _CoreManager2.default.getRESTController(); - var stateController = _CoreManager2.default.getObjectStateController(); - if (Array.isArray(target)) { - if (target.length < 1) { - return _ParsePromise2.default.as([]); - } - - var unsaved = target.concat(); - for (var i = 0; i < target.length; i++) { - if (target[i] instanceof ParseObject) { - unsaved = unsaved.concat((0, _unsavedChildren2.default)(target[i], true)); - } - } - unsaved = (0, _unique2.default)(unsaved); - - var filesSaved = _ParsePromise2.default.as(); - var pending = []; - unsaved.forEach(function (el) { - if (el instanceof _ParseFile2.default) { - filesSaved = filesSaved.then(function () { - return el.save(); - }); - } else if (el instanceof ParseObject) { - pending.push(el); - } - }); - - return filesSaved.then(function () { - var objectError = null; - return _ParsePromise2.default._continueWhile(function () { - return pending.length > 0; - }, function () { - var batch = []; - var nextPending = []; - pending.forEach(function (el) { - if (batch.length < 20 && (0, _canBeSerialized2.default)(el)) { - batch.push(el); - } else { - nextPending.push(el); - } - }); - pending = nextPending; - if (batch.length < 1) { - return _ParsePromise2.default.error(new _ParseError2.default(_ParseError2.default.OTHER_CAUSE, 'Tried to save a batch with a cycle.')); - } - - // Queue up tasks for each object in the batch. - // When every task is ready, the API request will execute - var batchReturned = new _ParsePromise2.default(); - var batchReady = []; - var batchTasks = []; - batch.forEach(function (obj, index) { - var ready = new _ParsePromise2.default(); - batchReady.push(ready); - - stateController.pushPendingState(obj._getStateIdentifier()); - batchTasks.push(stateController.enqueueTask(obj._getStateIdentifier(), function () { - ready.resolve(); - return batchReturned.then(function (responses, status) { - if (responses[index].hasOwnProperty('success')) { - obj._handleSaveResponse(responses[index].success, status); - } else { - if (!objectError && responses[index].hasOwnProperty('error')) { - var serverError = responses[index].error; - objectError = new _ParseError2.default(serverError.code, serverError.error); - // Cancel the rest of the save - pending = []; - } - obj._handleSaveError(); - } - }); - })); - }); - - _ParsePromise2.default.when(batchReady).then(function () { - // Kick off the batch request - return RESTController.request('POST', 'batch', { - requests: batch.map(function (obj) { - var params = obj._getSaveParams(); - params.path = getServerUrlPath() + params.path; - return params; - }) - }, options); - }).then(function (response, status, xhr) { - batchReturned.resolve(response, status); - }); - - return _ParsePromise2.default.when(batchTasks); - }).then(function () { - if (objectError) { - return _ParsePromise2.default.error(objectError); - } - return _ParsePromise2.default.as(target); - }); - }); - } else if (target instanceof ParseObject) { - // copying target lets Flow guarantee the pointer isn't modified elsewhere - var targetCopy = target; - var task = function () { - var params = targetCopy._getSaveParams(); - return RESTController.request(params.method, params.path, params.body, options).then(function (response, status) { - targetCopy._handleSaveResponse(response, status); - }, function (error) { - targetCopy._handleSaveError(); - return _ParsePromise2.default.error(error); - }); - }; - - stateController.pushPendingState(target._getStateIdentifier()); - return stateController.enqueueTask(target._getStateIdentifier(), task).then(function () { - return target; - }, function (error) { - return _ParsePromise2.default.error(error); - }); - } - return _ParsePromise2.default.as(); - } -}; - -_CoreManager2.default.setObjectController(DefaultController); -},{"./CoreManager":3,"./ParseACL":11,"./ParseError":13,"./ParseFile":14,"./ParseOp":19,"./ParsePromise":20,"./ParseQuery":21,"./ParseRelation":22,"./SingleInstanceStateController":28,"./UniqueInstanceStateController":32,"./canBeSerialized":34,"./decode":35,"./encode":36,"./equals":37,"./escape":38,"./parseDate":40,"./unique":41,"./unsavedChildren":42,"babel-runtime/core-js/json/stringify":44,"babel-runtime/core-js/object/create":46,"babel-runtime/core-js/object/define-property":47,"babel-runtime/core-js/object/freeze":48,"babel-runtime/core-js/object/keys":51,"babel-runtime/helpers/classCallCheck":56,"babel-runtime/helpers/createClass":57,"babel-runtime/helpers/typeof":61}],19:[function(_dereq_,module,exports){ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.RelationOp = exports.RemoveOp = exports.AddUniqueOp = exports.AddOp = exports.IncrementOp = exports.UnsetOp = exports.SetOp = exports.Op = undefined; - -var _getPrototypeOf = _dereq_('babel-runtime/core-js/object/get-prototype-of'); - -var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); - -var _possibleConstructorReturn2 = _dereq_('babel-runtime/helpers/possibleConstructorReturn'); - -var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); - -var _inherits2 = _dereq_('babel-runtime/helpers/inherits'); - -var _inherits3 = _interopRequireDefault(_inherits2); - -var _classCallCheck2 = _dereq_('babel-runtime/helpers/classCallCheck'); - -var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); - -var _createClass2 = _dereq_('babel-runtime/helpers/createClass'); - -var _createClass3 = _interopRequireDefault(_createClass2); - -exports.opFromJSON = opFromJSON; - -var _arrayContainsObject = _dereq_('./arrayContainsObject'); - -var _arrayContainsObject2 = _interopRequireDefault(_arrayContainsObject); - -var _decode = _dereq_('./decode'); - -var _decode2 = _interopRequireDefault(_decode); - -var _encode = _dereq_('./encode'); - -var _encode2 = _interopRequireDefault(_encode); - -var _ParseObject = _dereq_('./ParseObject'); - -var _ParseObject2 = _interopRequireDefault(_ParseObject); - -var _ParseRelation = _dereq_('./ParseRelation'); - -var _ParseRelation2 = _interopRequireDefault(_ParseRelation); - -var _unique = _dereq_('./unique'); - -var _unique2 = _interopRequireDefault(_unique); - -function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { default: obj }; -} - -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * - */ - -function opFromJSON(json) { - if (!json || !json.__op) { - return null; - } - switch (json.__op) { - case 'Delete': - return new UnsetOp(); - case 'Increment': - return new IncrementOp(json.amount); - case 'Add': - return new AddOp((0, _decode2.default)(json.objects)); - case 'AddUnique': - return new AddUniqueOp((0, _decode2.default)(json.objects)); - case 'Remove': - return new RemoveOp((0, _decode2.default)(json.objects)); - case 'AddRelation': - var toAdd = (0, _decode2.default)(json.objects); - if (!Array.isArray(toAdd)) { - return new RelationOp([], []); - } - return new RelationOp(toAdd, []); - case 'RemoveRelation': - var toRemove = (0, _decode2.default)(json.objects); - if (!Array.isArray(toRemove)) { - return new RelationOp([], []); - } - return new RelationOp([], toRemove); - case 'Batch': - var toAdd = []; - var toRemove = []; - for (var i = 0; i < json.ops.length; i++) { - if (json.ops[i].__op === 'AddRelation') { - toAdd = toAdd.concat((0, _decode2.default)(json.ops[i].objects)); - } else if (json.ops[i].__op === 'RemoveRelation') { - toRemove = toRemove.concat((0, _decode2.default)(json.ops[i].objects)); - } - } - return new RelationOp(toAdd, toRemove); - } - return null; -} - -var Op = exports.Op = function () { - function Op() { - (0, _classCallCheck3.default)(this, Op); - } - - (0, _createClass3.default)(Op, [{ - key: 'applyTo', - - // Empty parent class - value: function (value) {} - }, { - key: 'mergeWith', - value: function (previous) {} - }, { - key: 'toJSON', - value: function () {} - }]); - return Op; -}(); - -var SetOp = exports.SetOp = function (_Op) { - (0, _inherits3.default)(SetOp, _Op); - - function SetOp(value) { - (0, _classCallCheck3.default)(this, SetOp); - - var _this = (0, _possibleConstructorReturn3.default)(this, (SetOp.__proto__ || (0, _getPrototypeOf2.default)(SetOp)).call(this)); - - _this._value = value; - return _this; - } - - (0, _createClass3.default)(SetOp, [{ - key: 'applyTo', - value: function (value) { - return this._value; - } - }, { - key: 'mergeWith', - value: function (previous) { - return new SetOp(this._value); - } - }, { - key: 'toJSON', - value: function () { - return (0, _encode2.default)(this._value, false, true); - } - }]); - return SetOp; -}(Op); - -var UnsetOp = exports.UnsetOp = function (_Op2) { - (0, _inherits3.default)(UnsetOp, _Op2); - - function UnsetOp() { - (0, _classCallCheck3.default)(this, UnsetOp); - return (0, _possibleConstructorReturn3.default)(this, (UnsetOp.__proto__ || (0, _getPrototypeOf2.default)(UnsetOp)).apply(this, arguments)); - } - - (0, _createClass3.default)(UnsetOp, [{ - key: 'applyTo', - value: function (value) { - return undefined; - } - }, { - key: 'mergeWith', - value: function (previous) { - return new UnsetOp(); - } - }, { - key: 'toJSON', - value: function () { - return { __op: 'Delete' }; - } - }]); - return UnsetOp; -}(Op); - -var IncrementOp = exports.IncrementOp = function (_Op3) { - (0, _inherits3.default)(IncrementOp, _Op3); - - function IncrementOp(amount) { - (0, _classCallCheck3.default)(this, IncrementOp); - - var _this3 = (0, _possibleConstructorReturn3.default)(this, (IncrementOp.__proto__ || (0, _getPrototypeOf2.default)(IncrementOp)).call(this)); - - if (typeof amount !== 'number') { - throw new TypeError('Increment Op must be initialized with a numeric amount.'); - } - _this3._amount = amount; - return _this3; - } - - (0, _createClass3.default)(IncrementOp, [{ - key: 'applyTo', - value: function (value) { - if (typeof value === 'undefined') { - return this._amount; - } - if (typeof value !== 'number') { - throw new TypeError('Cannot increment a non-numeric value.'); - } - return this._amount + value; - } - }, { - key: 'mergeWith', - value: function (previous) { - if (!previous) { - return this; - } - if (previous instanceof SetOp) { - return new SetOp(this.applyTo(previous._value)); - } - if (previous instanceof UnsetOp) { - return new SetOp(this._amount); - } - if (previous instanceof IncrementOp) { - return new IncrementOp(this.applyTo(previous._amount)); - } - throw new Error('Cannot merge Increment Op with the previous Op'); - } - }, { - key: 'toJSON', - value: function () { - return { __op: 'Increment', amount: this._amount }; - } - }]); - return IncrementOp; -}(Op); - -var AddOp = exports.AddOp = function (_Op4) { - (0, _inherits3.default)(AddOp, _Op4); - - function AddOp(value) { - (0, _classCallCheck3.default)(this, AddOp); - - var _this4 = (0, _possibleConstructorReturn3.default)(this, (AddOp.__proto__ || (0, _getPrototypeOf2.default)(AddOp)).call(this)); - - _this4._value = Array.isArray(value) ? value : [value]; - return _this4; - } - - (0, _createClass3.default)(AddOp, [{ - key: 'applyTo', - value: function (value) { - if (value == null) { - return this._value; - } - if (Array.isArray(value)) { - return value.concat(this._value); - } - throw new Error('Cannot add elements to a non-array value'); - } - }, { - key: 'mergeWith', - value: function (previous) { - if (!previous) { - return this; - } - if (previous instanceof SetOp) { - return new SetOp(this.applyTo(previous._value)); - } - if (previous instanceof UnsetOp) { - return new SetOp(this._value); - } - if (previous instanceof AddOp) { - return new AddOp(this.applyTo(previous._value)); - } - throw new Error('Cannot merge Add Op with the previous Op'); - } - }, { - key: 'toJSON', - value: function () { - return { __op: 'Add', objects: (0, _encode2.default)(this._value, false, true) }; - } - }]); - return AddOp; -}(Op); - -var AddUniqueOp = exports.AddUniqueOp = function (_Op5) { - (0, _inherits3.default)(AddUniqueOp, _Op5); - - function AddUniqueOp(value) { - (0, _classCallCheck3.default)(this, AddUniqueOp); - - var _this5 = (0, _possibleConstructorReturn3.default)(this, (AddUniqueOp.__proto__ || (0, _getPrototypeOf2.default)(AddUniqueOp)).call(this)); - - _this5._value = (0, _unique2.default)(Array.isArray(value) ? value : [value]); - return _this5; - } - - (0, _createClass3.default)(AddUniqueOp, [{ - key: 'applyTo', - value: function (value) { - if (value == null) { - return this._value || []; - } - if (Array.isArray(value)) { - // copying value lets Flow guarantee the pointer isn't modified elsewhere - var valueCopy = value; - var toAdd = []; - this._value.forEach(function (v) { - if (v instanceof _ParseObject2.default) { - if (!(0, _arrayContainsObject2.default)(valueCopy, v)) { - toAdd.push(v); - } - } else { - if (valueCopy.indexOf(v) < 0) { - toAdd.push(v); - } - } - }); - return value.concat(toAdd); - } - throw new Error('Cannot add elements to a non-array value'); - } - }, { - key: 'mergeWith', - value: function (previous) { - if (!previous) { - return this; - } - if (previous instanceof SetOp) { - return new SetOp(this.applyTo(previous._value)); - } - if (previous instanceof UnsetOp) { - return new SetOp(this._value); - } - if (previous instanceof AddUniqueOp) { - return new AddUniqueOp(this.applyTo(previous._value)); - } - throw new Error('Cannot merge AddUnique Op with the previous Op'); - } - }, { - key: 'toJSON', - value: function () { - return { __op: 'AddUnique', objects: (0, _encode2.default)(this._value, false, true) }; - } - }]); - return AddUniqueOp; -}(Op); - -var RemoveOp = exports.RemoveOp = function (_Op6) { - (0, _inherits3.default)(RemoveOp, _Op6); - - function RemoveOp(value) { - (0, _classCallCheck3.default)(this, RemoveOp); - - var _this6 = (0, _possibleConstructorReturn3.default)(this, (RemoveOp.__proto__ || (0, _getPrototypeOf2.default)(RemoveOp)).call(this)); - - _this6._value = (0, _unique2.default)(Array.isArray(value) ? value : [value]); - return _this6; - } - - (0, _createClass3.default)(RemoveOp, [{ - key: 'applyTo', - value: function (value) { - if (value == null) { - return []; - } - if (Array.isArray(value)) { - var i = value.indexOf(this._value); - var removed = value.concat([]); - for (var i = 0; i < this._value.length; i++) { - var index = removed.indexOf(this._value[i]); - while (index > -1) { - removed.splice(index, 1); - index = removed.indexOf(this._value[i]); - } - if (this._value[i] instanceof _ParseObject2.default && this._value[i].id) { - for (var j = 0; j < removed.length; j++) { - if (removed[j] instanceof _ParseObject2.default && this._value[i].id === removed[j].id) { - removed.splice(j, 1); - j--; - } - } - } - } - return removed; - } - throw new Error('Cannot remove elements from a non-array value'); - } - }, { - key: 'mergeWith', - value: function (previous) { - if (!previous) { - return this; - } - if (previous instanceof SetOp) { - return new SetOp(this.applyTo(previous._value)); - } - if (previous instanceof UnsetOp) { - return new UnsetOp(); - } - if (previous instanceof RemoveOp) { - var uniques = previous._value.concat([]); - for (var i = 0; i < this._value.length; i++) { - if (this._value[i] instanceof _ParseObject2.default) { - if (!(0, _arrayContainsObject2.default)(uniques, this._value[i])) { - uniques.push(this._value[i]); - } - } else { - if (uniques.indexOf(this._value[i]) < 0) { - uniques.push(this._value[i]); - } - } - } - return new RemoveOp(uniques); - } - throw new Error('Cannot merge Remove Op with the previous Op'); - } - }, { - key: 'toJSON', - value: function () { - return { __op: 'Remove', objects: (0, _encode2.default)(this._value, false, true) }; - } - }]); - return RemoveOp; -}(Op); - -var RelationOp = exports.RelationOp = function (_Op7) { - (0, _inherits3.default)(RelationOp, _Op7); - - function RelationOp(adds, removes) { - (0, _classCallCheck3.default)(this, RelationOp); - - var _this7 = (0, _possibleConstructorReturn3.default)(this, (RelationOp.__proto__ || (0, _getPrototypeOf2.default)(RelationOp)).call(this)); - - _this7._targetClassName = null; - - if (Array.isArray(adds)) { - _this7.relationsToAdd = (0, _unique2.default)(adds.map(_this7._extractId, _this7)); - } - - if (Array.isArray(removes)) { - _this7.relationsToRemove = (0, _unique2.default)(removes.map(_this7._extractId, _this7)); - } - return _this7; - } - - (0, _createClass3.default)(RelationOp, [{ - key: '_extractId', - value: function (obj) { - if (typeof obj === 'string') { - return obj; - } - if (!obj.id) { - throw new Error('You cannot add or remove an unsaved Parse Object from a relation'); - } - if (!this._targetClassName) { - this._targetClassName = obj.className; - } - if (this._targetClassName !== obj.className) { - throw new Error('Tried to create a Relation with 2 different object types: ' + this._targetClassName + ' and ' + obj.className + '.'); - } - return obj.id; - } - }, { - key: 'applyTo', - value: function (value, object, key) { - if (!value) { - if (!object || !key) { - throw new Error('Cannot apply a RelationOp without either a previous value, or an object and a key'); - } - var parent = new _ParseObject2.default(object.className); - if (object.id && object.id.indexOf('local') === 0) { - parent._localId = object.id; - } else if (object.id) { - parent.id = object.id; - } - var relation = new _ParseRelation2.default(parent, key); - relation.targetClassName = this._targetClassName; - return relation; - } - if (value instanceof _ParseRelation2.default) { - if (this._targetClassName) { - if (value.targetClassName) { - if (this._targetClassName !== value.targetClassName) { - throw new Error('Related object must be a ' + value.targetClassName + ', but a ' + this._targetClassName + ' was passed in.'); - } - } else { - value.targetClassName = this._targetClassName; - } - } - return value; - } else { - throw new Error('Relation cannot be applied to a non-relation field'); - } - } - }, { - key: 'mergeWith', - value: function (previous) { - if (!previous) { - return this; - } else if (previous instanceof UnsetOp) { - throw new Error('You cannot modify a relation after deleting it.'); - } else if (previous instanceof RelationOp) { - if (previous._targetClassName && previous._targetClassName !== this._targetClassName) { - throw new Error('Related object must be of class ' + previous._targetClassName + ', but ' + (this._targetClassName || 'null') + ' was passed in.'); - } - var newAdd = previous.relationsToAdd.concat([]); - this.relationsToRemove.forEach(function (r) { - var index = newAdd.indexOf(r); - if (index > -1) { - newAdd.splice(index, 1); - } - }); - this.relationsToAdd.forEach(function (r) { - var index = newAdd.indexOf(r); - if (index < 0) { - newAdd.push(r); - } - }); - - var newRemove = previous.relationsToRemove.concat([]); - this.relationsToAdd.forEach(function (r) { - var index = newRemove.indexOf(r); - if (index > -1) { - newRemove.splice(index, 1); - } - }); - this.relationsToRemove.forEach(function (r) { - var index = newRemove.indexOf(r); - if (index < 0) { - newRemove.push(r); - } - }); - - var newRelation = new RelationOp(newAdd, newRemove); - newRelation._targetClassName = this._targetClassName; - return newRelation; - } - throw new Error('Cannot merge Relation Op with the previous Op'); - } - }, { - key: 'toJSON', - value: function () { - var _this8 = this; - - var idToPointer = function (id) { - return { - __type: 'Pointer', - className: _this8._targetClassName, - objectId: id - }; - }; - - var adds = null; - var removes = null; - var pointers = null; - - if (this.relationsToAdd.length > 0) { - pointers = this.relationsToAdd.map(idToPointer); - adds = { __op: 'AddRelation', objects: pointers }; - } - if (this.relationsToRemove.length > 0) { - pointers = this.relationsToRemove.map(idToPointer); - removes = { __op: 'RemoveRelation', objects: pointers }; - } - - if (adds && removes) { - return { __op: 'Batch', ops: [adds, removes] }; - } - - return adds || removes || {}; - } - }]); - return RelationOp; -}(Op); -},{"./ParseObject":18,"./ParseRelation":22,"./arrayContainsObject":33,"./decode":35,"./encode":36,"./unique":41,"babel-runtime/core-js/object/get-prototype-of":50,"babel-runtime/helpers/classCallCheck":56,"babel-runtime/helpers/createClass":57,"babel-runtime/helpers/inherits":59,"babel-runtime/helpers/possibleConstructorReturn":60}],20:[function(_dereq_,module,exports){ -(function (process){ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _getIterator2 = _dereq_('babel-runtime/core-js/get-iterator'); - -var _getIterator3 = _interopRequireDefault(_getIterator2); - -var _typeof2 = _dereq_('babel-runtime/helpers/typeof'); - -var _typeof3 = _interopRequireDefault(_typeof2); - -var _classCallCheck2 = _dereq_('babel-runtime/helpers/classCallCheck'); - -var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); - -var _createClass2 = _dereq_('babel-runtime/helpers/createClass'); - -var _createClass3 = _interopRequireDefault(_createClass2); - -function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { default: obj }; -} - -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -var _isPromisesAPlusCompliant = true; - -/** - * A Promise is returned by async methods as a hook to provide callbacks to be - * called when the async task is fulfilled. - * - *
Typical usage would be like:
- * query.find().then(function(results) { - * results[0].set("foo", "bar"); - * return results[0].saveAsync(); - * }).then(function(result) { - * console.log("Updated " + result.id); - * }); - *- * - * @class Parse.Promise - * @constructor - */ - -var ParsePromise = function () { - function ParsePromise(executor) { - (0, _classCallCheck3.default)(this, ParsePromise); - - this._resolved = false; - this._rejected = false; - this._resolvedCallbacks = []; - this._rejectedCallbacks = []; - - if (typeof executor === 'function') { - executor(this.resolve.bind(this), this.reject.bind(this)); - } - } - - /** - * Marks this promise as fulfilled, firing any callbacks waiting on it. - * @method resolve - * @param {Object} result the result to pass to the callbacks. - */ - - (0, _createClass3.default)(ParsePromise, [{ - key: 'resolve', - value: function () { - if (this._resolved || this._rejected) { - throw new Error('A promise was resolved even though it had already been ' + (this._resolved ? 'resolved' : 'rejected') + '.'); - } - this._resolved = true; - - for (var _len = arguments.length, results = Array(_len), _key = 0; _key < _len; _key++) { - results[_key] = arguments[_key]; - } - - this._result = results; - for (var i = 0; i < this._resolvedCallbacks.length; i++) { - this._resolvedCallbacks[i].apply(this, results); - } - - this._resolvedCallbacks = []; - this._rejectedCallbacks = []; - } - - /** - * Marks this promise as fulfilled, firing any callbacks waiting on it. - * @method reject - * @param {Object} error the error to pass to the callbacks. - */ - - }, { - key: 'reject', - value: function (error) { - if (this._resolved || this._rejected) { - throw new Error('A promise was rejected even though it had already been ' + (this._resolved ? 'resolved' : 'rejected') + '.'); - } - this._rejected = true; - this._error = error; - for (var i = 0; i < this._rejectedCallbacks.length; i++) { - this._rejectedCallbacks[i](error); - } - this._resolvedCallbacks = []; - this._rejectedCallbacks = []; - } - - /** - * Adds callbacks to be called when this promise is fulfilled. Returns a new - * Promise that will be fulfilled when the callback is complete. It allows - * chaining. If the callback itself returns a Promise, then the one returned - * by "then" will not be fulfilled until that one returned by the callback - * is fulfilled. - * @method then - * @param {Function} resolvedCallback Function that is called when this - * Promise is resolved. Once the callback is complete, then the Promise - * returned by "then" will also be fulfilled. - * @param {Function} rejectedCallback Function that is called when this - * Promise is rejected with an error. Once the callback is complete, then - * the promise returned by "then" with be resolved successfully. If - * rejectedCallback is null, or it returns a rejected Promise, then the - * Promise returned by "then" will be rejected with that error. - * @return {Parse.Promise} A new Promise that will be fulfilled after this - * Promise is fulfilled and either callback has completed. If the callback - * returned a Promise, then this Promise will not be fulfilled until that - * one is. - */ - - }, { - key: 'then', - value: function (resolvedCallback, rejectedCallback) { - var _this = this; - - var promise = new ParsePromise(); - - var wrappedResolvedCallback = function () { - for (var _len2 = arguments.length, results = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { - results[_key2] = arguments[_key2]; - } - - if (typeof resolvedCallback === 'function') { - if (_isPromisesAPlusCompliant) { - try { - results = [resolvedCallback.apply(this, results)]; - } catch (e) { - results = [ParsePromise.error(e)]; - } - } else { - results = [resolvedCallback.apply(this, results)]; - } - } - if (results.length === 1 && ParsePromise.is(results[0])) { - results[0].then(function () { - promise.resolve.apply(promise, arguments); - }, function (error) { - promise.reject(error); - }); - } else { - promise.resolve.apply(promise, results); - } - }; - - var wrappedRejectedCallback = function (error) { - var result = []; - if (typeof rejectedCallback === 'function') { - if (_isPromisesAPlusCompliant) { - try { - result = [rejectedCallback(error)]; - } catch (e) { - result = [ParsePromise.error(e)]; - } - } else { - result = [rejectedCallback(error)]; - } - if (result.length === 1 && ParsePromise.is(result[0])) { - result[0].then(function () { - promise.resolve.apply(promise, arguments); - }, function (error) { - promise.reject(error); - }); - } else { - if (_isPromisesAPlusCompliant) { - promise.resolve.apply(promise, result); - } else { - promise.reject(result[0]); - } - } - } else { - promise.reject(error); - } - }; - - var runLater = function (fn) { - fn.call(); - }; - if (_isPromisesAPlusCompliant) { - if (typeof process !== 'undefined' && typeof process.nextTick === 'function') { - runLater = function (fn) { - process.nextTick(fn); - }; - } else if (typeof setTimeout === 'function') { - runLater = function (fn) { - setTimeout(fn, 0); - }; - } - } - - if (this._resolved) { - runLater(function () { - wrappedResolvedCallback.apply(_this, _this._result); - }); - } else if (this._rejected) { - runLater(function () { - wrappedRejectedCallback(_this._error); - }); - } else { - this._resolvedCallbacks.push(wrappedResolvedCallback); - this._rejectedCallbacks.push(wrappedRejectedCallback); - } - - return promise; - } - - /** - * Add handlers to be called when the promise - * is either resolved or rejected - * @method always - */ - - }, { - key: 'always', - value: function (callback) { - return this.then(callback, callback); - } - - /** - * Add handlers to be called when the Promise object is resolved - * @method done - */ - - }, { - key: 'done', - value: function (callback) { - return this.then(callback); - } - - /** - * Add handlers to be called when the Promise object is rejected - * Alias for catch(). - * @method fail - */ - - }, { - key: 'fail', - value: function (callback) { - return this.then(null, callback); - } - - /** - * Add handlers to be called when the Promise object is rejected - * @method catch - */ - - }, { - key: 'catch', - value: function (callback) { - return this.then(null, callback); - } - - /** - * Run the given callbacks after this promise is fulfilled. - * @method _thenRunCallbacks - * @param optionsOrCallback {} A Backbone-style options callback, or a - * callback function. If this is an options object and contains a "model" - * attributes, that will be passed to error callbacks as the first argument. - * @param model {} If truthy, this will be passed as the first result of - * error callbacks. This is for Backbone-compatability. - * @return {Parse.Promise} A promise that will be resolved after the - * callbacks are run, with the same result as this. - */ - - }, { - key: '_thenRunCallbacks', - value: function (optionsOrCallback, model) { - var options = {}; - if (typeof optionsOrCallback === 'function') { - options.success = function (result) { - optionsOrCallback(result, null); - }; - options.error = function (error) { - optionsOrCallback(null, error); - }; - } else if ((typeof optionsOrCallback === 'undefined' ? 'undefined' : (0, _typeof3.default)(optionsOrCallback)) === 'object') { - if (typeof optionsOrCallback.success === 'function') { - options.success = optionsOrCallback.success; - } - if (typeof optionsOrCallback.error === 'function') { - options.error = optionsOrCallback.error; - } - } - - return this.then(function () { - for (var _len3 = arguments.length, results = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) { - results[_key3] = arguments[_key3]; - } - - if (options.success) { - options.success.apply(this, results); - } - return ParsePromise.as.apply(ParsePromise, arguments); - }, function (error) { - if (options.error) { - if (typeof model !== 'undefined') { - options.error(model, error); - } else { - options.error(error); - } - } - // By explicitly returning a rejected Promise, this will work with - // either jQuery or Promises/A+ semantics. - return ParsePromise.error(error); - }); - } - - /** - * Adds a callback function that should be called regardless of whether - * this promise failed or succeeded. The callback will be given either the - * array of results for its first argument, or the error as its second, - * depending on whether this Promise was rejected or resolved. Returns a - * new Promise, like "then" would. - * @method _continueWith - * @param {Function} continuation the callback. - */ - - }, { - key: '_continueWith', - value: function (continuation) { - return this.then(function () { - for (var _len4 = arguments.length, args = Array(_len4), _key4 = 0; _key4 < _len4; _key4++) { - args[_key4] = arguments[_key4]; - } - - return continuation(args, null); - }, function (error) { - return continuation(null, error); - }); - } - - /** - * Returns true iff the given object fulfils the Promise interface. - * @method is - * @param {Object} promise The object to test - * @static - * @return {Boolean} - */ - - }], [{ - key: 'is', - value: function (promise) { - return promise != null && typeof promise.then === 'function'; - } - - /** - * Returns a new promise that is resolved with a given value. - * @method as - * @param value The value to resolve the promise with - * @static - * @return {Parse.Promise} the new promise. - */ - - }, { - key: 'as', - value: function () { - var promise = new ParsePromise(); - - for (var _len5 = arguments.length, values = Array(_len5), _key5 = 0; _key5 < _len5; _key5++) { - values[_key5] = arguments[_key5]; - } - - promise.resolve.apply(promise, values); - return promise; - } - - /** - * Returns a new promise that is resolved with a given value. - * If that value is a thenable Promise (has a .then() prototype - * method), the new promise will be chained to the end of the - * value. - * @method resolve - * @param value The value to resolve the promise with - * @static - * @return {Parse.Promise} the new promise. - */ - - }, { - key: 'resolve', - value: function (value) { - return new ParsePromise(function (resolve, reject) { - if (ParsePromise.is(value)) { - value.then(resolve, reject); - } else { - resolve(value); - } - }); - } - - /** - * Returns a new promise that is rejected with a given error. - * @method error - * @param error The error to reject the promise with - * @static - * @return {Parse.Promise} the new promise. - */ - - }, { - key: 'error', - value: function () { - var promise = new ParsePromise(); - - for (var _len6 = arguments.length, errors = Array(_len6), _key6 = 0; _key6 < _len6; _key6++) { - errors[_key6] = arguments[_key6]; - } - - promise.reject.apply(promise, errors); - return promise; - } - - /** - * Returns a new promise that is rejected with a given error. - * This is an alias for Parse.Promise.error, for compliance with - * the ES6 implementation. - * @method reject - * @param error The error to reject the promise with - * @static - * @return {Parse.Promise} the new promise. - */ - - }, { - key: 'reject', - value: function () { - for (var _len7 = arguments.length, errors = Array(_len7), _key7 = 0; _key7 < _len7; _key7++) { - errors[_key7] = arguments[_key7]; - } - - return ParsePromise.error.apply(null, errors); - } - - /** - * Returns a new promise that is fulfilled when all of the input promises - * are resolved. If any promise in the list fails, then the returned promise - * will be rejected with an array containing the error from each promise. - * If they all succeed, then the returned promise will succeed, with the - * results being the results of all the input - * promises. For example:
- * var p1 = Parse.Promise.as(1); - * var p2 = Parse.Promise.as(2); - * var p3 = Parse.Promise.as(3); - * - * Parse.Promise.when(p1, p2, p3).then(function(r1, r2, r3) { - * console.log(r1); // prints 1 - * console.log(r2); // prints 2 - * console.log(r3); // prints 3 - * });- * - * The input promises can also be specified as an array:
- * var promises = [p1, p2, p3]; - * Parse.Promise.when(promises).then(function(results) { - * console.log(results); // prints [1,2,3] - * }); - *- * @method when - * @param {Array} promises a list of promises to wait for. - * @static - * @return {Parse.Promise} the new promise. - */ - - }, { - key: 'when', - value: function (promises) { - var objects; - var arrayArgument = Array.isArray(promises); - if (arrayArgument) { - objects = promises; - } else { - objects = arguments; - } - - var total = objects.length; - var hadError = false; - var results = []; - var returnValue = arrayArgument ? [results] : results; - var errors = []; - results.length = objects.length; - errors.length = objects.length; - - if (total === 0) { - return ParsePromise.as.apply(this, returnValue); - } - - var promise = new ParsePromise(); - - var resolveOne = function () { - total--; - if (total <= 0) { - if (hadError) { - promise.reject(errors); - } else { - promise.resolve.apply(promise, returnValue); - } - } - }; - - var chain = function (object, index) { - if (ParsePromise.is(object)) { - object.then(function (result) { - results[index] = result; - resolveOne(); - }, function (error) { - errors[index] = error; - hadError = true; - resolveOne(); - }); - } else { - results[i] = object; - resolveOne(); - } - }; - for (var i = 0; i < objects.length; i++) { - chain(objects[i], i); - } - - return promise; - } - - /** - * Returns a new promise that is fulfilled when all of the promises in the - * iterable argument are resolved. If any promise in the list fails, then - * the returned promise will be immediately rejected with the reason that - * single promise rejected. If they all succeed, then the returned promise - * will succeed, with the results being the results of all the input - * promises. If the iterable provided is empty, the returned promise will - * be immediately resolved. - * - * For example:
- * var p1 = Parse.Promise.as(1); - * var p2 = Parse.Promise.as(2); - * var p3 = Parse.Promise.as(3); - * - * Parse.Promise.all([p1, p2, p3]).then(function([r1, r2, r3]) { - * console.log(r1); // prints 1 - * console.log(r2); // prints 2 - * console.log(r3); // prints 3 - * });- * - * @method all - * @param {Iterable} promises an iterable of promises to wait for. - * @static - * @return {Parse.Promise} the new promise. - */ - - }, { - key: 'all', - value: function (promises) { - var total = 0; - var objects = []; - - var _iteratorNormalCompletion = true; - var _didIteratorError = false; - var _iteratorError = undefined; - - try { - for (var _iterator = (0, _getIterator3.default)(promises), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { - var p = _step.value; - - objects[total++] = p; - } - } catch (err) { - _didIteratorError = true; - _iteratorError = err; - } finally { - try { - if (!_iteratorNormalCompletion && _iterator.return) { - _iterator.return(); - } - } finally { - if (_didIteratorError) { - throw _iteratorError; - } - } - } - - if (total === 0) { - return ParsePromise.as([]); - } - - var hadError = false; - var promise = new ParsePromise(); - var resolved = 0; - var results = []; - objects.forEach(function (object, i) { - if (ParsePromise.is(object)) { - object.then(function (result) { - if (hadError) { - return false; - } - results[i] = result; - resolved++; - if (resolved >= total) { - promise.resolve(results); - } - }, function (error) { - // Reject immediately - promise.reject(error); - hadError = true; - }); - } else { - results[i] = object; - resolved++; - if (!hadError && resolved >= total) { - promise.resolve(results); - } - } - }); - - return promise; - } - - /** - * Returns a new promise that is immediately fulfilled when any of the - * promises in the iterable argument are resolved or rejected. If the - * first promise to complete is resolved, the returned promise will be - * resolved with the same value. Likewise, if the first promise to - * complete is rejected, the returned promise will be rejected with the - * same reason. - * - * @method race - * @param {Iterable} promises an iterable of promises to wait for. - * @static - * @return {Parse.Promise} the new promise. - */ - - }, { - key: 'race', - value: function (promises) { - var completed = false; - var promise = new ParsePromise(); - var _iteratorNormalCompletion2 = true; - var _didIteratorError2 = false; - var _iteratorError2 = undefined; - - try { - for (var _iterator2 = (0, _getIterator3.default)(promises), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { - var p = _step2.value; - - if (ParsePromise.is(p)) { - p.then(function (result) { - if (completed) { - return; - } - completed = true; - promise.resolve(result); - }, function (error) { - if (completed) { - return; - } - completed = true; - promise.reject(error); - }); - } else if (!completed) { - completed = true; - promise.resolve(p); - } - } - } catch (err) { - _didIteratorError2 = true; - _iteratorError2 = err; - } finally { - try { - if (!_iteratorNormalCompletion2 && _iterator2.return) { - _iterator2.return(); - } - } finally { - if (_didIteratorError2) { - throw _iteratorError2; - } - } - } - - return promise; - } - - /** - * Runs the given asyncFunction repeatedly, as long as the predicate - * function returns a truthy value. Stops repeating if asyncFunction returns - * a rejected promise. - * @method _continueWhile - * @param {Function} predicate should return false when ready to stop. - * @param {Function} asyncFunction should return a Promise. - * @static - */ - - }, { - key: '_continueWhile', - value: function (predicate, asyncFunction) { - if (predicate()) { - return asyncFunction().then(function () { - return ParsePromise._continueWhile(predicate, asyncFunction); - }); - } - return ParsePromise.as(); - } - }, { - key: 'isPromisesAPlusCompliant', - value: function () { - return _isPromisesAPlusCompliant; - } - }, { - key: 'enableAPlusCompliant', - value: function () { - _isPromisesAPlusCompliant = true; - } - }, { - key: 'disableAPlusCompliant', - value: function () { - _isPromisesAPlusCompliant = false; - } - }]); - return ParsePromise; -}(); - -exports.default = ParsePromise; -}).call(this,_dereq_('_process')) -},{"_process":168,"babel-runtime/core-js/get-iterator":43,"babel-runtime/helpers/classCallCheck":56,"babel-runtime/helpers/createClass":57,"babel-runtime/helpers/typeof":61}],21:[function(_dereq_,module,exports){ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _typeof2 = _dereq_('babel-runtime/helpers/typeof'); - -var _typeof3 = _interopRequireDefault(_typeof2); - -var _classCallCheck2 = _dereq_('babel-runtime/helpers/classCallCheck'); - -var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); - -var _createClass2 = _dereq_('babel-runtime/helpers/createClass'); - -var _createClass3 = _interopRequireDefault(_createClass2); - -var _keys = _dereq_('babel-runtime/core-js/object/keys'); - -var _keys2 = _interopRequireDefault(_keys); - -var _CoreManager = _dereq_('./CoreManager'); - -var _CoreManager2 = _interopRequireDefault(_CoreManager); - -var _encode = _dereq_('./encode'); - -var _encode2 = _interopRequireDefault(_encode); - -var _ParseError = _dereq_('./ParseError'); - -var _ParseError2 = _interopRequireDefault(_ParseError); - -var _ParseGeoPoint = _dereq_('./ParseGeoPoint'); - -var _ParseGeoPoint2 = _interopRequireDefault(_ParseGeoPoint); - -var _ParseObject = _dereq_('./ParseObject'); - -var _ParseObject2 = _interopRequireDefault(_ParseObject); - -var _ParsePromise = _dereq_('./ParsePromise'); - -var _ParsePromise2 = _interopRequireDefault(_ParsePromise); - -function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { default: obj }; -} - -/** - * Converts a string into a regex that matches it. - * Surrounding with \Q .. \E does this, we just need to escape any \E's in - * the text separately. - */ -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * - */ - -function quote(s) { - return '\\Q' + s.replace('\\E', '\\E\\\\E\\Q') + '\\E'; -} - -/** - * Handles pre-populating the result data of a query with select fields, - * making sure that the data object contains keys for all objects that have - * been requested with a select, so that our cached state updates correctly. - */ -function handleSelectResult(data, select) { - var serverDataMask = {}; - - select.forEach(function (field) { - var hasSubObjectSelect = field.indexOf(".") !== -1; - if (!hasSubObjectSelect && !data.hasOwnProperty(field)) { - // this field was selected, but is missing from the retrieved data - data[field] = undefined; - } else if (hasSubObjectSelect) { - // this field references a sub-object, - // so we need to walk down the path components - var pathComponents = field.split("."); - var obj = data; - var serverMask = serverDataMask; - - pathComponents.forEach(function (component, index, arr) { - // add keys if the expected data is missing - if (!obj[component]) { - obj[component] = index == arr.length - 1 ? undefined : {}; - } - obj = obj[component]; - - //add this path component to the server mask so we can fill it in later if needed - if (index < arr.length - 1) { - if (!serverMask[component]) { - serverMask[component] = {}; - } - } - }); - } - }); - - if ((0, _keys2.default)(serverDataMask).length > 0) { - var copyMissingDataWithMask = function copyMissingDataWithMask(src, dest, mask, copyThisLevel) { - //copy missing elements at this level - if (copyThisLevel) { - for (var key in src) { - if (src.hasOwnProperty(key) && !dest.hasOwnProperty(key)) { - dest[key] = src[key]; - } - } - } - for (var key in mask) { - //traverse into objects as needed - copyMissingDataWithMask(src[key], dest[key], mask[key], true); - } - }; - - // When selecting from sub-objects, we don't want to blow away the missing - // information that we may have retrieved before. We've already added any - // missing selected keys to sub-objects, but we still need to add in the - // data for any previously retrieved sub-objects that were not selected. - - var serverData = _CoreManager2.default.getObjectStateController().getServerData({ id: data.objectId, className: data.className }); - - copyMissingDataWithMask(serverData, data, serverDataMask, false); - } -} - -/** - * Creates a new parse Parse.Query for the given Parse.Object subclass. - * @class Parse.Query - * @constructor - * @param {} objectClass An instance of a subclass of Parse.Object, or a Parse className string. - * - *
Parse.Query defines a query that is used to fetch Parse.Objects. The
- * most common use case is finding all objects that match a query through the
- * find
method. For example, this sample code fetches all objects
- * of class MyClass
. It calls a different function depending on
- * whether the fetch succeeded or not.
- *
- *
- * var query = new Parse.Query(MyClass); - * query.find({ - * success: function(results) { - * // results is an array of Parse.Object. - * }, - * - * error: function(error) { - * // error is an instance of Parse.Error. - * } - * });- * - *
A Parse.Query can also be used to retrieve a single object whose id is
- * known, through the get method. For example, this sample code fetches an
- * object of class MyClass
and id myId
. It calls a
- * different function depending on whether the fetch succeeded or not.
- *
- *
- * var query = new Parse.Query(MyClass); - * query.get(myId, { - * success: function(object) { - * // object is an instance of Parse.Object. - * }, - * - * error: function(object, error) { - * // error is an instance of Parse.Error. - * } - * });- * - *
A Parse.Query can also be used to count the number of objects that match
- * the query without retrieving all of those objects. For example, this
- * sample code counts the number of objects of the class MyClass
- *
- * var query = new Parse.Query(MyClass); - * query.count({ - * success: function(number) { - * // There are number instances of MyClass. - * }, - * - * error: function(error) { - * // error is an instance of Parse.Error. - * } - * });- */ - -var ParseQuery = function () { - function ParseQuery(objectClass) { - (0, _classCallCheck3.default)(this, ParseQuery); - - if (typeof objectClass === 'string') { - if (objectClass === 'User' && _CoreManager2.default.get('PERFORM_USER_REWRITE')) { - this.className = '_User'; - } else { - this.className = objectClass; - } - } else if (objectClass instanceof _ParseObject2.default) { - this.className = objectClass.className; - } else if (typeof objectClass === 'function') { - if (typeof objectClass.className === 'string') { - this.className = objectClass.className; - } else { - var obj = new objectClass(); - this.className = obj.className; - } - } else { - throw new TypeError('A ParseQuery must be constructed with a ParseObject or class name.'); - } - - this._where = {}; - this._include = []; - this._limit = -1; // negative limit is not sent in the server request - this._skip = 0; - this._extraOptions = {}; - } - - /** - * Adds constraint that at least one of the passed in queries matches. - * @method _orQuery - * @param {Array} queries - * @return {Parse.Query} Returns the query, so you can chain this call. - */ - - (0, _createClass3.default)(ParseQuery, [{ - key: '_orQuery', - value: function (queries) { - var queryJSON = queries.map(function (q) { - return q.toJSON().where; - }); - - this._where.$or = queryJSON; - return this; - } - - /** - * Helper for condition queries - */ - - }, { - key: '_addCondition', - value: function (key, condition, value) { - if (!this._where[key] || typeof this._where[key] === 'string') { - this._where[key] = {}; - } - this._where[key][condition] = (0, _encode2.default)(value, false, true); - return this; - } - - /** - * Converts string for regular expression at the beginning - */ - - }, { - key: '_regexStartWith', - value: function (string) { - return '^' + quote(string); - } - - /** - * Returns a JSON representation of this query. - * @method toJSON - * @return {Object} The JSON representation of the query. - */ - - }, { - key: 'toJSON', - value: function () { - var params = { - where: this._where - }; - - if (this._include.length) { - params.include = this._include.join(','); - } - if (this._select) { - params.keys = this._select.join(','); - } - if (this._limit >= 0) { - params.limit = this._limit; - } - if (this._skip > 0) { - params.skip = this._skip; - } - if (this._order) { - params.order = this._order.join(','); - } - for (var key in this._extraOptions) { - params[key] = this._extraOptions[key]; - } - - return params; - } - - /** - * Constructs a Parse.Object whose id is already known by fetching data from - * the server. Either options.success or options.error is called when the - * find completes. - * - * @method get - * @param {String} objectId The id of the object to be fetched. - * @param {Object} options A Backbone-style options object. - * Valid options are:
var compoundQuery = Parse.Query.or(query1, query2, query3);- * - * will create a compoundQuery that is an or of the query1, query2, and - * query3. - * @method or - * @param {...Parse.Query} var_args The list of queries to OR. - * @static - * @return {Parse.Query} The query that is the OR of the passed in queries. - */ - - }], [{ - key: 'or', - value: function () { - var className = null; - - for (var _len7 = arguments.length, queries = Array(_len7), _key7 = 0; _key7 < _len7; _key7++) { - queries[_key7] = arguments[_key7]; - } - - queries.forEach(function (q) { - if (!className) { - className = q.className; - } - - if (className !== q.className) { - throw new Error('All queries must be for the same class.'); - } - }); - - var query = new ParseQuery(className); - query._orQuery(queries); - return query; - } - }]); - return ParseQuery; -}(); - -exports.default = ParseQuery; - -var DefaultController = { - find: function (className, params, options) { - var RESTController = _CoreManager2.default.getRESTController(); - - return RESTController.request('GET', 'classes/' + className, params, options); - } -}; - -_CoreManager2.default.setQueryController(DefaultController); -},{"./CoreManager":3,"./ParseError":13,"./ParseGeoPoint":15,"./ParseObject":18,"./ParsePromise":20,"./encode":36,"babel-runtime/core-js/object/keys":51,"babel-runtime/helpers/classCallCheck":56,"babel-runtime/helpers/createClass":57,"babel-runtime/helpers/typeof":61}],22:[function(_dereq_,module,exports){ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _classCallCheck2 = _dereq_('babel-runtime/helpers/classCallCheck'); - -var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); - -var _createClass2 = _dereq_('babel-runtime/helpers/createClass'); - -var _createClass3 = _interopRequireDefault(_createClass2); - -var _ParseOp = _dereq_('./ParseOp'); - -var _ParseObject = _dereq_('./ParseObject'); - -var _ParseObject2 = _interopRequireDefault(_ParseObject); - -var _ParseQuery = _dereq_('./ParseQuery'); - -var _ParseQuery2 = _interopRequireDefault(_ParseQuery); - -function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { default: obj }; -} - -/** - * Creates a new Relation for the given parent object and key. This - * constructor should rarely be used directly, but rather created by - * Parse.Object.relation. - * @class Parse.Relation - * @constructor - * @param {Parse.Object} parent The parent of this relation. - * @param {String} key The key for this relation on the parent. - * - *
- * A class that is used to access all of the children of a many-to-many - * relationship. Each instance of Parse.Relation is associated with a - * particular parent object and key. - *
- */ -var ParseRelation = function () { - function ParseRelation(parent, key) { - (0, _classCallCheck3.default)(this, ParseRelation); - - this.parent = parent; - this.key = key; - this.targetClassName = null; - } - - /** - * Makes sure that this relation has the right parent and key. - */ - - (0, _createClass3.default)(ParseRelation, [{ - key: '_ensureParentAndKey', - value: function (parent, key) { - this.key = this.key || key; - if (this.key !== key) { - throw new Error('Internal Error. Relation retrieved from two different keys.'); - } - if (this.parent) { - if (this.parent.className !== parent.className) { - throw new Error('Internal Error. Relation retrieved from two different Objects.'); - } - if (this.parent.id) { - if (this.parent.id !== parent.id) { - throw new Error('Internal Error. Relation retrieved from two different Objects.'); - } - } else if (parent.id) { - this.parent = parent; - } - } else { - this.parent = parent; - } - } - - /** - * Adds a Parse.Object or an array of Parse.Objects to the relation. - * @method add - * @param {} objects The item or items to add. - */ - - }, { - key: 'add', - value: function (objects) { - if (!Array.isArray(objects)) { - objects = [objects]; - } - - var change = new _ParseOp.RelationOp(objects, []); - var parent = this.parent; - if (!parent) { - throw new Error('Cannot add to a Relation without a parent'); - } - parent.set(this.key, change); - this.targetClassName = change._targetClassName; - return parent; - } - - /** - * Removes a Parse.Object or an array of Parse.Objects from this relation. - * @method remove - * @param {} objects The item or items to remove. - */ - - }, { - key: 'remove', - value: function (objects) { - if (!Array.isArray(objects)) { - objects = [objects]; - } - - var change = new _ParseOp.RelationOp([], objects); - if (!this.parent) { - throw new Error('Cannot remove from a Relation without a parent'); - } - this.parent.set(this.key, change); - this.targetClassName = change._targetClassName; - } - - /** - * Returns a JSON version of the object suitable for saving to disk. - * @method toJSON - * @return {Object} - */ - - }, { - key: 'toJSON', - value: function () { - return { - __type: 'Relation', - className: this.targetClassName - }; - } - - /** - * Returns a Parse.Query that is limited to objects in this - * relation. - * @method query - * @return {Parse.Query} - */ - - }, { - key: 'query', - value: function () { - var query; - var parent = this.parent; - if (!parent) { - throw new Error('Cannot construct a query for a Relation without a parent'); - } - if (!this.targetClassName) { - query = new _ParseQuery2.default(parent.className); - query._extraOptions.redirectClassNameForKey = this.key; - } else { - query = new _ParseQuery2.default(this.targetClassName); - } - query._addCondition('$relatedTo', 'object', { - __type: 'Pointer', - className: parent.className, - objectId: parent.id - }); - query._addCondition('$relatedTo', 'key', this.key); - - return query; - } - }]); - return ParseRelation; -}(); /** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * - */ - -exports.default = ParseRelation; -},{"./ParseObject":18,"./ParseOp":19,"./ParseQuery":21,"babel-runtime/helpers/classCallCheck":56,"babel-runtime/helpers/createClass":57}],23:[function(_dereq_,module,exports){ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _getPrototypeOf = _dereq_('babel-runtime/core-js/object/get-prototype-of'); - -var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); - -var _classCallCheck2 = _dereq_('babel-runtime/helpers/classCallCheck'); - -var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); - -var _createClass2 = _dereq_('babel-runtime/helpers/createClass'); - -var _createClass3 = _interopRequireDefault(_createClass2); - -var _possibleConstructorReturn2 = _dereq_('babel-runtime/helpers/possibleConstructorReturn'); - -var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); - -var _get2 = _dereq_('babel-runtime/helpers/get'); - -var _get3 = _interopRequireDefault(_get2); - -var _inherits2 = _dereq_('babel-runtime/helpers/inherits'); - -var _inherits3 = _interopRequireDefault(_inherits2); - -var _ParseACL = _dereq_('./ParseACL'); - -var _ParseACL2 = _interopRequireDefault(_ParseACL); - -var _ParseError = _dereq_('./ParseError'); - -var _ParseError2 = _interopRequireDefault(_ParseError); - -var _ParseObject2 = _dereq_('./ParseObject'); - -var _ParseObject3 = _interopRequireDefault(_ParseObject2); - -function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { default: obj }; -} - -/** - * Represents a Role on the Parse server. Roles represent groupings of - * Users for the purposes of granting permissions (e.g. specifying an ACL - * for an Object). Roles are specified by their sets of child users and - * child roles, all of which are granted any permissions that the parent - * role has. - * - *Roles must have a name (which cannot be changed after creation of the - * role), and must specify an ACL.
- * @class Parse.Role - * @constructor - * @param {String} name The name of the Role to create. - * @param {Parse.ACL} acl The ACL for this role. Roles must have an ACL. - * A Parse.Role is a local representation of a role persisted to the Parse - * cloud. - */ -var ParseRole = function (_ParseObject) { - (0, _inherits3.default)(ParseRole, _ParseObject); - - function ParseRole(name, acl) { - (0, _classCallCheck3.default)(this, ParseRole); - - var _this = (0, _possibleConstructorReturn3.default)(this, (ParseRole.__proto__ || (0, _getPrototypeOf2.default)(ParseRole)).call(this, '_Role')); - - if (typeof name === 'string' && acl instanceof _ParseACL2.default) { - _this.setName(name); - _this.setACL(acl); - } - return _this; - } - - /** - * Gets the name of the role. You can alternatively call role.get("name") - * - * @method getName - * @return {String} the name of the role. - */ - - (0, _createClass3.default)(ParseRole, [{ - key: 'getName', - value: function () { - var name = this.get('name'); - if (name == null || typeof name === 'string') { - return name; - } - return ''; - } - - /** - * Sets the name for a role. This value must be set before the role has - * been saved to the server, and cannot be set once the role has been - * saved. - * - *- * A role's name can only contain alphanumeric characters, _, -, and - * spaces. - *
- * - *This is equivalent to calling role.set("name", name)
- * - * @method setName - * @param {String} name The name of the role. - * @param {Object} options Standard options object with success and error - * callbacks. - */ - - }, { - key: 'setName', - value: function (name, options) { - return this.set('name', name, options); - } - - /** - * Gets the Parse.Relation for the Parse.Users that are direct - * children of this role. These users are granted any privileges that this - * role has been granted (e.g. read or write access through ACLs). You can - * add or remove users from the role through this relation. - * - *This is equivalent to calling role.relation("users")
- * - * @method getUsers - * @return {Parse.Relation} the relation for the users belonging to this - * role. - */ - - }, { - key: 'getUsers', - value: function () { - return this.relation('users'); - } - - /** - * Gets the Parse.Relation for the Parse.Roles that are direct - * children of this role. These roles' users are granted any privileges that - * this role has been granted (e.g. read or write access through ACLs). You - * can add or remove child roles from this role through this relation. - * - *This is equivalent to calling role.relation("roles")
- * - * @method getRoles - * @return {Parse.Relation} the relation for the roles belonging to this - * role. - */ - - }, { - key: 'getRoles', - value: function () { - return this.relation('roles'); - } - }, { - key: 'validate', - value: function (attrs, options) { - var isInvalid = (0, _get3.default)(ParseRole.prototype.__proto__ || (0, _getPrototypeOf2.default)(ParseRole.prototype), 'validate', this).call(this, attrs, options); - if (isInvalid) { - return isInvalid; - } - - if ('name' in attrs && attrs.name !== this.getName()) { - var newName = attrs.name; - if (this.id && this.id !== attrs.objectId) { - // Check to see if the objectId being set matches this.id - // This happens during a fetch -- the id is set before calling fetch - // Let the name be set in this case - return new _ParseError2.default(_ParseError2.default.OTHER_CAUSE, 'A role\'s name can only be set before it has been saved.'); - } - if (typeof newName !== 'string') { - return new _ParseError2.default(_ParseError2.default.OTHER_CAUSE, 'A role\'s name must be a String.'); - } - if (!/^[0-9a-zA-Z\-_ ]+$/.test(newName)) { - return new _ParseError2.default(_ParseError2.default.OTHER_CAUSE, 'A role\'s name can be only contain alphanumeric characters, _, ' + '-, and spaces.'); - } - } - return false; - } - }]); - return ParseRole; -}(_ParseObject3.default); /** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * - */ - -exports.default = ParseRole; - -_ParseObject3.default.registerSubclass('_Role', ParseRole); -},{"./ParseACL":11,"./ParseError":13,"./ParseObject":18,"babel-runtime/core-js/object/get-prototype-of":50,"babel-runtime/helpers/classCallCheck":56,"babel-runtime/helpers/createClass":57,"babel-runtime/helpers/get":58,"babel-runtime/helpers/inherits":59,"babel-runtime/helpers/possibleConstructorReturn":60}],24:[function(_dereq_,module,exports){ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _typeof2 = _dereq_('babel-runtime/helpers/typeof'); - -var _typeof3 = _interopRequireDefault(_typeof2); - -var _getPrototypeOf = _dereq_('babel-runtime/core-js/object/get-prototype-of'); - -var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); - -var _classCallCheck2 = _dereq_('babel-runtime/helpers/classCallCheck'); - -var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); - -var _createClass2 = _dereq_('babel-runtime/helpers/createClass'); - -var _createClass3 = _interopRequireDefault(_createClass2); - -var _possibleConstructorReturn2 = _dereq_('babel-runtime/helpers/possibleConstructorReturn'); - -var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); - -var _inherits2 = _dereq_('babel-runtime/helpers/inherits'); - -var _inherits3 = _interopRequireDefault(_inherits2); - -var _CoreManager = _dereq_('./CoreManager'); - -var _CoreManager2 = _interopRequireDefault(_CoreManager); - -var _isRevocableSession = _dereq_('./isRevocableSession'); - -var _isRevocableSession2 = _interopRequireDefault(_isRevocableSession); - -var _ParseObject2 = _dereq_('./ParseObject'); - -var _ParseObject3 = _interopRequireDefault(_ParseObject2); - -var _ParsePromise = _dereq_('./ParsePromise'); - -var _ParsePromise2 = _interopRequireDefault(_ParsePromise); - -var _ParseUser = _dereq_('./ParseUser'); - -var _ParseUser2 = _interopRequireDefault(_ParseUser); - -function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { default: obj }; -} - -/** - * @class Parse.Session - * @constructor - * - *A Parse.Session object is a local representation of a revocable session. - * This class is a subclass of a Parse.Object, and retains the same - * functionality of a Parse.Object.
- */ -var ParseSession = function (_ParseObject) { - (0, _inherits3.default)(ParseSession, _ParseObject); - - function ParseSession(attributes) { - (0, _classCallCheck3.default)(this, ParseSession); - - var _this = (0, _possibleConstructorReturn3.default)(this, (ParseSession.__proto__ || (0, _getPrototypeOf2.default)(ParseSession)).call(this, '_Session')); - - if (attributes && (typeof attributes === 'undefined' ? 'undefined' : (0, _typeof3.default)(attributes)) === 'object') { - if (!_this.set(attributes || {})) { - throw new Error('Can\'t create an invalid Session'); - } - } - return _this; - } - - /** - * Returns the session token string. - * @method getSessionToken - * @return {String} - */ - - (0, _createClass3.default)(ParseSession, [{ - key: 'getSessionToken', - value: function () { - var token = this.get('sessionToken'); - if (typeof token === 'string') { - return token; - } - return ''; - } - }], [{ - key: 'readOnlyAttributes', - value: function () { - return ['createdWith', 'expiresAt', 'installationId', 'restricted', 'sessionToken', 'user']; - } - - /** - * Retrieves the Session object for the currently logged in session. - * @method current - * @static - * @return {Parse.Promise} A promise that is resolved with the Parse.Session - * object after it has been fetched. If there is no current user, the - * promise will be rejected. - */ - - }, { - key: 'current', - value: function (options) { - options = options || {}; - var controller = _CoreManager2.default.getSessionController(); - - var sessionOptions = {}; - if (options.hasOwnProperty('useMasterKey')) { - sessionOptions.useMasterKey = options.useMasterKey; - } - return _ParseUser2.default.currentAsync().then(function (user) { - if (!user) { - return _ParsePromise2.default.error('There is no current user.'); - } - user.getSessionToken(); - - sessionOptions.sessionToken = user.getSessionToken(); - return controller.getSession(sessionOptions); - }); - } - - /** - * Determines whether the current session token is revocable. - * This method is useful for migrating Express.js or Node.js web apps to - * use revocable sessions. If you are migrating an app that uses the Parse - * SDK in the browser only, please use Parse.User.enableRevocableSession() - * instead, so that sessions can be automatically upgraded. - * @method isCurrentSessionRevocable - * @static - * @return {Boolean} - */ - - }, { - key: 'isCurrentSessionRevocable', - value: function () { - var currentUser = _ParseUser2.default.current(); - if (currentUser) { - return (0, _isRevocableSession2.default)(currentUser.getSessionToken() || ''); - } - return false; - } - }]); - return ParseSession; -}(_ParseObject3.default); /** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * - */ - -exports.default = ParseSession; - -_ParseObject3.default.registerSubclass('_Session', ParseSession); - -var DefaultController = { - getSession: function (options) { - var RESTController = _CoreManager2.default.getRESTController(); - var session = new ParseSession(); - - return RESTController.request('GET', 'sessions/me', {}, options).then(function (sessionData) { - session._finishFetch(sessionData); - session._setExisted(true); - return session; - }); - } -}; - -_CoreManager2.default.setSessionController(DefaultController); -},{"./CoreManager":3,"./ParseObject":18,"./ParsePromise":20,"./ParseUser":25,"./isRevocableSession":39,"babel-runtime/core-js/object/get-prototype-of":50,"babel-runtime/helpers/classCallCheck":56,"babel-runtime/helpers/createClass":57,"babel-runtime/helpers/inherits":59,"babel-runtime/helpers/possibleConstructorReturn":60,"babel-runtime/helpers/typeof":61}],25:[function(_dereq_,module,exports){ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _stringify = _dereq_('babel-runtime/core-js/json/stringify'); - -var _stringify2 = _interopRequireDefault(_stringify); - -var _defineProperty = _dereq_('babel-runtime/core-js/object/define-property'); - -var _defineProperty2 = _interopRequireDefault(_defineProperty); - -var _typeof2 = _dereq_('babel-runtime/helpers/typeof'); - -var _typeof3 = _interopRequireDefault(_typeof2); - -var _getPrototypeOf = _dereq_('babel-runtime/core-js/object/get-prototype-of'); - -var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); - -var _classCallCheck2 = _dereq_('babel-runtime/helpers/classCallCheck'); - -var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); - -var _createClass2 = _dereq_('babel-runtime/helpers/createClass'); - -var _createClass3 = _interopRequireDefault(_createClass2); - -var _possibleConstructorReturn2 = _dereq_('babel-runtime/helpers/possibleConstructorReturn'); - -var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); - -var _get2 = _dereq_('babel-runtime/helpers/get'); - -var _get3 = _interopRequireDefault(_get2); - -var _inherits2 = _dereq_('babel-runtime/helpers/inherits'); - -var _inherits3 = _interopRequireDefault(_inherits2); - -var _CoreManager = _dereq_('./CoreManager'); - -var _CoreManager2 = _interopRequireDefault(_CoreManager); - -var _isRevocableSession = _dereq_('./isRevocableSession'); - -var _isRevocableSession2 = _interopRequireDefault(_isRevocableSession); - -var _ParseError = _dereq_('./ParseError'); - -var _ParseError2 = _interopRequireDefault(_ParseError); - -var _ParseObject2 = _dereq_('./ParseObject'); - -var _ParseObject3 = _interopRequireDefault(_ParseObject2); - -var _ParsePromise = _dereq_('./ParsePromise'); - -var _ParsePromise2 = _interopRequireDefault(_ParsePromise); - -var _ParseSession = _dereq_('./ParseSession'); - -var _ParseSession2 = _interopRequireDefault(_ParseSession); - -var _Storage = _dereq_('./Storage'); - -var _Storage2 = _interopRequireDefault(_Storage); - -function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { default: obj }; -} - -var CURRENT_USER_KEY = 'currentUser'; /** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * - */ - -var canUseCurrentUser = !_CoreManager2.default.get('IS_NODE'); -var currentUserCacheMatchesDisk = false; -var currentUserCache = null; - -var authProviders = {}; - -/** - * @class Parse.User - * @constructor - * - *A Parse.User object is a local representation of a user persisted to the - * Parse cloud. This class is a subclass of a Parse.Object, and retains the - * same functionality of a Parse.Object, but also extends it with various - * user specific methods, like authentication, signing up, and validation of - * uniqueness.
- */ - -var ParseUser = function (_ParseObject) { - (0, _inherits3.default)(ParseUser, _ParseObject); - - function ParseUser(attributes) { - (0, _classCallCheck3.default)(this, ParseUser); - - var _this = (0, _possibleConstructorReturn3.default)(this, (ParseUser.__proto__ || (0, _getPrototypeOf2.default)(ParseUser)).call(this, '_User')); - - if (attributes && (typeof attributes === 'undefined' ? 'undefined' : (0, _typeof3.default)(attributes)) === 'object') { - if (!_this.set(attributes || {})) { - throw new Error('Can\'t create an invalid Parse User'); - } - } - return _this; - } - - /** - * Request a revocable session token to replace the older style of token. - * @method _upgradeToRevocableSession - * @param {Object} options A Backbone-style options object. - * @return {Parse.Promise} A promise that is resolved when the replacement - * token has been fetched. - */ - - (0, _createClass3.default)(ParseUser, [{ - key: '_upgradeToRevocableSession', - value: function (options) { - options = options || {}; - - var upgradeOptions = {}; - if (options.hasOwnProperty('useMasterKey')) { - upgradeOptions.useMasterKey = options.useMasterKey; - } - - var controller = _CoreManager2.default.getUserController(); - return controller.upgradeToRevocableSession(this, upgradeOptions)._thenRunCallbacks(options); - } - - /** - * Unlike in the Android/iOS SDKs, logInWith is unnecessary, since you can - * call linkWith on the user (even if it doesn't exist yet on the server). - * @method _linkWith - */ - - }, { - key: '_linkWith', - value: function (provider, options) { - var _this2 = this; - - var authType; - if (typeof provider === 'string') { - authType = provider; - provider = authProviders[provider]; - } else { - authType = provider.getAuthType(); - } - if (options && options.hasOwnProperty('authData')) { - var authData = this.get('authData') || {}; - if ((typeof authData === 'undefined' ? 'undefined' : (0, _typeof3.default)(authData)) !== 'object') { - throw new Error('Invalid type: authData field should be an object'); - } - authData[authType] = options.authData; - - var controller = _CoreManager2.default.getUserController(); - return controller.linkWith(this, authData)._thenRunCallbacks(options, this); - } else { - var promise = new _ParsePromise2.default(); - provider.authenticate({ - success: function (provider, result) { - var opts = {}; - opts.authData = result; - if (options.success) { - opts.success = options.success; - } - if (options.error) { - opts.error = options.error; - } - _this2._linkWith(provider, opts).then(function () { - promise.resolve(_this2); - }, function (error) { - promise.reject(error); - }); - }, - error: function (provider, _error) { - if (typeof options.error === 'function') { - options.error(_this2, _error); - } - promise.reject(_error); - } - }); - return promise; - } - } - - /** - * Synchronizes auth data for a provider (e.g. puts the access token in the - * right place to be used by the Facebook SDK). - * @method _synchronizeAuthData - */ - - }, { - key: '_synchronizeAuthData', - value: function (provider) { - if (!this.isCurrent() || !provider) { - return; - } - var authType; - if (typeof provider === 'string') { - authType = provider; - provider = authProviders[authType]; - } else { - authType = provider.getAuthType(); - } - var authData = this.get('authData'); - if (!provider || !authData || (typeof authData === 'undefined' ? 'undefined' : (0, _typeof3.default)(authData)) !== 'object') { - return; - } - var success = provider.restoreAuthentication(authData[authType]); - if (!success) { - this._unlinkFrom(provider); - } - } - - /** - * Synchronizes authData for all providers. - * @method _synchronizeAllAuthData - */ - - }, { - key: '_synchronizeAllAuthData', - value: function () { - var authData = this.get('authData'); - if ((typeof authData === 'undefined' ? 'undefined' : (0, _typeof3.default)(authData)) !== 'object') { - return; - } - - for (var key in authData) { - this._synchronizeAuthData(key); - } - } - - /** - * Removes null values from authData (which exist temporarily for - * unlinking) - * @method _cleanupAuthData - */ - - }, { - key: '_cleanupAuthData', - value: function () { - if (!this.isCurrent()) { - return; - } - var authData = this.get('authData'); - if ((typeof authData === 'undefined' ? 'undefined' : (0, _typeof3.default)(authData)) !== 'object') { - return; - } - - for (var key in authData) { - if (!authData[key]) { - delete authData[key]; - } - } - } - - /** - * Unlinks a user from a service. - * @method _unlinkFrom - */ - - }, { - key: '_unlinkFrom', - value: function (provider, options) { - var _this3 = this; - - if (typeof provider === 'string') { - provider = authProviders[provider]; - } else { - provider.getAuthType(); - } - return this._linkWith(provider, { authData: null }).then(function () { - _this3._synchronizeAuthData(provider); - return _ParsePromise2.default.as(_this3); - })._thenRunCallbacks(options); - } - - /** - * Checks whether a user is linked to a service. - * @method _isLinked - */ - - }, { - key: '_isLinked', - value: function (provider) { - var authType; - if (typeof provider === 'string') { - authType = provider; - } else { - authType = provider.getAuthType(); - } - var authData = this.get('authData') || {}; - if ((typeof authData === 'undefined' ? 'undefined' : (0, _typeof3.default)(authData)) !== 'object') { - return false; - } - return !!authData[authType]; - } - - /** - * Deauthenticates all providers. - * @method _logOutWithAll - */ - - }, { - key: '_logOutWithAll', - value: function () { - var authData = this.get('authData'); - if ((typeof authData === 'undefined' ? 'undefined' : (0, _typeof3.default)(authData)) !== 'object') { - return; - } - - for (var key in authData) { - this._logOutWith(key); - } - } - - /** - * Deauthenticates a single provider (e.g. removing access tokens from the - * Facebook SDK). - * @method _logOutWith - */ - - }, { - key: '_logOutWith', - value: function (provider) { - if (!this.isCurrent()) { - return; - } - if (typeof provider === 'string') { - provider = authProviders[provider]; - } - if (provider && provider.deauthenticate) { - provider.deauthenticate(); - } - } - - /** - * Class instance method used to maintain specific keys when a fetch occurs. - * Used to ensure that the session token is not lost. - */ - - }, { - key: '_preserveFieldsOnFetch', - value: function () { - return { - sessionToken: this.get('sessionToken') - }; - } - - /** - * Returns true ifcurrent
would return this user.
- * @method isCurrent
- * @return {Boolean}
- */
-
- }, {
- key: 'isCurrent',
- value: function () {
- var current = ParseUser.current();
- return !!current && current.id === this.id;
- }
-
- /**
- * Returns get("username").
- * @method getUsername
- * @return {String}
- */
-
- }, {
- key: 'getUsername',
- value: function () {
- var username = this.get('username');
- if (username == null || typeof username === 'string') {
- return username;
- }
- return '';
- }
-
- /**
- * Calls set("username", username, options) and returns the result.
- * @method setUsername
- * @param {String} username
- * @param {Object} options A Backbone-style options object.
- * @return {Boolean}
- */
-
- }, {
- key: 'setUsername',
- value: function (username) {
- // Strip anonymity, even we do not support anonymous user in js SDK, we may
- // encounter anonymous user created by android/iOS in cloud code.
- var authData = this.get('authData');
- if (authData && (typeof authData === 'undefined' ? 'undefined' : (0, _typeof3.default)(authData)) === 'object' && authData.hasOwnProperty('anonymous')) {
- // We need to set anonymous to null instead of deleting it in order to remove it from Parse.
- authData.anonymous = null;
- }
- this.set('username', username);
- }
-
- /**
- * Calls set("password", password, options) and returns the result.
- * @method setPassword
- * @param {String} password
- * @param {Object} options A Backbone-style options object.
- * @return {Boolean}
- */
-
- }, {
- key: 'setPassword',
- value: function (password) {
- this.set('password', password);
- }
-
- /**
- * Returns get("email").
- * @method getEmail
- * @return {String}
- */
-
- }, {
- key: 'getEmail',
- value: function () {
- var email = this.get('email');
- if (email == null || typeof email === 'string') {
- return email;
- }
- return '';
- }
-
- /**
- * Calls set("email", email, options) and returns the result.
- * @method setEmail
- * @param {String} email
- * @param {Object} options A Backbone-style options object.
- * @return {Boolean}
- */
-
- }, {
- key: 'setEmail',
- value: function (email) {
- this.set('email', email);
- }
-
- /**
- * Returns the session token for this user, if the user has been logged in,
- * or if it is the result of a query with the master key. Otherwise, returns
- * undefined.
- * @method getSessionToken
- * @return {String} the session token, or undefined
- */
-
- }, {
- key: 'getSessionToken',
- value: function () {
- var token = this.get('sessionToken');
- if (token == null || typeof token === 'string') {
- return token;
- }
- return '';
- }
-
- /**
- * Checks whether this user is the current user and has been authenticated.
- * @method authenticated
- * @return (Boolean) whether this user is the current user and is logged in.
- */
-
- }, {
- key: 'authenticated',
- value: function () {
- var current = ParseUser.current();
- return !!this.get('sessionToken') && !!current && current.id === this.id;
- }
-
- /**
- * Signs up a new user. You should call this instead of save for
- * new Parse.Users. This will create a new Parse.User on the server, and
- * also persist the session on disk so that you can access the user using
- * current
.
- *
- * A username and password must be set before calling signUp.
- * - *Calls options.success or options.error on completion.
- * - * @method signUp - * @param {Object} attrs Extra fields to set on the new user, or null. - * @param {Object} options A Backbone-style options object. - * @return {Parse.Promise} A promise that is fulfilled when the signup - * finishes. - */ - - }, { - key: 'signUp', - value: function (attrs, options) { - options = options || {}; - - var signupOptions = {}; - if (options.hasOwnProperty('useMasterKey')) { - signupOptions.useMasterKey = options.useMasterKey; - } - if (options.hasOwnProperty('installationId')) { - signupOptions.installationId = options.installationId; - } - - var controller = _CoreManager2.default.getUserController(); - return controller.signUp(this, attrs, signupOptions)._thenRunCallbacks(options, this); - } - - /** - * Logs in a Parse.User. On success, this saves the session to disk, - * so you can retrieve the currently logged in user using - *current
.
- *
- * A username and password must be set before calling logIn.
- * - *Calls options.success or options.error on completion.
- * - * @method logIn - * @param {Object} options A Backbone-style options object. - * @return {Parse.Promise} A promise that is fulfilled with the user when - * the login is complete. - */ - - }, { - key: 'logIn', - value: function (options) { - options = options || {}; - - var loginOptions = {}; - if (options.hasOwnProperty('useMasterKey')) { - loginOptions.useMasterKey = options.useMasterKey; - } - if (options.hasOwnProperty('installationId')) { - loginOptions.installationId = options.installationId; - } - - var controller = _CoreManager2.default.getUserController(); - return controller.logIn(this, loginOptions)._thenRunCallbacks(options, this); - } - - /** - * Wrap the default save behavior with functionality to save to local - * storage if this is current user. - */ - - }, { - key: 'save', - value: function () { - var _this4 = this; - - for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { - args[_key] = arguments[_key]; - } - - return (0, _get3.default)(ParseUser.prototype.__proto__ || (0, _getPrototypeOf2.default)(ParseUser.prototype), 'save', this).apply(this, args).then(function () { - if (_this4.isCurrent()) { - return _CoreManager2.default.getUserController().updateUserOnDisk(_this4); - } - return _this4; - }); - } - - /** - * Wrap the default destroy behavior with functionality that logs out - * the current user when it is destroyed - */ - - }, { - key: 'destroy', - value: function () { - var _this5 = this; - - for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { - args[_key2] = arguments[_key2]; - } - - return (0, _get3.default)(ParseUser.prototype.__proto__ || (0, _getPrototypeOf2.default)(ParseUser.prototype), 'destroy', this).apply(this, args).then(function () { - if (_this5.isCurrent()) { - return _CoreManager2.default.getUserController().removeUserFromDisk(); - } - return _this5; - }); - } - - /** - * Wrap the default fetch behavior with functionality to save to local - * storage if this is current user. - */ - - }, { - key: 'fetch', - value: function () { - var _this6 = this; - - for (var _len3 = arguments.length, args = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) { - args[_key3] = arguments[_key3]; - } - - return (0, _get3.default)(ParseUser.prototype.__proto__ || (0, _getPrototypeOf2.default)(ParseUser.prototype), 'fetch', this).apply(this, args).then(function () { - if (_this6.isCurrent()) { - return _CoreManager2.default.getUserController().updateUserOnDisk(_this6); - } - return _this6; - }); - } - }], [{ - key: 'readOnlyAttributes', - value: function () { - return ['sessionToken']; - } - - /** - * Adds functionality to the existing Parse.User class - * @method extend - * @param {Object} protoProps A set of properties to add to the prototype - * @param {Object} classProps A set of static properties to add to the class - * @static - * @return {Class} The newly extended Parse.User class - */ - - }, { - key: 'extend', - value: function (protoProps, classProps) { - if (protoProps) { - for (var prop in protoProps) { - if (prop !== 'className') { - (0, _defineProperty2.default)(ParseUser.prototype, prop, { - value: protoProps[prop], - enumerable: false, - writable: true, - configurable: true - }); - } - } - } - - if (classProps) { - for (var prop in classProps) { - if (prop !== 'className') { - (0, _defineProperty2.default)(ParseUser, prop, { - value: classProps[prop], - enumerable: false, - writable: true, - configurable: true - }); - } - } - } - - return ParseUser; - } - - /** - * Retrieves the currently logged in ParseUser with a valid session, - * either from memory or localStorage, if necessary. - * @method current - * @static - * @return {Parse.Object} The currently logged in Parse.User. - */ - - }, { - key: 'current', - value: function () { - if (!canUseCurrentUser) { - return null; - } - var controller = _CoreManager2.default.getUserController(); - return controller.currentUser(); - } - - /** - * Retrieves the currently logged in ParseUser from asynchronous Storage. - * @method currentAsync - * @static - * @return {Parse.Promise} A Promise that is resolved with the currently - * logged in Parse User - */ - - }, { - key: 'currentAsync', - value: function () { - if (!canUseCurrentUser) { - return _ParsePromise2.default.as(null); - } - var controller = _CoreManager2.default.getUserController(); - return controller.currentUserAsync(); - } - - /** - * Signs up a new user with a username (or email) and password. - * This will create a new Parse.User on the server, and also persist the - * session in localStorage so that you can access the user using - * {@link #current}. - * - *Calls options.success or options.error on completion.
- * - * @method signUp - * @param {String} username The username (or email) to sign up with. - * @param {String} password The password to sign up with. - * @param {Object} attrs Extra fields to set on the new user. - * @param {Object} options A Backbone-style options object. - * @static - * @return {Parse.Promise} A promise that is fulfilled with the user when - * the signup completes. - */ - - }, { - key: 'signUp', - value: function (username, password, attrs, options) { - attrs = attrs || {}; - attrs.username = username; - attrs.password = password; - var user = new ParseUser(attrs); - return user.signUp({}, options); - } - - /** - * Logs in a user with a username (or email) and password. On success, this - * saves the session to disk, so you can retrieve the currently logged in - * user usingcurrent
.
- *
- * Calls options.success or options.error on completion.
- * - * @method logIn - * @param {String} username The username (or email) to log in with. - * @param {String} password The password to log in with. - * @param {Object} options A Backbone-style options object. - * @static - * @return {Parse.Promise} A promise that is fulfilled with the user when - * the login completes. - */ - - }, { - key: 'logIn', - value: function (username, password, options) { - if (typeof username !== 'string') { - return _ParsePromise2.default.error(new _ParseError2.default(_ParseError2.default.OTHER_CAUSE, 'Username must be a string.')); - } else if (typeof password !== 'string') { - return _ParsePromise2.default.error(new _ParseError2.default(_ParseError2.default.OTHER_CAUSE, 'Password must be a string.')); - } - var user = new ParseUser(); - user._finishFetch({ username: username, password: password }); - return user.logIn(options); - } - - /** - * Logs in a user with a session token. On success, this saves the session - * to disk, so you can retrieve the currently logged in user using - *current
.
- *
- * Calls options.success or options.error on completion.
- * - * @method become - * @param {String} sessionToken The sessionToken to log in with. - * @param {Object} options A Backbone-style options object. - * @static - * @return {Parse.Promise} A promise that is fulfilled with the user when - * the login completes. - */ - - }, { - key: 'become', - value: function (sessionToken, options) { - if (!canUseCurrentUser) { - throw new Error('It is not memory-safe to become a user in a server environment'); - } - options = options || {}; - - var becomeOptions = { - sessionToken: sessionToken - }; - if (options.hasOwnProperty('useMasterKey')) { - becomeOptions.useMasterKey = options.useMasterKey; - } - - var controller = _CoreManager2.default.getUserController(); - return controller.become(becomeOptions)._thenRunCallbacks(options); - } - }, { - key: 'logInWith', - value: function (provider, options) { - return ParseUser._logInWith(provider, options); - } - - /** - * Logs out the currently logged in user session. This will remove the - * session from disk, log out of linked services, and future calls to - *current
will return null
.
- * @method logOut
- * @static
- * @return {Parse.Promise} A promise that is resolved when the session is
- * destroyed on the server.
- */
-
- }, {
- key: 'logOut',
- value: function () {
- if (!canUseCurrentUser) {
- throw new Error('There is no current user user on a node.js server environment.');
- }
-
- var controller = _CoreManager2.default.getUserController();
- return controller.logOut();
- }
-
- /**
- * Requests a password reset email to be sent to the specified email address
- * associated with the user account. This email allows the user to securely
- * reset their password on the Parse site.
- *
- * Calls options.success or options.error on completion.
- * - * @method requestPasswordReset - * @param {String} email The email address associated with the user that - * forgot their password. - * @param {Object} options A Backbone-style options object. - * @static - */ - - }, { - key: 'requestPasswordReset', - value: function (email, options) { - options = options || {}; - - var requestOptions = {}; - if (options.hasOwnProperty('useMasterKey')) { - requestOptions.useMasterKey = options.useMasterKey; - } - - var controller = _CoreManager2.default.getUserController(); - return controller.requestPasswordReset(email, requestOptions)._thenRunCallbacks(options); - } - - /** - * Allow someone to define a custom User class without className - * being rewritten to _User. The default behavior is to rewrite - * User to _User for legacy reasons. This allows developers to - * override that behavior. - * - * @method allowCustomUserClass - * @param {Boolean} isAllowed Whether or not to allow custom User class - * @static - */ - - }, { - key: 'allowCustomUserClass', - value: function (isAllowed) { - _CoreManager2.default.set('PERFORM_USER_REWRITE', !isAllowed); - } - - /** - * Allows a legacy application to start using revocable sessions. If the - * current session token is not revocable, a request will be made for a new, - * revocable session. - * It is not necessary to call this method from cloud code unless you are - * handling user signup or login from the server side. In a cloud code call, - * this function will not attempt to upgrade the current token. - * @method enableRevocableSession - * @param {Object} options A Backbone-style options object. - * @static - * @return {Parse.Promise} A promise that is resolved when the process has - * completed. If a replacement session token is requested, the promise - * will be resolved after a new token has been fetched. - */ - - }, { - key: 'enableRevocableSession', - value: function (options) { - options = options || {}; - _CoreManager2.default.set('FORCE_REVOCABLE_SESSION', true); - if (canUseCurrentUser) { - var current = ParseUser.current(); - if (current) { - return current._upgradeToRevocableSession(options); - } - } - return _ParsePromise2.default.as()._thenRunCallbacks(options); - } - - /** - * Enables the use of become or the current user in a server - * environment. These features are disabled by default, since they depend on - * global objects that are not memory-safe for most servers. - * @method enableUnsafeCurrentUser - * @static - */ - - }, { - key: 'enableUnsafeCurrentUser', - value: function () { - canUseCurrentUser = true; - } - - /** - * Disables the use of become or the current user in any environment. - * These features are disabled on servers by default, since they depend on - * global objects that are not memory-safe for most servers. - * @method disableUnsafeCurrentUser - * @static - */ - - }, { - key: 'disableUnsafeCurrentUser', - value: function () { - canUseCurrentUser = false; - } - }, { - key: '_registerAuthenticationProvider', - value: function (provider) { - authProviders[provider.getAuthType()] = provider; - // Synchronize the current user with the auth provider. - ParseUser.currentAsync().then(function (current) { - if (current) { - current._synchronizeAuthData(provider.getAuthType()); - } - }); - } - }, { - key: '_logInWith', - value: function (provider, options) { - var user = new ParseUser(); - return user._linkWith(provider, options); - } - }, { - key: '_clearCache', - value: function () { - currentUserCache = null; - currentUserCacheMatchesDisk = false; - } - }, { - key: '_setCurrentUserCache', - value: function (user) { - currentUserCache = user; - } - }]); - return ParseUser; -}(_ParseObject3.default); - -exports.default = ParseUser; - -_ParseObject3.default.registerSubclass('_User', ParseUser); - -var DefaultController = { - updateUserOnDisk: function (user) { - var path = _Storage2.default.generatePath(CURRENT_USER_KEY); - var json = user.toJSON(); - json.className = '_User'; - return _Storage2.default.setItemAsync(path, (0, _stringify2.default)(json)).then(function () { - return user; - }); - }, - removeUserFromDisk: function () { - var path = _Storage2.default.generatePath(CURRENT_USER_KEY); - currentUserCacheMatchesDisk = true; - currentUserCache = null; - return _Storage2.default.removeItemAsync(path); - }, - setCurrentUser: function (user) { - currentUserCache = user; - user._cleanupAuthData(); - user._synchronizeAllAuthData(); - return DefaultController.updateUserOnDisk(user); - }, - currentUser: function () { - if (currentUserCache) { - return currentUserCache; - } - if (currentUserCacheMatchesDisk) { - return null; - } - if (_Storage2.default.async()) { - throw new Error('Cannot call currentUser() when using a platform with an async ' + 'storage system. Call currentUserAsync() instead.'); - } - var path = _Storage2.default.generatePath(CURRENT_USER_KEY); - var userData = _Storage2.default.getItem(path); - currentUserCacheMatchesDisk = true; - if (!userData) { - currentUserCache = null; - return null; - } - userData = JSON.parse(userData); - if (!userData.className) { - userData.className = '_User'; - } - if (userData._id) { - if (userData.objectId !== userData._id) { - userData.objectId = userData._id; - } - delete userData._id; - } - if (userData._sessionToken) { - userData.sessionToken = userData._sessionToken; - delete userData._sessionToken; - } - var current = _ParseObject3.default.fromJSON(userData); - currentUserCache = current; - current._synchronizeAllAuthData(); - return current; - }, - currentUserAsync: function () { - if (currentUserCache) { - return _ParsePromise2.default.as(currentUserCache); - } - if (currentUserCacheMatchesDisk) { - return _ParsePromise2.default.as(null); - } - var path = _Storage2.default.generatePath(CURRENT_USER_KEY); - return _Storage2.default.getItemAsync(path).then(function (userData) { - currentUserCacheMatchesDisk = true; - if (!userData) { - currentUserCache = null; - return _ParsePromise2.default.as(null); - } - userData = JSON.parse(userData); - if (!userData.className) { - userData.className = '_User'; - } - if (userData._id) { - if (userData.objectId !== userData._id) { - userData.objectId = userData._id; - } - delete userData._id; - } - if (userData._sessionToken) { - userData.sessionToken = userData._sessionToken; - delete userData._sessionToken; - } - var current = _ParseObject3.default.fromJSON(userData); - currentUserCache = current; - current._synchronizeAllAuthData(); - return _ParsePromise2.default.as(current); - }); - }, - signUp: function (user, attrs, options) { - var username = attrs && attrs.username || user.get('username'); - var password = attrs && attrs.password || user.get('password'); - - if (!username || !username.length) { - return _ParsePromise2.default.error(new _ParseError2.default(_ParseError2.default.OTHER_CAUSE, 'Cannot sign up user with an empty name.')); - } - if (!password || !password.length) { - return _ParsePromise2.default.error(new _ParseError2.default(_ParseError2.default.OTHER_CAUSE, 'Cannot sign up user with an empty password.')); - } - - return user.save(attrs, options).then(function () { - // Clear the password field - user._finishFetch({ password: undefined }); - - if (canUseCurrentUser) { - return DefaultController.setCurrentUser(user); - } - return user; - }); - }, - logIn: function (user, options) { - var RESTController = _CoreManager2.default.getRESTController(); - var stateController = _CoreManager2.default.getObjectStateController(); - var auth = { - username: user.get('username'), - password: user.get('password') - }; - return RESTController.request('GET', 'login', auth, options).then(function (response, status) { - user._migrateId(response.objectId); - user._setExisted(true); - stateController.setPendingOp(user._getStateIdentifier(), 'username', undefined); - stateController.setPendingOp(user._getStateIdentifier(), 'password', undefined); - response.password = undefined; - user._finishFetch(response); - if (!canUseCurrentUser) { - // We can't set the current user, so just return the one we logged in - return _ParsePromise2.default.as(user); - } - return DefaultController.setCurrentUser(user); - }); - }, - become: function (options) { - var user = new ParseUser(); - var RESTController = _CoreManager2.default.getRESTController(); - return RESTController.request('GET', 'users/me', {}, options).then(function (response, status) { - user._finishFetch(response); - user._setExisted(true); - return DefaultController.setCurrentUser(user); - }); - }, - logOut: function () { - return DefaultController.currentUserAsync().then(function (currentUser) { - var path = _Storage2.default.generatePath(CURRENT_USER_KEY); - var promise = _Storage2.default.removeItemAsync(path); - var RESTController = _CoreManager2.default.getRESTController(); - if (currentUser !== null) { - var currentSession = currentUser.getSessionToken(); - if (currentSession && (0, _isRevocableSession2.default)(currentSession)) { - promise = promise.then(function () { - return RESTController.request('POST', 'logout', {}, { sessionToken: currentSession }); - }); - } - currentUser._logOutWithAll(); - currentUser._finishFetch({ sessionToken: undefined }); - } - currentUserCacheMatchesDisk = true; - currentUserCache = null; - - return promise; - }); - }, - requestPasswordReset: function (email, options) { - var RESTController = _CoreManager2.default.getRESTController(); - return RESTController.request('POST', 'requestPasswordReset', { email: email }, options); - }, - upgradeToRevocableSession: function (user, options) { - var token = user.getSessionToken(); - if (!token) { - return _ParsePromise2.default.error(new _ParseError2.default(_ParseError2.default.SESSION_MISSING, 'Cannot upgrade a user with no session token')); - } - - options.sessionToken = token; - - var RESTController = _CoreManager2.default.getRESTController(); - return RESTController.request('POST', 'upgradeToRevocableSession', {}, options).then(function (result) { - var session = new _ParseSession2.default(); - session._finishFetch(result); - user._finishFetch({ sessionToken: session.getSessionToken() }); - if (user.isCurrent()) { - return DefaultController.setCurrentUser(user); - } - return _ParsePromise2.default.as(user); - }); - }, - linkWith: function (user, authData) { - return user.save({ authData: authData }).then(function () { - if (canUseCurrentUser) { - return DefaultController.setCurrentUser(user); - } - return user; - }); - } -}; - -_CoreManager2.default.setUserController(DefaultController); -},{"./CoreManager":3,"./ParseError":13,"./ParseObject":18,"./ParsePromise":20,"./ParseSession":24,"./Storage":29,"./isRevocableSession":39,"babel-runtime/core-js/json/stringify":44,"babel-runtime/core-js/object/define-property":47,"babel-runtime/core-js/object/get-prototype-of":50,"babel-runtime/helpers/classCallCheck":56,"babel-runtime/helpers/createClass":57,"babel-runtime/helpers/get":58,"babel-runtime/helpers/inherits":59,"babel-runtime/helpers/possibleConstructorReturn":60,"babel-runtime/helpers/typeof":61}],26:[function(_dereq_,module,exports){ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _typeof2 = _dereq_('babel-runtime/helpers/typeof'); - -var _typeof3 = _interopRequireDefault(_typeof2); - -exports.send = send; - -var _CoreManager = _dereq_('./CoreManager'); - -var _CoreManager2 = _interopRequireDefault(_CoreManager); - -var _ParseQuery = _dereq_('./ParseQuery'); - -var _ParseQuery2 = _interopRequireDefault(_ParseQuery); - -function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { default: obj }; -} - -/** - * Contains functions to deal with Push in Parse. - * @class Parse.Push - * @static - */ - -/** - * Sends a push notification. - * @method send - * @param {Object} data - The data of the push notification. Valid fields - * are: - *- * var dimensions = { - * gender: 'm', - * source: 'web', - * dayType: 'weekend' - * }; - * Parse.Analytics.track('signup', dimensions); - *- * - * There is a default limit of 8 dimensions per event tracked. - * - * @method track - * @param {String} name The name of the custom event to report to Parse as - * having happened. - * @param {Object} dimensions The dictionary of information by which to - * segment this event. - * @param {Object} options A Backbone-style callback object. - * @return {Parse.Promise} A promise that is resolved when the round-trip - * to the server completes. - */ -function track(name, dimensions, options) { - name = name || ''; - name = name.replace(/^\s*/, ''); - name = name.replace(/\s*$/, ''); - if (name.length === 0) { - throw new TypeError('A name for the custom event must be provided'); - } - - for (var key in dimensions) { - if (typeof key !== 'string' || typeof dimensions[key] !== 'string') { - throw new TypeError('track() dimensions expects keys and values of type "string".'); - } - } - - options = options || {}; - return _CoreManager2.default.getAnalyticsController().track(name, dimensions)._thenRunCallbacks(options); -} /** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * - */ - -var DefaultController = { - track: function (name, dimensions) { - var RESTController = _CoreManager2.default.getRESTController(); - return RESTController.request('POST', 'events/' + name, { dimensions: dimensions }); - } -}; - -_CoreManager2.default.setAnalyticsController(DefaultController); \ No newline at end of file diff --git a/lib/browser/Cloud.js b/lib/browser/Cloud.js deleted file mode 100644 index 72c333750..000000000 --- a/lib/browser/Cloud.js +++ /dev/null @@ -1,109 +0,0 @@ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.run = run; - -var _CoreManager = require('./CoreManager'); - -var _CoreManager2 = _interopRequireDefault(_CoreManager); - -var _decode = require('./decode'); - -var _decode2 = _interopRequireDefault(_decode); - -var _encode = require('./encode'); - -var _encode2 = _interopRequireDefault(_encode); - -var _ParseError = require('./ParseError'); - -var _ParseError2 = _interopRequireDefault(_ParseError); - -var _ParsePromise = require('./ParsePromise'); - -var _ParsePromise2 = _interopRequireDefault(_ParsePromise); - -function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { default: obj }; -} - -/** - * Contains functions for calling and declaring - * cloud functions. - *
- * Some functions are only available from Cloud Code. - *
- * - * @class Parse.Cloud - * @static - */ - -/** - * Makes a call to a cloud function. - * @method run - * @param {String} name The function name. - * @param {Object} data The parameters to send to the cloud function. - * @param {Object} options A Backbone-style options object - * options.success, if set, should be a function to handle a successful - * call to a cloud function. options.error should be a function that - * handles an error running the cloud function. Both functions are - * optional. Both functions take a single argument. - * @return {Parse.Promise} A promise that will be resolved with the result - * of the function. - */ -function run(name, data, options) { - options = options || {}; - - if (typeof name !== 'string' || name.length === 0) { - throw new TypeError('Cloud function name must be a string.'); - } - - var requestOptions = {}; - if (options.useMasterKey) { - requestOptions.useMasterKey = options.useMasterKey; - } - if (options.sessionToken) { - requestOptions.sessionToken = options.sessionToken; - } - - return _CoreManager2.default.getCloudController().run(name, data, requestOptions)._thenRunCallbacks(options); -} /** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * - */ - -var DefaultController = { - run: function (name, data, options) { - var RESTController = _CoreManager2.default.getRESTController(); - - var payload = (0, _encode2.default)(data, true); - - var requestOptions = {}; - if (options.hasOwnProperty('useMasterKey')) { - requestOptions.useMasterKey = options.useMasterKey; - } - if (options.hasOwnProperty('sessionToken')) { - requestOptions.sessionToken = options.sessionToken; - } - - var request = RESTController.request('POST', 'functions/' + name, payload, requestOptions); - - return request.then(function (res) { - var decoded = (0, _decode2.default)(res); - if (decoded && decoded.hasOwnProperty('result')) { - return _ParsePromise2.default.as(decoded.result); - } - return _ParsePromise2.default.error(new _ParseError2.default(_ParseError2.default.INVALID_JSON, 'The server returned an invalid response.')); - })._thenRunCallbacks(options); - } -}; - -_CoreManager2.default.setCloudController(DefaultController); \ No newline at end of file diff --git a/lib/browser/CoreManager.js b/lib/browser/CoreManager.js deleted file mode 100644 index 20d66a5f1..000000000 --- a/lib/browser/CoreManager.js +++ /dev/null @@ -1,161 +0,0 @@ -'use strict'; - -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * - */ - -var config = { - // Defaults - IS_NODE: typeof process !== 'undefined' && !!process.versions && !!process.versions.node && !process.versions.electron, - REQUEST_ATTEMPT_LIMIT: 5, - SERVER_URL: 'https://api.parse.com/1', - LIVEQUERY_SERVER_URL: null, - VERSION: 'js' + '1.9.2', - APPLICATION_ID: null, - JAVASCRIPT_KEY: null, - MASTER_KEY: null, - USE_MASTER_KEY: false, - PERFORM_USER_REWRITE: true, - FORCE_REVOCABLE_SESSION: false -}; - -function requireMethods(name, methods, controller) { - methods.forEach(function (func) { - if (typeof controller[func] !== 'function') { - throw new Error(name + ' must implement ' + func + '()'); - } - }); -} - -module.exports = { - get: function (key) { - if (config.hasOwnProperty(key)) { - return config[key]; - } - throw new Error('Configuration key not found: ' + key); - }, - - set: function (key, value) { - config[key] = value; - }, - - /* Specialized Controller Setters/Getters */ - - setAnalyticsController: function (controller) { - requireMethods('AnalyticsController', ['track'], controller); - config['AnalyticsController'] = controller; - }, - getAnalyticsController: function () { - return config['AnalyticsController']; - }, - setCloudController: function (controller) { - requireMethods('CloudController', ['run'], controller); - config['CloudController'] = controller; - }, - getCloudController: function () { - return config['CloudController']; - }, - setConfigController: function (controller) { - requireMethods('ConfigController', ['current', 'get'], controller); - config['ConfigController'] = controller; - }, - getConfigController: function () { - return config['ConfigController']; - }, - setFileController: function (controller) { - requireMethods('FileController', ['saveFile', 'saveBase64'], controller); - config['FileController'] = controller; - }, - getFileController: function () { - return config['FileController']; - }, - setInstallationController: function (controller) { - requireMethods('InstallationController', ['currentInstallationId'], controller); - config['InstallationController'] = controller; - }, - getInstallationController: function () { - return config['InstallationController']; - }, - setObjectController: function (controller) { - requireMethods('ObjectController', ['save', 'fetch', 'destroy'], controller); - config['ObjectController'] = controller; - }, - getObjectController: function () { - return config['ObjectController']; - }, - setObjectStateController: function (controller) { - requireMethods('ObjectStateController', ['getState', 'initializeState', 'removeState', 'getServerData', 'setServerData', 'getPendingOps', 'setPendingOp', 'pushPendingState', 'popPendingState', 'mergeFirstPendingState', 'getObjectCache', 'estimateAttribute', 'estimateAttributes', 'commitServerChanges', 'enqueueTask', 'clearAllState'], controller); - - config['ObjectStateController'] = controller; - }, - getObjectStateController: function () { - return config['ObjectStateController']; - }, - setPushController: function (controller) { - requireMethods('PushController', ['send'], controller); - config['PushController'] = controller; - }, - getPushController: function () { - return config['PushController']; - }, - setQueryController: function (controller) { - requireMethods('QueryController', ['find'], controller); - config['QueryController'] = controller; - }, - getQueryController: function () { - return config['QueryController']; - }, - setRESTController: function (controller) { - requireMethods('RESTController', ['request', 'ajax'], controller); - config['RESTController'] = controller; - }, - getRESTController: function () { - return config['RESTController']; - }, - setSessionController: function (controller) { - requireMethods('SessionController', ['getSession'], controller); - config['SessionController'] = controller; - }, - getSessionController: function () { - return config['SessionController']; - }, - setStorageController: function (controller) { - if (controller.async) { - requireMethods('An async StorageController', ['getItemAsync', 'setItemAsync', 'removeItemAsync'], controller); - } else { - requireMethods('A synchronous StorageController', ['getItem', 'setItem', 'removeItem'], controller); - } - config['StorageController'] = controller; - }, - getStorageController: function () { - return config['StorageController']; - }, - setUserController: function (controller) { - requireMethods('UserController', ['setCurrentUser', 'currentUser', 'currentUserAsync', 'signUp', 'logIn', 'become', 'logOut', 'requestPasswordReset', 'upgradeToRevocableSession', 'linkWith'], controller); - config['UserController'] = controller; - }, - getUserController: function () { - return config['UserController']; - }, - setLiveQueryController: function (controller) { - requireMethods('LiveQueryController', ['subscribe', 'unsubscribe', 'open', 'close'], controller); - config['LiveQueryController'] = controller; - }, - getLiveQueryController: function () { - return config['LiveQueryController']; - }, - setHooksController: function (controller) { - requireMethods('HooksController', ['create', 'get', 'update', 'remove'], controller); - config['HooksController'] = controller; - }, - getHooksController: function () { - return config['HooksController']; - } -}; \ No newline at end of file diff --git a/lib/browser/EventEmitter.js b/lib/browser/EventEmitter.js deleted file mode 100644 index e9414f0bf..000000000 --- a/lib/browser/EventEmitter.js +++ /dev/null @@ -1,15 +0,0 @@ -'use strict'; - -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * This is a simple wrapper to unify EventEmitter implementations across platforms. - */ - -module.exports = require('events').EventEmitter; -var EventEmitter; \ No newline at end of file diff --git a/lib/browser/FacebookUtils.js b/lib/browser/FacebookUtils.js deleted file mode 100644 index bb8b7ef51..000000000 --- a/lib/browser/FacebookUtils.js +++ /dev/null @@ -1,243 +0,0 @@ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _parseDate = require('./parseDate'); - -var _parseDate2 = _interopRequireDefault(_parseDate); - -var _ParseUser = require('./ParseUser'); - -var _ParseUser2 = _interopRequireDefault(_ParseUser); - -function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { default: obj }; -} - -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * -weak - */ - -var PUBLIC_KEY = "*"; - -var initialized = false; -var requestedPermissions; -var initOptions; -var provider = { - authenticate: function (options) { - var _this = this; - - if (typeof FB === 'undefined') { - options.error(this, 'Facebook SDK not found.'); - } - FB.login(function (response) { - if (response.authResponse) { - if (options.success) { - options.success(_this, { - id: response.authResponse.userID, - access_token: response.authResponse.accessToken, - expiration_date: new Date(response.authResponse.expiresIn * 1000 + new Date().getTime()).toJSON() - }); - } - } else { - if (options.error) { - options.error(_this, response); - } - } - }, { - scope: requestedPermissions - }); - }, - restoreAuthentication: function (authData) { - if (authData) { - var expiration = (0, _parseDate2.default)(authData.expiration_date); - var expiresIn = expiration ? (expiration.getTime() - new Date().getTime()) / 1000 : 0; - - var authResponse = { - userID: authData.id, - accessToken: authData.access_token, - expiresIn: expiresIn - }; - var newOptions = {}; - if (initOptions) { - for (var key in initOptions) { - newOptions[key] = initOptions[key]; - } - } - newOptions.authResponse = authResponse; - - // Suppress checks for login status from the browser. - newOptions.status = false; - - // If the user doesn't match the one known by the FB SDK, log out. - // Most of the time, the users will match -- it's only in cases where - // the FB SDK knows of a different user than the one being restored - // from a Parse User that logged in with username/password. - var existingResponse = FB.getAuthResponse(); - if (existingResponse && existingResponse.userID !== authResponse.userID) { - FB.logout(); - } - - FB.init(newOptions); - } - return true; - }, - getAuthType: function () { - return 'facebook'; - }, - deauthenticate: function () { - this.restoreAuthentication(null); - } -}; - -/** - * Provides a set of utilities for using Parse with Facebook. - * @class Parse.FacebookUtils - * @static - */ -var FacebookUtils = { - /** - * Initializes Parse Facebook integration. Call this function after you - * have loaded the Facebook Javascript SDK with the same parameters - * as you would pass to
- *
- * FB.init()
. Parse.FacebookUtils will invoke FB.init() for you
- * with these arguments.
- *
- * @method init
- * @param {Object} options Facebook options argument as described here:
- *
- * FB.init(). The status flag will be coerced to 'false' because it
- * interferes with Parse Facebook integration. Call FB.getLoginStatus()
- * explicitly if this behavior is required by your application.
- */
- init: function (options) {
- if (typeof FB === 'undefined') {
- throw new Error('The Facebook JavaScript SDK must be loaded before calling init.');
- }
- initOptions = {};
- if (options) {
- for (var key in options) {
- initOptions[key] = options[key];
- }
- }
- if (initOptions.status && typeof console !== 'undefined') {
- var warn = console.warn || console.log || function () {};
- warn.call(console, 'The "status" flag passed into' + ' FB.init, when set to true, can interfere with Parse Facebook' + ' integration, so it has been suppressed. Please call' + ' FB.getLoginStatus() explicitly if you require this behavior.');
- }
- initOptions.status = false;
- FB.init(initOptions);
- _ParseUser2.default._registerAuthenticationProvider(provider);
- initialized = true;
- },
-
- /**
- * Gets whether the user has their account linked to Facebook.
- *
- * @method isLinked
- * @param {Parse.User} user User to check for a facebook link.
- * The user must be logged in on this device.
- * @return {Boolean} true
if the user has their account
- * linked to Facebook.
- */
- isLinked: function (user) {
- return user._isLinked('facebook');
- },
-
- /**
- * Logs in a user using Facebook. This method delegates to the Facebook
- * SDK to authenticate the user, and then automatically logs in (or
- * creates, in the case where it is a new user) a Parse.User.
- *
- * @method logIn
- * @param {String, Object} permissions The permissions required for Facebook
- * log in. This is a comma-separated string of permissions.
- * Alternatively, supply a Facebook authData object as described in our
- * REST API docs if you want to handle getting facebook auth tokens
- * yourself.
- * @param {Object} options Standard options object with success and error
- * callbacks.
- */
- logIn: function (permissions, options) {
- if (!permissions || typeof permissions === 'string') {
- if (!initialized) {
- throw new Error('You must initialize FacebookUtils before calling logIn.');
- }
- requestedPermissions = permissions;
- return _ParseUser2.default._logInWith('facebook', options);
- } else {
- var newOptions = {};
- if (options) {
- for (var key in options) {
- newOptions[key] = options[key];
- }
- }
- newOptions.authData = permissions;
- return _ParseUser2.default._logInWith('facebook', newOptions);
- }
- },
-
- /**
- * Links Facebook to an existing PFUser. This method delegates to the
- * Facebook SDK to authenticate the user, and then automatically links
- * the account to the Parse.User.
- *
- * @method link
- * @param {Parse.User} user User to link to Facebook. This must be the
- * current user.
- * @param {String, Object} permissions The permissions required for Facebook
- * log in. This is a comma-separated string of permissions.
- * Alternatively, supply a Facebook authData object as described in our
- * REST API docs if you want to handle getting facebook auth tokens
- * yourself.
- * @param {Object} options Standard options object with success and error
- * callbacks.
- */
- link: function (user, permissions, options) {
- if (!permissions || typeof permissions === 'string') {
- if (!initialized) {
- throw new Error('You must initialize FacebookUtils before calling link.');
- }
- requestedPermissions = permissions;
- return user._linkWith('facebook', options);
- } else {
- var newOptions = {};
- if (options) {
- for (var key in options) {
- newOptions[key] = options[key];
- }
- }
- newOptions.authData = permissions;
- return user._linkWith('facebook', newOptions);
- }
- },
-
- /**
- * Unlinks the Parse.User from a Facebook account.
- *
- * @method unlink
- * @param {Parse.User} user User to unlink from Facebook. This must be the
- * current user.
- * @param {Object} options Standard options object with success and error
- * callbacks.
- */
- unlink: function (user, options) {
- if (!initialized) {
- throw new Error('You must initialize FacebookUtils before calling unlink.');
- }
- return user._unlinkFrom('facebook', options);
- }
-};
-
-exports.default = FacebookUtils;
\ No newline at end of file
diff --git a/lib/browser/InstallationController.js b/lib/browser/InstallationController.js
deleted file mode 100644
index 7b01e3cad..000000000
--- a/lib/browser/InstallationController.js
+++ /dev/null
@@ -1,64 +0,0 @@
-'use strict';
-
-var _CoreManager = require('./CoreManager');
-
-var _CoreManager2 = _interopRequireDefault(_CoreManager);
-
-var _ParsePromise = require('./ParsePromise');
-
-var _ParsePromise2 = _interopRequireDefault(_ParsePromise);
-
-var _Storage = require('./Storage');
-
-var _Storage2 = _interopRequireDefault(_Storage);
-
-function _interopRequireDefault(obj) {
- return obj && obj.__esModule ? obj : { default: obj };
-}
-
-var iidCache = null; /**
- * Copyright (c) 2015-present, Parse, LLC.
- * All rights reserved.
- *
- * This source code is licensed under the BSD-style license found in the
- * LICENSE file in the root directory of this source tree. An additional grant
- * of patent rights can be found in the PATENTS file in the same directory.
- *
- *
- */
-
-function hexOctet() {
- return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
-}
-
-function generateId() {
- return hexOctet() + hexOctet() + '-' + hexOctet() + '-' + hexOctet() + '-' + hexOctet() + '-' + hexOctet() + hexOctet() + hexOctet();
-}
-
-var InstallationController = {
- currentInstallationId: function () {
- if (typeof iidCache === 'string') {
- return _ParsePromise2.default.as(iidCache);
- }
- var path = _Storage2.default.generatePath('installationId');
- return _Storage2.default.getItemAsync(path).then(function (iid) {
- if (!iid) {
- iid = generateId();
- return _Storage2.default.setItemAsync(path, iid).then(function () {
- iidCache = iid;
- return iid;
- });
- }
- iidCache = iid;
- return iid;
- });
- },
- _clearCache: function () {
- iidCache = null;
- },
- _setInstallationIdCache: function (iid) {
- iidCache = iid;
- }
-};
-
-module.exports = InstallationController;
\ No newline at end of file
diff --git a/lib/browser/LiveQueryClient.js b/lib/browser/LiveQueryClient.js
deleted file mode 100644
index bc9603a62..000000000
--- a/lib/browser/LiveQueryClient.js
+++ /dev/null
@@ -1,595 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-
-var _typeof2 = require('babel-runtime/helpers/typeof');
-
-var _typeof3 = _interopRequireDefault(_typeof2);
-
-var _getIterator2 = require('babel-runtime/core-js/get-iterator');
-
-var _getIterator3 = _interopRequireDefault(_getIterator2);
-
-var _stringify = require('babel-runtime/core-js/json/stringify');
-
-var _stringify2 = _interopRequireDefault(_stringify);
-
-var _map = require('babel-runtime/core-js/map');
-
-var _map2 = _interopRequireDefault(_map);
-
-var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of');
-
-var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
-
-var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
-
-var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
-
-var _createClass2 = require('babel-runtime/helpers/createClass');
-
-var _createClass3 = _interopRequireDefault(_createClass2);
-
-var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn');
-
-var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
-
-var _inherits2 = require('babel-runtime/helpers/inherits');
-
-var _inherits3 = _interopRequireDefault(_inherits2);
-
-var _EventEmitter2 = require('./EventEmitter');
-
-var _EventEmitter3 = _interopRequireDefault(_EventEmitter2);
-
-var _ParsePromise = require('./ParsePromise');
-
-var _ParsePromise2 = _interopRequireDefault(_ParsePromise);
-
-var _ParseObject = require('./ParseObject');
-
-var _ParseObject2 = _interopRequireDefault(_ParseObject);
-
-var _LiveQuerySubscription = require('./LiveQuerySubscription');
-
-var _LiveQuerySubscription2 = _interopRequireDefault(_LiveQuerySubscription);
-
-function _interopRequireDefault(obj) {
- return obj && obj.__esModule ? obj : { default: obj };
-}
-
-// The LiveQuery client inner state
-/**
- * Copyright (c) 2015-present, Parse, LLC.
- * All rights reserved.
- *
- * This source code is licensed under the BSD-style license found in the
- * LICENSE file in the root directory of this source tree. An additional grant
- * of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-var CLIENT_STATE = {
- INITIALIZED: 'initialized',
- CONNECTING: 'connecting',
- CONNECTED: 'connected',
- CLOSED: 'closed',
- RECONNECTING: 'reconnecting',
- DISCONNECTED: 'disconnected'
-};
-
-// The event type the LiveQuery client should sent to server
-var OP_TYPES = {
- CONNECT: 'connect',
- SUBSCRIBE: 'subscribe',
- UNSUBSCRIBE: 'unsubscribe',
- ERROR: 'error'
-};
-
-// The event we get back from LiveQuery server
-var OP_EVENTS = {
- CONNECTED: 'connected',
- SUBSCRIBED: 'subscribed',
- UNSUBSCRIBED: 'unsubscribed',
- ERROR: 'error',
- CREATE: 'create',
- UPDATE: 'update',
- ENTER: 'enter',
- LEAVE: 'leave',
- DELETE: 'delete'
-};
-
-// The event the LiveQuery client should emit
-var CLIENT_EMMITER_TYPES = {
- CLOSE: 'close',
- ERROR: 'error',
- OPEN: 'open'
-};
-
-// The event the LiveQuery subscription should emit
-var SUBSCRIPTION_EMMITER_TYPES = {
- OPEN: 'open',
- CLOSE: 'close',
- ERROR: 'error',
- CREATE: 'create',
- UPDATE: 'update',
- ENTER: 'enter',
- LEAVE: 'leave',
- DELETE: 'delete'
-};
-
-var generateInterval = function (k) {
- return Math.random() * Math.min(30, Math.pow(2, k) - 1) * 1000;
-};
-
-/**
- * Creates a new LiveQueryClient.
- * Extends events.EventEmitter
- * cloud functions.
- *
- * A wrapper of a standard WebSocket client. We add several useful methods to
- * help you connect/disconnect to LiveQueryServer, subscribe/unsubscribe a ParseQuery easily.
- *
- * javascriptKey and masterKey are used for verifying the LiveQueryClient when it tries
- * to connect to the LiveQuery server
- *
- * @class Parse.LiveQueryClient
- * @constructor
- * @param {Object} options
- * @param {string} options.applicationId - applicationId of your Parse app
- * @param {string} options.serverURL - the URL of your LiveQuery server
- * @param {string} options.javascriptKey (optional)
- * @param {string} options.masterKey (optional) Your Parse Master Key. (Node.js only!)
- * @param {string} options.sessionToken (optional)
- *
- *
- * We expose three events to help you monitor the status of the LiveQueryClient.
- *
- * - * let Parse = require('parse/node'); - * let LiveQueryClient = Parse.LiveQueryClient; - * let client = new LiveQueryClient({ - * applicationId: '', - * serverURL: '', - * javascriptKey: '', - * masterKey: '' - * }); - *- * - * Open - When we establish the WebSocket connection to the LiveQuery server, you'll get this event. - *
- * client.on('open', () => { - * - * });- * - * Close - When we lose the WebSocket connection to the LiveQuery server, you'll get this event. - *
- * client.on('close', () => { - * - * });- * - * Error - When some network error or LiveQuery server error happens, you'll get this event. - *
- * client.on('error', (error) => { - * - * });- * - * - */ - -var LiveQueryClient = function (_EventEmitter) { - (0, _inherits3.default)(LiveQueryClient, _EventEmitter); - - function LiveQueryClient(_ref) { - var applicationId = _ref.applicationId, - serverURL = _ref.serverURL, - javascriptKey = _ref.javascriptKey, - masterKey = _ref.masterKey, - sessionToken = _ref.sessionToken; - (0, _classCallCheck3.default)(this, LiveQueryClient); - - var _this = (0, _possibleConstructorReturn3.default)(this, (LiveQueryClient.__proto__ || (0, _getPrototypeOf2.default)(LiveQueryClient)).call(this)); - - if (!serverURL || serverURL.indexOf('ws') !== 0) { - throw new Error('You need to set a proper Parse LiveQuery server url before using LiveQueryClient'); - } - - _this.reconnectHandle = null; - _this.attempts = 1;; - _this.id = 0; - _this.requestId = 1; - _this.serverURL = serverURL; - _this.applicationId = applicationId; - _this.javascriptKey = javascriptKey; - _this.masterKey = masterKey; - _this.sessionToken = sessionToken; - _this.connectPromise = new _ParsePromise2.default(); - _this.subscriptions = new _map2.default(); - _this.state = CLIENT_STATE.INITIALIZED; - return _this; - } - - (0, _createClass3.default)(LiveQueryClient, [{ - key: 'shouldOpen', - value: function () { - return this.state === CLIENT_STATE.INITIALIZED || this.state === CLIENT_STATE.DISCONNECTED; - } - - /** - * Subscribes to a ParseQuery - * - * If you provide the sessionToken, when the LiveQuery server gets ParseObject's - * updates from parse server, it'll try to check whether the sessionToken fulfills - * the ParseObject's ACL. The LiveQuery server will only send updates to clients whose - * sessionToken is fit for the ParseObject's ACL. You can check the LiveQuery protocol - * here for more details. The subscription you get is the same subscription you get - * from our Standard API. - * - * @method subscribe - * @param {Object} query - the ParseQuery you want to subscribe to - * @param {string} sessionToken (optional) - * @return {Object} subscription - */ - - }, { - key: 'subscribe', - value: function (query, sessionToken) { - var _this2 = this; - - if (!query) { - return; - } - var where = query.toJSON().where; - var className = query.className; - var subscribeRequest = { - op: OP_TYPES.SUBSCRIBE, - requestId: this.requestId, - query: { - className: className, - where: where - } - }; - - if (sessionToken) { - subscribeRequest.sessionToken = sessionToken; - } - - var subscription = new _LiveQuerySubscription2.default(this.requestId, query, sessionToken); - this.subscriptions.set(this.requestId, subscription); - this.requestId += 1; - this.connectPromise.then(function () { - _this2.socket.send((0, _stringify2.default)(subscribeRequest)); - }); - - // adding listener so process does not crash - // best practice is for developer to register their own listener - subscription.on('error', function () {}); - - return subscription; - } - - /** - * After calling unsubscribe you'll stop receiving events from the subscription object. - * - * @method unsubscribe - * @param {Object} subscription - subscription you would like to unsubscribe from. - */ - - }, { - key: 'unsubscribe', - value: function (subscription) { - var _this3 = this; - - if (!subscription) { - return; - } - - this.subscriptions.delete(subscription.id); - var unsubscribeRequest = { - op: OP_TYPES.UNSUBSCRIBE, - requestId: subscription.id - }; - this.connectPromise.then(function () { - _this3.socket.send((0, _stringify2.default)(unsubscribeRequest)); - }); - } - - /** - * After open is called, the LiveQueryClient will try to send a connect request - * to the LiveQuery server. - * - * @method open - */ - - }, { - key: 'open', - value: function () { - var _this4 = this; - - var WebSocketImplementation = this._getWebSocketImplementation(); - if (!WebSocketImplementation) { - this.emit(CLIENT_EMMITER_TYPES.ERROR, 'Can not find WebSocket implementation'); - return; - } - - if (this.state !== CLIENT_STATE.RECONNECTING) { - this.state = CLIENT_STATE.CONNECTING; - } - - // Get WebSocket implementation - this.socket = new WebSocketImplementation(this.serverURL); - - // Bind WebSocket callbacks - this.socket.onopen = function () { - _this4._handleWebSocketOpen(); - }; - - this.socket.onmessage = function (event) { - _this4._handleWebSocketMessage(event); - }; - - this.socket.onclose = function () { - _this4._handleWebSocketClose(); - }; - - this.socket.onerror = function (error) { - _this4._handleWebSocketError(error); - }; - } - }, { - key: 'resubscribe', - value: function () { - var _this5 = this; - - this.subscriptions.forEach(function (subscription, requestId) { - var query = subscription.query; - var where = query.toJSON().where; - var className = query.className; - var sessionToken = subscription.sessionToken; - var subscribeRequest = { - op: OP_TYPES.SUBSCRIBE, - requestId: requestId, - query: { - className: className, - where: where - } - }; - - if (sessionToken) { - subscribeRequest.sessionToken = sessionToken; - } - - _this5.connectPromise.then(function () { - _this5.socket.send((0, _stringify2.default)(subscribeRequest)); - }); - }); - } - - /** - * This method will close the WebSocket connection to this LiveQueryClient, - * cancel the auto reconnect and unsubscribe all subscriptions based on it. - * - * @method close - */ - - }, { - key: 'close', - value: function () { - if (this.state === CLIENT_STATE.INITIALIZED || this.state === CLIENT_STATE.DISCONNECTED) { - return; - } - this.state = CLIENT_STATE.DISCONNECTED; - this.socket.close(); - // Notify each subscription about the close - var _iteratorNormalCompletion = true; - var _didIteratorError = false; - var _iteratorError = undefined; - - try { - for (var _iterator = (0, _getIterator3.default)(this.subscriptions.values()), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { - var subscription = _step.value; - - subscription.emit(SUBSCRIPTION_EMMITER_TYPES.CLOSE); - } - } catch (err) { - _didIteratorError = true; - _iteratorError = err; - } finally { - try { - if (!_iteratorNormalCompletion && _iterator.return) { - _iterator.return(); - } - } finally { - if (_didIteratorError) { - throw _iteratorError; - } - } - } - - this._handleReset(); - this.emit(CLIENT_EMMITER_TYPES.CLOSE); - } - }, { - key: '_getWebSocketImplementation', - value: function () { - return typeof WebSocket === 'function' || (typeof WebSocket === 'undefined' ? 'undefined' : (0, _typeof3.default)(WebSocket)) === 'object' ? WebSocket : null; - } - - // ensure we start with valid state if connect is called again after close - - }, { - key: '_handleReset', - value: function () { - this.attempts = 1;; - this.id = 0; - this.requestId = 1; - this.connectPromise = new _ParsePromise2.default(); - this.subscriptions = new _map2.default(); - } - }, { - key: '_handleWebSocketOpen', - value: function () { - this.attempts = 1; - var connectRequest = { - op: OP_TYPES.CONNECT, - applicationId: this.applicationId, - javascriptKey: this.javascriptKey, - masterKey: this.masterKey, - sessionToken: this.sessionToken - }; - this.socket.send((0, _stringify2.default)(connectRequest)); - } - }, { - key: '_handleWebSocketMessage', - value: function (event) { - var data = event.data; - if (typeof data === 'string') { - data = JSON.parse(data); - } - var subscription = null; - if (data.requestId) { - subscription = this.subscriptions.get(data.requestId); - } - switch (data.op) { - case OP_EVENTS.CONNECTED: - if (this.state === CLIENT_STATE.RECONNECTING) { - this.resubscribe(); - } - this.emit(CLIENT_EMMITER_TYPES.OPEN); - this.id = data.clientId; - this.connectPromise.resolve(); - this.state = CLIENT_STATE.CONNECTED; - break; - case OP_EVENTS.SUBSCRIBED: - if (subscription) { - subscription.emit(SUBSCRIPTION_EMMITER_TYPES.OPEN); - } - break; - case OP_EVENTS.ERROR: - if (data.requestId) { - if (subscription) { - subscription.emit(SUBSCRIPTION_EMMITER_TYPES.ERROR, data.error); - } - } else { - this.emit(CLIENT_EMMITER_TYPES.ERROR, data.error); - } - break; - case OP_EVENTS.UNSUBSCRIBED: - // We have already deleted subscription in unsubscribe(), do nothing here - break; - default: - // create, update, enter, leave, delete cases - var className = data.object.className; - // Delete the extrea __type and className fields during transfer to full JSON - delete data.object.__type; - delete data.object.className; - var parseObject = new _ParseObject2.default(className); - parseObject._finishFetch(data.object); - if (!subscription) { - break; - } - subscription.emit(data.op, parseObject); - } - } - }, { - key: '_handleWebSocketClose', - value: function () { - if (this.state === CLIENT_STATE.DISCONNECTED) { - return; - } - this.state = CLIENT_STATE.CLOSED; - this.emit(CLIENT_EMMITER_TYPES.CLOSE); - // Notify each subscription about the close - var _iteratorNormalCompletion2 = true; - var _didIteratorError2 = false; - var _iteratorError2 = undefined; - - try { - for (var _iterator2 = (0, _getIterator3.default)(this.subscriptions.values()), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { - var subscription = _step2.value; - - subscription.emit(SUBSCRIPTION_EMMITER_TYPES.CLOSE); - } - } catch (err) { - _didIteratorError2 = true; - _iteratorError2 = err; - } finally { - try { - if (!_iteratorNormalCompletion2 && _iterator2.return) { - _iterator2.return(); - } - } finally { - if (_didIteratorError2) { - throw _iteratorError2; - } - } - } - - this._handleReconnect(); - } - }, { - key: '_handleWebSocketError', - value: function (error) { - this.emit(CLIENT_EMMITER_TYPES.ERROR, error); - var _iteratorNormalCompletion3 = true; - var _didIteratorError3 = false; - var _iteratorError3 = undefined; - - try { - for (var _iterator3 = (0, _getIterator3.default)(this.subscriptions.values()), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) { - var subscription = _step3.value; - - subscription.emit(SUBSCRIPTION_EMMITER_TYPES.ERROR); - } - } catch (err) { - _didIteratorError3 = true; - _iteratorError3 = err; - } finally { - try { - if (!_iteratorNormalCompletion3 && _iterator3.return) { - _iterator3.return(); - } - } finally { - if (_didIteratorError3) { - throw _iteratorError3; - } - } - } - - this._handleReconnect(); - } - }, { - key: '_handleReconnect', - value: function () { - var _this6 = this; - - // if closed or currently reconnecting we stop attempting to reconnect - if (this.state === CLIENT_STATE.DISCONNECTED) { - return; - } - - this.state = CLIENT_STATE.RECONNECTING; - var time = generateInterval(this.attempts); - - // handle case when both close/error occur at frequent rates we ensure we do not reconnect unnecessarily. - // we're unable to distinguish different between close/error when we're unable to reconnect therefore - // we try to reonnect in both cases - // server side ws and browser WebSocket behave differently in when close/error get triggered - - if (this.reconnectHandle) { - clearTimeout(this.reconnectHandle); - } - - this.reconnectHandle = setTimeout(function () { - _this6.attempts++; - _this6.connectPromise = new _ParsePromise2.default(); - _this6.open(); - }.bind(this), time); - } - }]); - return LiveQueryClient; -}(_EventEmitter3.default); - -exports.default = LiveQueryClient; \ No newline at end of file diff --git a/lib/browser/LiveQuerySubscription.js b/lib/browser/LiveQuerySubscription.js deleted file mode 100644 index 4078b65d5..000000000 --- a/lib/browser/LiveQuerySubscription.js +++ /dev/null @@ -1,162 +0,0 @@ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); - -var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); - -var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); - -var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); - -var _createClass2 = require('babel-runtime/helpers/createClass'); - -var _createClass3 = _interopRequireDefault(_createClass2); - -var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); - -var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); - -var _inherits2 = require('babel-runtime/helpers/inherits'); - -var _inherits3 = _interopRequireDefault(_inherits2); - -var _EventEmitter2 = require('./EventEmitter'); - -var _EventEmitter3 = _interopRequireDefault(_EventEmitter2); - -var _CoreManager = require('./CoreManager'); - -var _CoreManager2 = _interopRequireDefault(_CoreManager); - -function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { default: obj }; -} - -/** - * Creates a new LiveQuery Subscription. - * Extends events.EventEmitter - * cloud functions. - * - * @constructor - * @param {string} id - subscription id - * @param {string} query - query to subscribe to - * @param {string} sessionToken - optional session token - * - *
Open Event - When you call query.subscribe(), we send a subscribe request to - * the LiveQuery server, when we get the confirmation from the LiveQuery server, - * this event will be emitted. When the client loses WebSocket connection to the - * LiveQuery server, we will try to auto reconnect the LiveQuery server. If we - * reconnect the LiveQuery server and successfully resubscribe the ParseQuery, - * you'll also get this event. - * - *
- * subscription.on('open', () => { - * - * });- * - *
Create Event - When a new ParseObject is created and it fulfills the ParseQuery you subscribe, - * you'll get this event. The object is the ParseObject which is created. - * - *
- * subscription.on('create', (object) => { - * - * });- * - *
Update Event - When an existing ParseObject which fulfills the ParseQuery you subscribe - * is updated (The ParseObject fulfills the ParseQuery before and after changes), - * you'll get this event. The object is the ParseObject which is updated. - * Its content is the latest value of the ParseObject. - * - *
- * subscription.on('update', (object) => { - * - * });- * - *
Enter Event - When an existing ParseObject's old value doesn't fulfill the ParseQuery - * but its new value fulfills the ParseQuery, you'll get this event. The object is the - * ParseObject which enters the ParseQuery. Its content is the latest value of the ParseObject. - * - *
- * subscription.on('enter', (object) => { - * - * });- * - * - *
Update Event - When an existing ParseObject's old value fulfills the ParseQuery but its new value - * doesn't fulfill the ParseQuery, you'll get this event. The object is the ParseObject - * which leaves the ParseQuery. Its content is the latest value of the ParseObject. - * - *
- * subscription.on('leave', (object) => { - * - * });- * - * - *
Delete Event - When an existing ParseObject which fulfills the ParseQuery is deleted, you'll - * get this event. The object is the ParseObject which is deleted. - * - *
- * subscription.on('delete', (object) => { - * - * });- * - * - *
Close Event - When the client loses the WebSocket connection to the LiveQuery - * server and we stop receiving events, you'll get this event. - * - *
- * subscription.on('close', () => { - * - * });- * - * - */ -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -var Subscription = function (_EventEmitter) { - (0, _inherits3.default)(Subscription, _EventEmitter); - - function Subscription(id, query, sessionToken) { - (0, _classCallCheck3.default)(this, Subscription); - - var _this2 = (0, _possibleConstructorReturn3.default)(this, (Subscription.__proto__ || (0, _getPrototypeOf2.default)(Subscription)).call(this)); - - _this2.id = id; - _this2.query = query; - _this2.sessionToken = sessionToken; - return _this2; - } - - /** - * @method unsubscribe - */ - - (0, _createClass3.default)(Subscription, [{ - key: 'unsubscribe', - value: function () { - var _this3 = this; - - var _this = this; - _CoreManager2.default.getLiveQueryController().getDefaultLiveQueryClient().then(function (liveQueryClient) { - liveQueryClient.unsubscribe(_this); - _this.emit('close'); - _this3.resolve(); - }); - } - }]); - return Subscription; -}(_EventEmitter3.default); - -exports.default = Subscription; \ No newline at end of file diff --git a/lib/browser/ObjectStateMutations.js b/lib/browser/ObjectStateMutations.js deleted file mode 100644 index b476a0e2f..000000000 --- a/lib/browser/ObjectStateMutations.js +++ /dev/null @@ -1,165 +0,0 @@ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _stringify = require('babel-runtime/core-js/json/stringify'); - -var _stringify2 = _interopRequireDefault(_stringify); - -var _typeof2 = require('babel-runtime/helpers/typeof'); - -var _typeof3 = _interopRequireDefault(_typeof2); - -exports.defaultState = defaultState; -exports.setServerData = setServerData; -exports.setPendingOp = setPendingOp; -exports.pushPendingState = pushPendingState; -exports.popPendingState = popPendingState; -exports.mergeFirstPendingState = mergeFirstPendingState; -exports.estimateAttribute = estimateAttribute; -exports.estimateAttributes = estimateAttributes; -exports.commitServerChanges = commitServerChanges; - -var _encode = require('./encode'); - -var _encode2 = _interopRequireDefault(_encode); - -var _ParseFile = require('./ParseFile'); - -var _ParseFile2 = _interopRequireDefault(_ParseFile); - -var _ParseObject = require('./ParseObject'); - -var _ParseObject2 = _interopRequireDefault(_ParseObject); - -var _ParsePromise = require('./ParsePromise'); - -var _ParsePromise2 = _interopRequireDefault(_ParsePromise); - -var _ParseRelation = require('./ParseRelation'); - -var _ParseRelation2 = _interopRequireDefault(_ParseRelation); - -var _TaskQueue = require('./TaskQueue'); - -var _TaskQueue2 = _interopRequireDefault(_TaskQueue); - -var _ParseOp = require('./ParseOp'); - -function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { default: obj }; -} - -function defaultState() { - return { - serverData: {}, - pendingOps: [{}], - objectCache: {}, - tasks: new _TaskQueue2.default(), - existed: false - }; -} /** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * - */ - -function setServerData(serverData, attributes) { - for (var _attr in attributes) { - if (typeof attributes[_attr] !== 'undefined') { - serverData[_attr] = attributes[_attr]; - } else { - delete serverData[_attr]; - } - } -} - -function setPendingOp(pendingOps, attr, op) { - var last = pendingOps.length - 1; - if (op) { - pendingOps[last][attr] = op; - } else { - delete pendingOps[last][attr]; - } -} - -function pushPendingState(pendingOps) { - pendingOps.push({}); -} - -function popPendingState(pendingOps) { - var first = pendingOps.shift(); - if (!pendingOps.length) { - pendingOps[0] = {}; - } - return first; -} - -function mergeFirstPendingState(pendingOps) { - var first = popPendingState(pendingOps); - var next = pendingOps[0]; - for (var _attr2 in first) { - if (next[_attr2] && first[_attr2]) { - var merged = next[_attr2].mergeWith(first[_attr2]); - if (merged) { - next[_attr2] = merged; - } - } else { - next[_attr2] = first[_attr2]; - } - } -} - -function estimateAttribute(serverData, pendingOps, className, id, attr) { - var value = serverData[attr]; - for (var i = 0; i < pendingOps.length; i++) { - if (pendingOps[i][attr]) { - if (pendingOps[i][attr] instanceof _ParseOp.RelationOp) { - if (id) { - value = pendingOps[i][attr].applyTo(value, { className: className, id: id }, attr); - } - } else { - value = pendingOps[i][attr].applyTo(value); - } - } - } - return value; -} - -function estimateAttributes(serverData, pendingOps, className, id) { - var data = {}; - var attr = void 0; - for (attr in serverData) { - data[attr] = serverData[attr]; - } - for (var i = 0; i < pendingOps.length; i++) { - for (attr in pendingOps[i]) { - if (pendingOps[i][attr] instanceof _ParseOp.RelationOp) { - if (id) { - data[attr] = pendingOps[i][attr].applyTo(data[attr], { className: className, id: id }, attr); - } - } else { - data[attr] = pendingOps[i][attr].applyTo(data[attr]); - } - } - } - return data; -} - -function commitServerChanges(serverData, objectCache, changes) { - for (var _attr3 in changes) { - var val = changes[_attr3]; - serverData[_attr3] = val; - if (val && (typeof val === 'undefined' ? 'undefined' : (0, _typeof3.default)(val)) === 'object' && !(val instanceof _ParseObject2.default) && !(val instanceof _ParseFile2.default) && !(val instanceof _ParseRelation2.default)) { - var json = (0, _encode2.default)(val, false, true); - objectCache[_attr3] = (0, _stringify2.default)(json); - } - } -} \ No newline at end of file diff --git a/lib/browser/Parse.js b/lib/browser/Parse.js deleted file mode 100644 index 2499dc786..000000000 --- a/lib/browser/Parse.js +++ /dev/null @@ -1,186 +0,0 @@ -'use strict'; - -var _decode = require('./decode'); - -var _decode2 = _interopRequireDefault(_decode); - -var _encode = require('./encode'); - -var _encode2 = _interopRequireDefault(_encode); - -var _CoreManager = require('./CoreManager'); - -var _CoreManager2 = _interopRequireDefault(_CoreManager); - -var _InstallationController = require('./InstallationController'); - -var _InstallationController2 = _interopRequireDefault(_InstallationController); - -var _ParseOp = require('./ParseOp'); - -var ParseOp = _interopRequireWildcard(_ParseOp); - -var _RESTController = require('./RESTController'); - -var _RESTController2 = _interopRequireDefault(_RESTController); - -function _interopRequireWildcard(obj) { - if (obj && obj.__esModule) { - return obj; - } else { - var newObj = {};if (obj != null) { - for (var key in obj) { - if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; - } - }newObj.default = obj;return newObj; - } -} - -function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { default: obj }; -} - -/** - * Contains all Parse API classes and functions. - * @class Parse - * @static - */ -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -var Parse = { - /** - * Call this method first to set up your authentication tokens for Parse. - * You can get your keys from the Data Browser on parse.com. - * @method initialize - * @param {String} applicationId Your Parse Application ID. - * @param {String} javaScriptKey (optional) Your Parse JavaScript Key (Not needed for parse-server) - * @param {String} masterKey (optional) Your Parse Master Key. (Node.js only!) - * @static - */ - initialize: function (applicationId, javaScriptKey) { - if ('browser' === 'browser' && _CoreManager2.default.get('IS_NODE')) { - console.log('It looks like you\'re using the browser version of the SDK in a ' + 'node.js environment. You should require(\'parse/node\') instead.'); - } - Parse._initialize(applicationId, javaScriptKey); - }, - _initialize: function (applicationId, javaScriptKey, masterKey) { - _CoreManager2.default.set('APPLICATION_ID', applicationId); - _CoreManager2.default.set('JAVASCRIPT_KEY', javaScriptKey); - _CoreManager2.default.set('MASTER_KEY', masterKey); - _CoreManager2.default.set('USE_MASTER_KEY', false); - } -}; - -/** These legacy setters may eventually be deprecated **/ -Object.defineProperty(Parse, 'applicationId', { - get: function () { - return _CoreManager2.default.get('APPLICATION_ID'); - }, - set: function (value) { - _CoreManager2.default.set('APPLICATION_ID', value); - } -}); -Object.defineProperty(Parse, 'javaScriptKey', { - get: function () { - return _CoreManager2.default.get('JAVASCRIPT_KEY'); - }, - set: function (value) { - _CoreManager2.default.set('JAVASCRIPT_KEY', value); - } -}); -Object.defineProperty(Parse, 'masterKey', { - get: function () { - return _CoreManager2.default.get('MASTER_KEY'); - }, - set: function (value) { - _CoreManager2.default.set('MASTER_KEY', value); - } -}); -Object.defineProperty(Parse, 'serverURL', { - get: function () { - return _CoreManager2.default.get('SERVER_URL'); - }, - set: function (value) { - _CoreManager2.default.set('SERVER_URL', value); - } -}); -Object.defineProperty(Parse, 'liveQueryServerURL', { - get: function () { - return _CoreManager2.default.get('LIVEQUERY_SERVER_URL'); - }, - set: function (value) { - _CoreManager2.default.set('LIVEQUERY_SERVER_URL', value); - } -}); -/** End setters **/ - -Parse.ACL = require('./ParseACL').default; -Parse.Analytics = require('./Analytics'); -Parse.Cloud = require('./Cloud'); -Parse.CoreManager = require('./CoreManager'); -Parse.Config = require('./ParseConfig').default; -Parse.Error = require('./ParseError').default; -Parse.FacebookUtils = require('./FacebookUtils').default; -Parse.File = require('./ParseFile').default; -Parse.GeoPoint = require('./ParseGeoPoint').default; -Parse.Installation = require('./ParseInstallation').default; -Parse.Object = require('./ParseObject').default; -Parse.Op = { - Set: ParseOp.SetOp, - Unset: ParseOp.UnsetOp, - Increment: ParseOp.IncrementOp, - Add: ParseOp.AddOp, - Remove: ParseOp.RemoveOp, - AddUnique: ParseOp.AddUniqueOp, - Relation: ParseOp.RelationOp -}; -Parse.Promise = require('./ParsePromise').default; -Parse.Push = require('./Push'); -Parse.Query = require('./ParseQuery').default; -Parse.Relation = require('./ParseRelation').default; -Parse.Role = require('./ParseRole').default; -Parse.Session = require('./ParseSession').default; -Parse.Storage = require('./Storage'); -Parse.User = require('./ParseUser').default; -Parse.LiveQuery = require('./ParseLiveQuery').default; -Parse.LiveQueryClient = require('./LiveQueryClient').default; - -Parse._request = function () { - for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { - args[_key] = arguments[_key]; - } - - return _CoreManager2.default.getRESTController().request.apply(null, args); -}; -Parse._ajax = function () { - for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { - args[_key2] = arguments[_key2]; - } - - return _CoreManager2.default.getRESTController().ajax.apply(null, args); -}; -// We attempt to match the signatures of the legacy versions of these methods -Parse._decode = function (_, value) { - return (0, _decode2.default)(value); -}; -Parse._encode = function (value, _, disallowObjects) { - return (0, _encode2.default)(value, disallowObjects); -}; -Parse._getInstallationId = function () { - return _CoreManager2.default.getInstallationController().currentInstallationId(); -}; - -_CoreManager2.default.setInstallationController(_InstallationController2.default); -_CoreManager2.default.setRESTController(_RESTController2.default); - -// For legacy requires, of the form `var Parse = require('parse').Parse` -Parse.Parse = Parse; - -module.exports = Parse; \ No newline at end of file diff --git a/lib/browser/ParseACL.js b/lib/browser/ParseACL.js deleted file mode 100644 index 2aa232c18..000000000 --- a/lib/browser/ParseACL.js +++ /dev/null @@ -1,406 +0,0 @@ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _keys = require('babel-runtime/core-js/object/keys'); - -var _keys2 = _interopRequireDefault(_keys); - -var _typeof2 = require('babel-runtime/helpers/typeof'); - -var _typeof3 = _interopRequireDefault(_typeof2); - -var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); - -var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); - -var _createClass2 = require('babel-runtime/helpers/createClass'); - -var _createClass3 = _interopRequireDefault(_createClass2); - -var _ParseRole = require('./ParseRole'); - -var _ParseRole2 = _interopRequireDefault(_ParseRole); - -var _ParseUser = require('./ParseUser'); - -var _ParseUser2 = _interopRequireDefault(_ParseUser); - -function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { default: obj }; -} - -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * - */ - -var PUBLIC_KEY = '*'; - -/** - * Creates a new ACL. - * If no argument is given, the ACL has no permissions for anyone. - * If the argument is a Parse.User, the ACL will have read and write - * permission for only that user. - * If the argument is any other JSON object, that object will be interpretted - * as a serialized ACL created with toJSON(). - * @class Parse.ACL - * @constructor - * - *
An ACL, or Access Control List can be added to any
- * Parse.Object
to restrict access to only a subset of users
- * of your application.
Parse.Error
.
- * @param {String} message A detailed description of the error.
- */
-var ParseError = function ParseError(code, message) {
- (0, _classCallCheck3.default)(this, ParseError);
-
- this.code = code;
- this.message = message;
-};
-
-/**
- * Error code indicating some error other than those enumerated here.
- * @property OTHER_CAUSE
- * @static
- * @final
- */
-
-exports.default = ParseError;
-ParseError.OTHER_CAUSE = -1;
-
-/**
- * Error code indicating that something has gone wrong with the server.
- * If you get this error code, it is Parse's fault. Contact us at
- * https://parse.com/help
- * @property INTERNAL_SERVER_ERROR
- * @static
- * @final
- */
-ParseError.INTERNAL_SERVER_ERROR = 1;
-
-/**
- * Error code indicating the connection to the Parse servers failed.
- * @property CONNECTION_FAILED
- * @static
- * @final
- */
-ParseError.CONNECTION_FAILED = 100;
-
-/**
- * Error code indicating the specified object doesn't exist.
- * @property OBJECT_NOT_FOUND
- * @static
- * @final
- */
-ParseError.OBJECT_NOT_FOUND = 101;
-
-/**
- * Error code indicating you tried to query with a datatype that doesn't
- * support it, like exact matching an array or object.
- * @property INVALID_QUERY
- * @static
- * @final
- */
-ParseError.INVALID_QUERY = 102;
-
-/**
- * Error code indicating a missing or invalid classname. Classnames are
- * case-sensitive. They must start with a letter, and a-zA-Z0-9_ are the
- * only valid characters.
- * @property INVALID_CLASS_NAME
- * @static
- * @final
- */
-ParseError.INVALID_CLASS_NAME = 103;
-
-/**
- * Error code indicating an unspecified object id.
- * @property MISSING_OBJECT_ID
- * @static
- * @final
- */
-ParseError.MISSING_OBJECT_ID = 104;
-
-/**
- * Error code indicating an invalid key name. Keys are case-sensitive. They
- * must start with a letter, and a-zA-Z0-9_ are the only valid characters.
- * @property INVALID_KEY_NAME
- * @static
- * @final
- */
-ParseError.INVALID_KEY_NAME = 105;
-
-/**
- * Error code indicating a malformed pointer. You should not see this unless
- * you have been mucking about changing internal Parse code.
- * @property INVALID_POINTER
- * @static
- * @final
- */
-ParseError.INVALID_POINTER = 106;
-
-/**
- * Error code indicating that badly formed JSON was received upstream. This
- * either indicates you have done something unusual with modifying how
- * things encode to JSON, or the network is failing badly.
- * @property INVALID_JSON
- * @static
- * @final
- */
-ParseError.INVALID_JSON = 107;
-
-/**
- * Error code indicating that the feature you tried to access is only
- * available internally for testing purposes.
- * @property COMMAND_UNAVAILABLE
- * @static
- * @final
- */
-ParseError.COMMAND_UNAVAILABLE = 108;
-
-/**
- * You must call Parse.initialize before using the Parse library.
- * @property NOT_INITIALIZED
- * @static
- * @final
- */
-ParseError.NOT_INITIALIZED = 109;
-
-/**
- * Error code indicating that a field was set to an inconsistent type.
- * @property INCORRECT_TYPE
- * @static
- * @final
- */
-ParseError.INCORRECT_TYPE = 111;
-
-/**
- * Error code indicating an invalid channel name. A channel name is either
- * an empty string (the broadcast channel) or contains only a-zA-Z0-9_
- * characters and starts with a letter.
- * @property INVALID_CHANNEL_NAME
- * @static
- * @final
- */
-ParseError.INVALID_CHANNEL_NAME = 112;
-
-/**
- * Error code indicating that push is misconfigured.
- * @property PUSH_MISCONFIGURED
- * @static
- * @final
- */
-ParseError.PUSH_MISCONFIGURED = 115;
-
-/**
- * Error code indicating that the object is too large.
- * @property OBJECT_TOO_LARGE
- * @static
- * @final
- */
-ParseError.OBJECT_TOO_LARGE = 116;
-
-/**
- * Error code indicating that the operation isn't allowed for clients.
- * @property OPERATION_FORBIDDEN
- * @static
- * @final
- */
-ParseError.OPERATION_FORBIDDEN = 119;
-
-/**
- * Error code indicating the result was not found in the cache.
- * @property CACHE_MISS
- * @static
- * @final
- */
-ParseError.CACHE_MISS = 120;
-
-/**
- * Error code indicating that an invalid key was used in a nested
- * JSONObject.
- * @property INVALID_NESTED_KEY
- * @static
- * @final
- */
-ParseError.INVALID_NESTED_KEY = 121;
-
-/**
- * Error code indicating that an invalid filename was used for ParseFile.
- * A valid file name contains only a-zA-Z0-9_. characters and is between 1
- * and 128 characters.
- * @property INVALID_FILE_NAME
- * @static
- * @final
- */
-ParseError.INVALID_FILE_NAME = 122;
-
-/**
- * Error code indicating an invalid ACL was provided.
- * @property INVALID_ACL
- * @static
- * @final
- */
-ParseError.INVALID_ACL = 123;
-
-/**
- * Error code indicating that the request timed out on the server. Typically
- * this indicates that the request is too expensive to run.
- * @property TIMEOUT
- * @static
- * @final
- */
-ParseError.TIMEOUT = 124;
-
-/**
- * Error code indicating that the email address was invalid.
- * @property INVALID_EMAIL_ADDRESS
- * @static
- * @final
- */
-ParseError.INVALID_EMAIL_ADDRESS = 125;
-
-/**
- * Error code indicating a missing content type.
- * @property MISSING_CONTENT_TYPE
- * @static
- * @final
- */
-ParseError.MISSING_CONTENT_TYPE = 126;
-
-/**
- * Error code indicating a missing content length.
- * @property MISSING_CONTENT_LENGTH
- * @static
- * @final
- */
-ParseError.MISSING_CONTENT_LENGTH = 127;
-
-/**
- * Error code indicating an invalid content length.
- * @property INVALID_CONTENT_LENGTH
- * @static
- * @final
- */
-ParseError.INVALID_CONTENT_LENGTH = 128;
-
-/**
- * Error code indicating a file that was too large.
- * @property FILE_TOO_LARGE
- * @static
- * @final
- */
-ParseError.FILE_TOO_LARGE = 129;
-
-/**
- * Error code indicating an error saving a file.
- * @property FILE_SAVE_ERROR
- * @static
- * @final
- */
-ParseError.FILE_SAVE_ERROR = 130;
-
-/**
- * Error code indicating that a unique field was given a value that is
- * already taken.
- * @property DUPLICATE_VALUE
- * @static
- * @final
- */
-ParseError.DUPLICATE_VALUE = 137;
-
-/**
- * Error code indicating that a role's name is invalid.
- * @property INVALID_ROLE_NAME
- * @static
- * @final
- */
-ParseError.INVALID_ROLE_NAME = 139;
-
-/**
- * Error code indicating that an application quota was exceeded. Upgrade to
- * resolve.
- * @property EXCEEDED_QUOTA
- * @static
- * @final
- */
-ParseError.EXCEEDED_QUOTA = 140;
-
-/**
- * Error code indicating that a Cloud Code script failed.
- * @property SCRIPT_FAILED
- * @static
- * @final
- */
-ParseError.SCRIPT_FAILED = 141;
-
-/**
- * Error code indicating that a Cloud Code validation failed.
- * @property VALIDATION_ERROR
- * @static
- * @final
- */
-ParseError.VALIDATION_ERROR = 142;
-
-/**
- * Error code indicating that invalid image data was provided.
- * @property INVALID_IMAGE_DATA
- * @static
- * @final
- */
-ParseError.INVALID_IMAGE_DATA = 143;
-
-/**
- * Error code indicating an unsaved file.
- * @property UNSAVED_FILE_ERROR
- * @static
- * @final
- */
-ParseError.UNSAVED_FILE_ERROR = 151;
-
-/**
- * Error code indicating an invalid push time.
- * @property INVALID_PUSH_TIME_ERROR
- * @static
- * @final
- */
-ParseError.INVALID_PUSH_TIME_ERROR = 152;
-
-/**
- * Error code indicating an error deleting a file.
- * @property FILE_DELETE_ERROR
- * @static
- * @final
- */
-ParseError.FILE_DELETE_ERROR = 153;
-
-/**
- * Error code indicating that the application has exceeded its request
- * limit.
- * @property REQUEST_LIMIT_EXCEEDED
- * @static
- * @final
- */
-ParseError.REQUEST_LIMIT_EXCEEDED = 155;
-
-/**
- * Error code indicating an invalid event name.
- * @property INVALID_EVENT_NAME
- * @static
- * @final
- */
-ParseError.INVALID_EVENT_NAME = 160;
-
-/**
- * Error code indicating that the username is missing or empty.
- * @property USERNAME_MISSING
- * @static
- * @final
- */
-ParseError.USERNAME_MISSING = 200;
-
-/**
- * Error code indicating that the password is missing or empty.
- * @property PASSWORD_MISSING
- * @static
- * @final
- */
-ParseError.PASSWORD_MISSING = 201;
-
-/**
- * Error code indicating that the username has already been taken.
- * @property USERNAME_TAKEN
- * @static
- * @final
- */
-ParseError.USERNAME_TAKEN = 202;
-
-/**
- * Error code indicating that the email has already been taken.
- * @property EMAIL_TAKEN
- * @static
- * @final
- */
-ParseError.EMAIL_TAKEN = 203;
-
-/**
- * Error code indicating that the email is missing, but must be specified.
- * @property EMAIL_MISSING
- * @static
- * @final
- */
-ParseError.EMAIL_MISSING = 204;
-
-/**
- * Error code indicating that a user with the specified email was not found.
- * @property EMAIL_NOT_FOUND
- * @static
- * @final
- */
-ParseError.EMAIL_NOT_FOUND = 205;
-
-/**
- * Error code indicating that a user object without a valid session could
- * not be altered.
- * @property SESSION_MISSING
- * @static
- * @final
- */
-ParseError.SESSION_MISSING = 206;
-
-/**
- * Error code indicating that a user can only be created through signup.
- * @property MUST_CREATE_USER_THROUGH_SIGNUP
- * @static
- * @final
- */
-ParseError.MUST_CREATE_USER_THROUGH_SIGNUP = 207;
-
-/**
- * Error code indicating that an an account being linked is already linked
- * to another user.
- * @property ACCOUNT_ALREADY_LINKED
- * @static
- * @final
- */
-ParseError.ACCOUNT_ALREADY_LINKED = 208;
-
-/**
- * Error code indicating that the current session token is invalid.
- * @property INVALID_SESSION_TOKEN
- * @static
- * @final
- */
-ParseError.INVALID_SESSION_TOKEN = 209;
-
-/**
- * Error code indicating that a user cannot be linked to an account because
- * that account's id could not be found.
- * @property LINKED_ID_MISSING
- * @static
- * @final
- */
-ParseError.LINKED_ID_MISSING = 250;
-
-/**
- * Error code indicating that a user with a linked (e.g. Facebook) account
- * has an invalid session.
- * @property INVALID_LINKED_SESSION
- * @static
- * @final
- */
-ParseError.INVALID_LINKED_SESSION = 251;
-
-/**
- * Error code indicating that a service being linked (e.g. Facebook or
- * Twitter) is unsupported.
- * @property UNSUPPORTED_SERVICE
- * @static
- * @final
- */
-ParseError.UNSUPPORTED_SERVICE = 252;
-
-/**
- * Error code indicating that there were multiple errors. Aggregate errors
- * have an "errors" property, which is an array of error objects with more
- * detail about each error that occurred.
- * @property AGGREGATE_ERROR
- * @static
- * @final
- */
-ParseError.AGGREGATE_ERROR = 600;
-
-/**
- * Error code indicating the client was unable to read an input file.
- * @property FILE_READ_ERROR
- * @static
- * @final
- */
-ParseError.FILE_READ_ERROR = 601;
-
-/**
- * Error code indicating a real error code is unavailable because
- * we had to use an XDomainRequest object to allow CORS requests in
- * Internet Explorer, which strips the body from HTTP responses that have
- * a non-2XX status code.
- * @property X_DOMAIN_REQUEST
- * @static
- * @final
- */
-ParseError.X_DOMAIN_REQUEST = 602;
\ No newline at end of file
diff --git a/lib/browser/ParseFile.js b/lib/browser/ParseFile.js
deleted file mode 100644
index 48efdb9d8..000000000
--- a/lib/browser/ParseFile.js
+++ /dev/null
@@ -1,291 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-
-var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
-
-var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
-
-var _createClass2 = require('babel-runtime/helpers/createClass');
-
-var _createClass3 = _interopRequireDefault(_createClass2);
-
-var _CoreManager = require('./CoreManager');
-
-var _CoreManager2 = _interopRequireDefault(_CoreManager);
-
-var _ParsePromise = require('./ParsePromise');
-
-var _ParsePromise2 = _interopRequireDefault(_ParsePromise);
-
-function _interopRequireDefault(obj) {
- return obj && obj.__esModule ? obj : { default: obj };
-}
-
-/**
- * Copyright (c) 2015-present, Parse, LLC.
- * All rights reserved.
- *
- * This source code is licensed under the BSD-style license found in the
- * LICENSE file in the root directory of this source tree. An additional grant
- * of patent rights can be found in the PATENTS file in the same directory.
- *
- *
- */
-
-var dataUriRegexp = /^data:([a-zA-Z]*\/[a-zA-Z+.-]*);(charset=[a-zA-Z0-9\-\/\s]*,)?base64,/;
-
-function b64Digit(number) {
- if (number < 26) {
- return String.fromCharCode(65 + number);
- }
- if (number < 52) {
- return String.fromCharCode(97 + (number - 26));
- }
- if (number < 62) {
- return String.fromCharCode(48 + (number - 52));
- }
- if (number === 62) {
- return '+';
- }
- if (number === 63) {
- return '/';
- }
- throw new TypeError('Tried to encode large digit ' + number + ' in base64.');
-}
-
-/**
- * A Parse.File is a local representation of a file that is saved to the Parse
- * cloud.
- * @class Parse.File
- * @constructor
- * @param name {String} The file's name. This will be prefixed by a unique
- * value once the file has finished saving. The file name must begin with
- * an alphanumeric character, and consist of alphanumeric characters,
- * periods, spaces, underscores, or dashes.
- * @param data {Array} The data for the file, as either:
- * 1. an Array of byte value Numbers, or
- * 2. an Object like { base64: "..." } with a base64-encoded String.
- * 3. a File object selected with a file upload control. (3) only works
- * in Firefox 3.6+, Safari 6.0.2+, Chrome 7+, and IE 10+.
- * For example:- * var fileUploadControl = $("#profilePhotoFileUpload")[0]; - * if (fileUploadControl.files.length > 0) { - * var file = fileUploadControl.files[0]; - * var name = "photo.jpg"; - * var parseFile = new Parse.File(name, file); - * parseFile.save().then(function() { - * // The file has been saved to Parse. - * }, function(error) { - * // The file either could not be read, or could not be saved to Parse. - * }); - * }- * @param type {String} Optional Content-Type header to use for the file. If - * this is omitted, the content type will be inferred from the name's - * extension. - */ - -var ParseFile = function () { - function ParseFile(name, data, type) { - (0, _classCallCheck3.default)(this, ParseFile); - - var specifiedType = type || ''; - - this._name = name; - - if (data !== undefined) { - if (Array.isArray(data)) { - this._source = { - format: 'base64', - base64: ParseFile.encodeBase64(data), - type: specifiedType - }; - } else if (typeof File !== 'undefined' && data instanceof File) { - this._source = { - format: 'file', - file: data, - type: specifiedType - }; - } else if (data && typeof data.base64 === 'string') { - var _base = data.base64; - var commaIndex = _base.indexOf(','); - - if (commaIndex !== -1) { - var matches = dataUriRegexp.exec(_base.slice(0, commaIndex + 1)); - // if data URI with type and charset, there will be 4 matches. - this._source = { - format: 'base64', - base64: _base.slice(commaIndex + 1), - type: matches[1] - }; - } else { - this._source = { - format: 'base64', - base64: _base, - type: specifiedType - }; - } - } else { - throw new TypeError('Cannot create a Parse.File with that data.'); - } - } - } - - /** - * Gets the name of the file. Before save is called, this is the filename - * given by the user. After save is called, that name gets prefixed with a - * unique identifier. - * @method name - * @return {String} - */ - - (0, _createClass3.default)(ParseFile, [{ - key: 'name', - value: function () { - return this._name; - } - - /** - * Gets the url of the file. It is only available after you save the file or - * after you get the file from a Parse.Object. - * @method url - * @param {Object} options An object to specify url options - * @return {String} - */ - - }, { - key: 'url', - value: function (options) { - options = options || {}; - if (!this._url) { - return; - } - if (options.forceSecure) { - return this._url.replace(/^http:\/\//i, 'https://'); - } else { - return this._url; - } - } - - /** - * Saves the file to the Parse cloud. - * @method save - * @param {Object} options A Backbone-style options object. - * @return {Parse.Promise} Promise that is resolved when the save finishes. - */ - - }, { - key: 'save', - value: function (options) { - var _this = this; - - options = options || {}; - var controller = _CoreManager2.default.getFileController(); - if (!this._previousSave) { - if (this._source.format === 'file') { - this._previousSave = controller.saveFile(this._name, this._source).then(function (res) { - _this._name = res.name; - _this._url = res.url; - return _this; - }); - } else { - this._previousSave = controller.saveBase64(this._name, this._source).then(function (res) { - _this._name = res.name; - _this._url = res.url; - return _this; - }); - } - } - if (this._previousSave) { - return this._previousSave._thenRunCallbacks(options); - } - } - }, { - key: 'toJSON', - value: function () { - return { - __type: 'File', - name: this._name, - url: this._url - }; - } - }, { - key: 'equals', - value: function (other) { - if (this === other) { - return true; - } - // Unsaved Files are never equal, since they will be saved to different URLs - return other instanceof ParseFile && this.name() === other.name() && this.url() === other.url() && typeof this.url() !== 'undefined'; - } - }], [{ - key: 'fromJSON', - value: function (obj) { - if (obj.__type !== 'File') { - throw new TypeError('JSON object does not represent a ParseFile'); - } - var file = new ParseFile(obj.name); - file._url = obj.url; - return file; - } - }, { - key: 'encodeBase64', - value: function (bytes) { - var chunks = []; - chunks.length = Math.ceil(bytes.length / 3); - for (var i = 0; i < chunks.length; i++) { - var b1 = bytes[i * 3]; - var b2 = bytes[i * 3 + 1] || 0; - var b3 = bytes[i * 3 + 2] || 0; - - var has2 = i * 3 + 1 < bytes.length; - var has3 = i * 3 + 2 < bytes.length; - - chunks[i] = [b64Digit(b1 >> 2 & 0x3F), b64Digit(b1 << 4 & 0x30 | b2 >> 4 & 0x0F), has2 ? b64Digit(b2 << 2 & 0x3C | b3 >> 6 & 0x03) : '=', has3 ? b64Digit(b3 & 0x3F) : '='].join(''); - } - - return chunks.join(''); - } - }]); - return ParseFile; -}(); - -exports.default = ParseFile; - -var DefaultController = { - saveFile: function (name, source) { - if (source.format !== 'file') { - throw new Error('saveFile can only be used with File-type sources.'); - } - // To directly upload a File, we use a REST-style AJAX request - var headers = { - 'X-Parse-Application-ID': _CoreManager2.default.get('APPLICATION_ID'), - 'X-Parse-JavaScript-Key': _CoreManager2.default.get('JAVASCRIPT_KEY'), - 'Content-Type': source.type || (source.file ? source.file.type : null) - }; - var url = _CoreManager2.default.get('SERVER_URL'); - if (url[url.length - 1] !== '/') { - url += '/'; - } - url += 'files/' + name; - return _CoreManager2.default.getRESTController().ajax('POST', url, source.file, headers); - }, - - saveBase64: function (name, source) { - if (source.format !== 'base64') { - throw new Error('saveBase64 can only be used with Base64-type sources.'); - } - var data = { - base64: source.base64 - }; - if (source.type) { - data._ContentType = source.type; - } - - return _CoreManager2.default.getRESTController().request('POST', 'files/' + name, data); - } -}; - -_CoreManager2.default.setFileController(DefaultController); \ No newline at end of file diff --git a/lib/browser/ParseGeoPoint.js b/lib/browser/ParseGeoPoint.js deleted file mode 100644 index 98691f85b..000000000 --- a/lib/browser/ParseGeoPoint.js +++ /dev/null @@ -1,235 +0,0 @@ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _typeof2 = require('babel-runtime/helpers/typeof'); - -var _typeof3 = _interopRequireDefault(_typeof2); - -var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); - -var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); - -var _createClass2 = require('babel-runtime/helpers/createClass'); - -var _createClass3 = _interopRequireDefault(_createClass2); - -var _ParsePromise = require('./ParsePromise'); - -var _ParsePromise2 = _interopRequireDefault(_ParsePromise); - -function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { default: obj }; -} - -/** - * Creates a new GeoPoint with any of the following forms:
- * new GeoPoint(otherGeoPoint) - * new GeoPoint(30, 30) - * new GeoPoint([30, 30]) - * new GeoPoint({latitude: 30, longitude: 30}) - * new GeoPoint() // defaults to (0, 0) - *- * @class Parse.GeoPoint - * @constructor - * - *
Represents a latitude / longitude point that may be associated - * with a key in a ParseObject or used as a reference point for geo queries. - * This allows proximity-based queries on the key.
- * - *Only one key in a class may contain a GeoPoint.
- * - *Example:
- * var point = new Parse.GeoPoint(30.0, -20.0); - * var object = new Parse.Object("PlaceObject"); - * object.set("location", point); - * object.save();- */ -var ParseGeoPoint = function () { - function ParseGeoPoint(arg1, arg2) { - (0, _classCallCheck3.default)(this, ParseGeoPoint); - - if (Array.isArray(arg1)) { - ParseGeoPoint._validate(arg1[0], arg1[1]); - this._latitude = arg1[0]; - this._longitude = arg1[1]; - } else if ((typeof arg1 === 'undefined' ? 'undefined' : (0, _typeof3.default)(arg1)) === 'object') { - ParseGeoPoint._validate(arg1.latitude, arg1.longitude); - this._latitude = arg1.latitude; - this._longitude = arg1.longitude; - } else if (typeof arg1 === 'number' && typeof arg2 === 'number') { - ParseGeoPoint._validate(arg1, arg2); - this._latitude = arg1; - this._longitude = arg2; - } else { - this._latitude = 0; - this._longitude = 0; - } - } - - /** - * North-south portion of the coordinate, in range [-90, 90]. - * Throws an exception if set out of range in a modern browser. - * @property latitude - * @type Number - */ - - (0, _createClass3.default)(ParseGeoPoint, [{ - key: 'toJSON', - - /** - * Returns a JSON representation of the GeoPoint, suitable for Parse. - * @method toJSON - * @return {Object} - */ - value: function () { - ParseGeoPoint._validate(this._latitude, this._longitude); - return { - __type: 'GeoPoint', - latitude: this._latitude, - longitude: this._longitude - }; - } - }, { - key: 'equals', - value: function (other) { - return other instanceof ParseGeoPoint && this.latitude === other.latitude && this.longitude === other.longitude; - } - - /** - * Returns the distance from this GeoPoint to another in radians. - * @method radiansTo - * @param {Parse.GeoPoint} point the other Parse.GeoPoint. - * @return {Number} - */ - - }, { - key: 'radiansTo', - value: function (point) { - var d2r = Math.PI / 180.0; - var lat1rad = this.latitude * d2r; - var long1rad = this.longitude * d2r; - var lat2rad = point.latitude * d2r; - var long2rad = point.longitude * d2r; - - var sinDeltaLatDiv2 = Math.sin((lat1rad - lat2rad) / 2); - var sinDeltaLongDiv2 = Math.sin((long1rad - long2rad) / 2); - // Square of half the straight line chord distance between both points. - var a = sinDeltaLatDiv2 * sinDeltaLatDiv2 + Math.cos(lat1rad) * Math.cos(lat2rad) * sinDeltaLongDiv2 * sinDeltaLongDiv2; - a = Math.min(1.0, a); - return 2 * Math.asin(Math.sqrt(a)); - } - - /** - * Returns the distance from this GeoPoint to another in kilometers. - * @method kilometersTo - * @param {Parse.GeoPoint} point the other Parse.GeoPoint. - * @return {Number} - */ - - }, { - key: 'kilometersTo', - value: function (point) { - return this.radiansTo(point) * 6371.0; - } - - /** - * Returns the distance from this GeoPoint to another in miles. - * @method milesTo - * @param {Parse.GeoPoint} point the other Parse.GeoPoint. - * @return {Number} - */ - - }, { - key: 'milesTo', - value: function (point) { - return this.radiansTo(point) * 3958.8; - } - - /** - * Throws an exception if the given lat-long is out of bounds. - */ - - }, { - key: 'latitude', - get: function () { - return this._latitude; - }, - set: function (val) { - ParseGeoPoint._validate(val, this.longitude); - this._latitude = val; - } - - /** - * East-west portion of the coordinate, in range [-180, 180]. - * Throws if set out of range in a modern browser. - * @property longitude - * @type Number - */ - - }, { - key: 'longitude', - get: function () { - return this._longitude; - }, - set: function (val) { - ParseGeoPoint._validate(this.latitude, val); - this._longitude = val; - } - }], [{ - key: '_validate', - value: function (latitude, longitude) { - if (latitude !== latitude || longitude !== longitude) { - throw new TypeError('GeoPoint latitude and longitude must be valid numbers'); - } - if (latitude < -90.0) { - throw new TypeError('GeoPoint latitude out of bounds: ' + latitude + ' < -90.0.'); - } - if (latitude > 90.0) { - throw new TypeError('GeoPoint latitude out of bounds: ' + latitude + ' > 90.0.'); - } - if (longitude < -180.0) { - throw new TypeError('GeoPoint longitude out of bounds: ' + longitude + ' < -180.0.'); - } - if (longitude > 180.0) { - throw new TypeError('GeoPoint longitude out of bounds: ' + longitude + ' > 180.0.'); - } - } - - /** - * Creates a GeoPoint with the user's current location, if available. - * Calls options.success with a new GeoPoint instance or calls options.error. - * @method current - * @param {Object} options An object with success and error callbacks. - * @static - */ - - }, { - key: 'current', - value: function (options) { - var promise = new _ParsePromise2.default(); - navigator.geolocation.getCurrentPosition(function (location) { - promise.resolve(new ParseGeoPoint(location.coords.latitude, location.coords.longitude)); - }, function (error) { - promise.reject(error); - }); - - return promise._thenRunCallbacks(options); - } - }]); - return ParseGeoPoint; -}(); /** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * - */ - -exports.default = ParseGeoPoint; \ No newline at end of file diff --git a/lib/browser/ParseHooks.js b/lib/browser/ParseHooks.js deleted file mode 100644 index a2057e899..000000000 --- a/lib/browser/ParseHooks.js +++ /dev/null @@ -1,162 +0,0 @@ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _promise = require('babel-runtime/core-js/promise'); - -var _promise2 = _interopRequireDefault(_promise); - -exports.getFunctions = getFunctions; -exports.getTriggers = getTriggers; -exports.getFunction = getFunction; -exports.getTrigger = getTrigger; -exports.createFunction = createFunction; -exports.createTrigger = createTrigger; -exports.create = create; -exports.updateFunction = updateFunction; -exports.updateTrigger = updateTrigger; -exports.update = update; -exports.removeFunction = removeFunction; -exports.removeTrigger = removeTrigger; -exports.remove = remove; - -var _CoreManager = require('./CoreManager'); - -var _CoreManager2 = _interopRequireDefault(_CoreManager); - -var _decode = require('./decode'); - -var _decode2 = _interopRequireDefault(_decode); - -var _encode = require('./encode'); - -var _encode2 = _interopRequireDefault(_encode); - -var _ParseError = require('./ParseError'); - -var _ParseError2 = _interopRequireDefault(_ParseError); - -var _ParsePromise = require('./ParsePromise'); - -var _ParsePromise2 = _interopRequireDefault(_ParsePromise); - -function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { default: obj }; -} - -function getFunctions() { - return _CoreManager2.default.getHooksController().get("functions"); -} - -function getTriggers() { - return _CoreManager2.default.getHooksController().get("triggers"); -} - -function getFunction(name) { - return _CoreManager2.default.getHooksController().get("functions", name); -} - -function getTrigger(className, triggerName) { - return _CoreManager2.default.getHooksController().get("triggers", className, triggerName); -} - -function createFunction(functionName, url) { - return create({ functionName: functionName, url: url }); -} - -function createTrigger(className, triggerName, url) { - return create({ className: className, triggerName: triggerName, url: url }); -} - -function create(hook) { - return _CoreManager2.default.getHooksController().create(hook); -} - -function updateFunction(functionName, url) { - return update({ functionName: functionName, url: url }); -} - -function updateTrigger(className, triggerName, url) { - return update({ className: className, triggerName: triggerName, url: url }); -} - -function update(hook) { - return _CoreManager2.default.getHooksController().update(hook); -} - -function removeFunction(functionName) { - return remove({ functionName: functionName }); -} - -function removeTrigger(className, triggerName) { - return remove({ className: className, triggerName: triggerName }); -} - -function remove(hook) { - return _CoreManager2.default.getHooksController().remove(hook); -} - -var DefaultController = { - get: function (type, functionName, triggerName) { - var url = "/hooks/" + type; - if (functionName) { - url += "/" + functionName; - if (triggerName) { - url += "/" + triggerName; - } - } - return this.sendRequest("GET", url); - }, - create: function (hook) { - var url; - if (hook.functionName && hook.url) { - url = "/hooks/functions"; - } else if (hook.className && hook.triggerName && hook.url) { - url = "/hooks/triggers"; - } else { - return _promise2.default.reject({ error: 'invalid hook declaration', code: 143 }); - } - return this.sendRequest("POST", url, hook); - }, - remove: function (hook) { - var url; - if (hook.functionName) { - url = "/hooks/functions/" + hook.functionName; - delete hook.functionName; - } else if (hook.className && hook.triggerName) { - url = "/hooks/triggers/" + hook.className + "/" + hook.triggerName; - delete hook.className; - delete hook.triggerName; - } else { - return _promise2.default.reject({ error: 'invalid hook declaration', code: 143 }); - } - return this.sendRequest("PUT", url, { "__op": "Delete" }); - }, - update: function (hook) { - var url; - if (hook.functionName && hook.url) { - url = "/hooks/functions/" + hook.functionName; - delete hook.functionName; - } else if (hook.className && hook.triggerName && hook.url) { - url = "/hooks/triggers/" + hook.className + "/" + hook.triggerName; - delete hook.className; - delete hook.triggerName; - } else { - return _promise2.default.reject({ error: 'invalid hook declaration', code: 143 }); - } - return this.sendRequest('PUT', url, hook); - }, - sendRequest: function (method, url, body) { - return _CoreManager2.default.getRESTController().request(method, url, body, { useMasterKey: true }).then(function (res) { - var decoded = (0, _decode2.default)(res); - if (decoded) { - return _ParsePromise2.default.as(decoded); - } - return _ParsePromise2.default.error(new _ParseError2.default(_ParseError2.default.INVALID_JSON, 'The server returned an invalid response.')); - }); - } -}; - -_CoreManager2.default.setHooksController(DefaultController); \ No newline at end of file diff --git a/lib/browser/ParseInstallation.js b/lib/browser/ParseInstallation.js deleted file mode 100644 index 02d7be1d8..000000000 --- a/lib/browser/ParseInstallation.js +++ /dev/null @@ -1,65 +0,0 @@ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _typeof2 = require('babel-runtime/helpers/typeof'); - -var _typeof3 = _interopRequireDefault(_typeof2); - -var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); - -var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); - -var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); - -var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); - -var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); - -var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); - -var _inherits2 = require('babel-runtime/helpers/inherits'); - -var _inherits3 = _interopRequireDefault(_inherits2); - -var _ParseObject2 = require('./ParseObject'); - -var _ParseObject3 = _interopRequireDefault(_ParseObject2); - -function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { default: obj }; -} - -var Installation = function (_ParseObject) { - (0, _inherits3.default)(Installation, _ParseObject); - - function Installation(attributes) { - (0, _classCallCheck3.default)(this, Installation); - - var _this = (0, _possibleConstructorReturn3.default)(this, (Installation.__proto__ || (0, _getPrototypeOf2.default)(Installation)).call(this, '_Installation')); - - if (attributes && (typeof attributes === 'undefined' ? 'undefined' : (0, _typeof3.default)(attributes)) === 'object') { - if (!_this.set(attributes || {})) { - throw new Error('Can\'t create an invalid Session'); - } - } - return _this; - } - - return Installation; -}(_ParseObject3.default); /** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * - */ - -exports.default = Installation; - -_ParseObject3.default.registerSubclass('_Installation', Installation); \ No newline at end of file diff --git a/lib/browser/ParseLiveQuery.js b/lib/browser/ParseLiveQuery.js deleted file mode 100644 index a3e5fe31d..000000000 --- a/lib/browser/ParseLiveQuery.js +++ /dev/null @@ -1,241 +0,0 @@ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _EventEmitter = require('./EventEmitter'); - -var _EventEmitter2 = _interopRequireDefault(_EventEmitter); - -var _LiveQueryClient = require('./LiveQueryClient'); - -var _LiveQueryClient2 = _interopRequireDefault(_LiveQueryClient); - -var _CoreManager = require('./CoreManager'); - -var _CoreManager2 = _interopRequireDefault(_CoreManager); - -var _ParsePromise = require('./ParsePromise'); - -var _ParsePromise2 = _interopRequireDefault(_ParsePromise); - -function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { default: obj }; -} - -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * - */ - -function open() { - var LiveQueryController = _CoreManager2.default.getLiveQueryController(); - LiveQueryController.open(); -} - -function close() { - var LiveQueryController = _CoreManager2.default.getLiveQueryController(); - LiveQueryController.close(); -} - -/** - * - * We expose three events to help you monitor the status of the WebSocket connection: - * - *
Open - When we establish the WebSocket connection to the LiveQuery server, you'll get this event. - * - *
- * Parse.LiveQuery.on('open', () => { - * - * });- * - *
Close - When we lose the WebSocket connection to the LiveQuery server, you'll get this event. - * - *
- * Parse.LiveQuery.on('close', () => { - * - * });- * - *
Error - When some network error or LiveQuery server error happens, you'll get this event. - * - *
- * Parse.LiveQuery.on('error', (error) => { - * - * });- * - * @class Parse.LiveQuery - * @static - * - */ -var LiveQuery = new _EventEmitter2.default(); - -/** - * After open is called, the LiveQuery will try to send a connect request - * to the LiveQuery server. - * - * @method open - */ -LiveQuery.open = open; - -/** - * When you're done using LiveQuery, you can call Parse.LiveQuery.close(). - * This function will close the WebSocket connection to the LiveQuery server, - * cancel the auto reconnect, and unsubscribe all subscriptions based on it. - * If you call query.subscribe() after this, we'll create a new WebSocket - * connection to the LiveQuery server. - * - * @method close - */ - -LiveQuery.close = close; -// Register a default onError callback to make sure we do not crash on error -LiveQuery.on('error', function () {}); - -exports.default = LiveQuery; - -function getSessionToken() { - var controller = _CoreManager2.default.getUserController(); - return controller.currentUserAsync().then(function (currentUser) { - return currentUser ? currentUser.getSessionToken() : undefined; - }); -} - -function getLiveQueryClient() { - return _CoreManager2.default.getLiveQueryController().getDefaultLiveQueryClient(); -} - -var defaultLiveQueryClient = void 0; -var DefaultLiveQueryController = { - setDefaultLiveQueryClient: function (liveQueryClient) { - defaultLiveQueryClient = liveQueryClient; - }, - getDefaultLiveQueryClient: function () { - if (defaultLiveQueryClient) { - return _ParsePromise2.default.as(defaultLiveQueryClient); - } - - return getSessionToken().then(function (sessionToken) { - var liveQueryServerURL = _CoreManager2.default.get('LIVEQUERY_SERVER_URL'); - - if (liveQueryServerURL && liveQueryServerURL.indexOf('ws') !== 0) { - throw new Error('You need to set a proper Parse LiveQuery server url before using LiveQueryClient'); - } - - // If we can not find Parse.liveQueryServerURL, we try to extract it from Parse.serverURL - if (!liveQueryServerURL) { - var tempServerURL = _CoreManager2.default.get('SERVER_URL'); - var protocol = 'ws://'; - // If Parse is being served over SSL/HTTPS, ensure LiveQuery Server uses 'wss://' prefix - if (tempServerURL.indexOf('https') === 0) { - protocol = 'wss://'; - } - var host = tempServerURL.replace(/^https?:\/\//, ''); - liveQueryServerURL = protocol + host; - _CoreManager2.default.set('LIVEQUERY_SERVER_URL', liveQueryServerURL); - } - - var applicationId = _CoreManager2.default.get('APPLICATION_ID'); - var javascriptKey = _CoreManager2.default.get('JAVASCRIPT_KEY'); - var masterKey = _CoreManager2.default.get('MASTER_KEY'); - // Get currentUser sessionToken if possible - defaultLiveQueryClient = new _LiveQueryClient2.default({ - applicationId: applicationId, - serverURL: liveQueryServerURL, - javascriptKey: javascriptKey, - masterKey: masterKey, - sessionToken: sessionToken - }); - // Register a default onError callback to make sure we do not crash on error - // Cannot create these events on a nested way because of EventEmiiter from React Native - defaultLiveQueryClient.on('error', function (error) { - LiveQuery.emit('error', error); - }); - defaultLiveQueryClient.on('open', function () { - LiveQuery.emit('open'); - }); - defaultLiveQueryClient.on('close', function () { - LiveQuery.emit('close'); - }); - - return defaultLiveQueryClient; - }); - }, - open: function () { - var _this = this; - - getLiveQueryClient().then(function (liveQueryClient) { - _this.resolve(liveQueryClient.open()); - }); - }, - close: function () { - var _this2 = this; - - getLiveQueryClient().then(function (liveQueryClient) { - _this2.resolve(liveQueryClient.close()); - }); - }, - subscribe: function (query) { - var _this3 = this; - - var subscriptionWrap = new _EventEmitter2.default(); - - getLiveQueryClient().then(function (liveQueryClient) { - if (liveQueryClient.shouldOpen()) { - liveQueryClient.open(); - } - var promiseSessionToken = getSessionToken(); - // new event emitter - return promiseSessionToken.then(function (sessionToken) { - - var subscription = liveQueryClient.subscribe(query, sessionToken); - // enter, leave create, etc - - subscriptionWrap.id = subscription.id; - subscriptionWrap.query = subscription.query; - subscriptionWrap.sessionToken = subscription.sessionToken; - subscriptionWrap.unsubscribe = subscription.unsubscribe; - // Cannot create these events on a nested way because of EventEmiiter from React Native - subscription.on('open', function () { - subscriptionWrap.emit('open'); - }); - subscription.on('create', function (object) { - subscriptionWrap.emit('create', object); - }); - subscription.on('update', function (object) { - subscriptionWrap.emit('update', object); - }); - subscription.on('enter', function (object) { - subscriptionWrap.emit('enter', object); - }); - subscription.on('leave', function (object) { - subscriptionWrap.emit('leave', object); - }); - subscription.on('delete', function (object) { - subscriptionWrap.emit('delete', object); - }); - - _this3.resolve(); - }); - }); - return subscriptionWrap; - }, - unsubscribe: function (subscription) { - var _this4 = this; - - getLiveQueryClient().then(function (liveQueryClient) { - _this4.resolve(liveQueryClient.unsubscribe(subscription)); - }); - }, - _clearCachedDefaultClient: function () { - defaultLiveQueryClient = null; - } -}; - -_CoreManager2.default.setLiveQueryController(DefaultLiveQueryController); \ No newline at end of file diff --git a/lib/browser/ParseObject.js b/lib/browser/ParseObject.js deleted file mode 100644 index f08795e52..000000000 --- a/lib/browser/ParseObject.js +++ /dev/null @@ -1,2001 +0,0 @@ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _defineProperty = require('babel-runtime/core-js/object/define-property'); - -var _defineProperty2 = _interopRequireDefault(_defineProperty); - -var _create = require('babel-runtime/core-js/object/create'); - -var _create2 = _interopRequireDefault(_create); - -var _freeze = require('babel-runtime/core-js/object/freeze'); - -var _freeze2 = _interopRequireDefault(_freeze); - -var _stringify = require('babel-runtime/core-js/json/stringify'); - -var _stringify2 = _interopRequireDefault(_stringify); - -var _keys = require('babel-runtime/core-js/object/keys'); - -var _keys2 = _interopRequireDefault(_keys); - -var _typeof2 = require('babel-runtime/helpers/typeof'); - -var _typeof3 = _interopRequireDefault(_typeof2); - -var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); - -var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); - -var _createClass2 = require('babel-runtime/helpers/createClass'); - -var _createClass3 = _interopRequireDefault(_createClass2); - -var _CoreManager = require('./CoreManager'); - -var _CoreManager2 = _interopRequireDefault(_CoreManager); - -var _canBeSerialized = require('./canBeSerialized'); - -var _canBeSerialized2 = _interopRequireDefault(_canBeSerialized); - -var _decode = require('./decode'); - -var _decode2 = _interopRequireDefault(_decode); - -var _encode = require('./encode'); - -var _encode2 = _interopRequireDefault(_encode); - -var _equals = require('./equals'); - -var _equals2 = _interopRequireDefault(_equals); - -var _escape2 = require('./escape'); - -var _escape3 = _interopRequireDefault(_escape2); - -var _ParseACL = require('./ParseACL'); - -var _ParseACL2 = _interopRequireDefault(_ParseACL); - -var _parseDate = require('./parseDate'); - -var _parseDate2 = _interopRequireDefault(_parseDate); - -var _ParseError = require('./ParseError'); - -var _ParseError2 = _interopRequireDefault(_ParseError); - -var _ParseFile = require('./ParseFile'); - -var _ParseFile2 = _interopRequireDefault(_ParseFile); - -var _ParseOp = require('./ParseOp'); - -var _ParsePromise = require('./ParsePromise'); - -var _ParsePromise2 = _interopRequireDefault(_ParsePromise); - -var _ParseQuery = require('./ParseQuery'); - -var _ParseQuery2 = _interopRequireDefault(_ParseQuery); - -var _ParseRelation = require('./ParseRelation'); - -var _ParseRelation2 = _interopRequireDefault(_ParseRelation); - -var _SingleInstanceStateController = require('./SingleInstanceStateController'); - -var SingleInstanceStateController = _interopRequireWildcard(_SingleInstanceStateController); - -var _unique = require('./unique'); - -var _unique2 = _interopRequireDefault(_unique); - -var _UniqueInstanceStateController = require('./UniqueInstanceStateController'); - -var UniqueInstanceStateController = _interopRequireWildcard(_UniqueInstanceStateController); - -var _unsavedChildren = require('./unsavedChildren'); - -var _unsavedChildren2 = _interopRequireDefault(_unsavedChildren); - -function _interopRequireWildcard(obj) { - if (obj && obj.__esModule) { - return obj; - } else { - var newObj = {};if (obj != null) { - for (var key in obj) { - if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; - } - }newObj.default = obj;return newObj; - } -} - -function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { default: obj }; -} - -// Mapping of class names to constructors, so we can populate objects from the -// server with appropriate subclasses of ParseObject -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * - */ - -var classMap = {}; - -// Global counter for generating unique local Ids -var localCount = 0; -// Global counter for generating unique Ids for non-single-instance objects -var objectCount = 0; -// On web clients, objects are single-instance: any two objects with the same Id -// will have the same attributes. However, this may be dangerous default -// behavior in a server scenario -var singleInstance = !_CoreManager2.default.get('IS_NODE'); -if (singleInstance) { - _CoreManager2.default.setObjectStateController(SingleInstanceStateController); -} else { - _CoreManager2.default.setObjectStateController(UniqueInstanceStateController); -} - -function getServerUrlPath() { - var serverUrl = _CoreManager2.default.get('SERVER_URL'); - if (serverUrl[serverUrl.length - 1] !== '/') { - serverUrl += '/'; - } - var url = serverUrl.replace(/https?:\/\//, ''); - return url.substr(url.indexOf('/')); -} - -/** - * Creates a new model with defined attributes. - * - *
You won't normally call this method directly. It is recommended that
- * you use a subclass of Parse.Object
instead, created by calling
- * extend
.
However, if you don't want to use a subclass, or aren't sure which - * subclass is appropriate, you can use this form:
- * var object = new Parse.Object("ClassName"); - *- * That is basically equivalent to:
- * var MyClass = Parse.Object.extend("ClassName"); - * var object = new MyClass(); - *- * - * @class Parse.Object - * @constructor - * @param {String} className The class name for the object - * @param {Object} attributes The initial set of data to store in the object. - * @param {Object} options The options for this object instance. - */ - -var ParseObject = function () { - /** - * The ID of this object, unique within its class. - * @property id - * @type String - */ - function ParseObject(className, attributes, options) { - (0, _classCallCheck3.default)(this, ParseObject); - - // Enable legacy initializers - if (typeof this.initialize === 'function') { - this.initialize.apply(this, arguments); - } - - var toSet = null; - this._objCount = objectCount++; - if (typeof className === 'string') { - this.className = className; - if (attributes && (typeof attributes === 'undefined' ? 'undefined' : (0, _typeof3.default)(attributes)) === 'object') { - toSet = attributes; - } - } else if (className && (typeof className === 'undefined' ? 'undefined' : (0, _typeof3.default)(className)) === 'object') { - this.className = className.className; - toSet = {}; - for (var attr in className) { - if (attr !== 'className') { - toSet[attr] = className[attr]; - } - } - if (attributes && (typeof attributes === 'undefined' ? 'undefined' : (0, _typeof3.default)(attributes)) === 'object') { - options = attributes; - } - } - if (toSet && !this.set(toSet, options)) { - throw new Error('Can\'t create an invalid Parse Object'); - } - } - - /** Prototype getters / setters **/ - - (0, _createClass3.default)(ParseObject, [{ - key: '_getId', - - /** Private methods **/ - - /** - * Returns a local or server Id used uniquely identify this object - */ - value: function () { - if (typeof this.id === 'string') { - return this.id; - } - if (typeof this._localId === 'string') { - return this._localId; - } - var localId = 'local' + String(localCount++); - this._localId = localId; - return localId; - } - - /** - * Returns a unique identifier used to pull data from the State Controller. - */ - - }, { - key: '_getStateIdentifier', - value: function () { - if (singleInstance) { - var _id = this.id; - if (!_id) { - _id = this._getId(); - } - return { - id: _id, - className: this.className - }; - } else { - return this; - } - } - }, { - key: '_getServerData', - value: function () { - var stateController = _CoreManager2.default.getObjectStateController(); - return stateController.getServerData(this._getStateIdentifier()); - } - }, { - key: '_clearServerData', - value: function () { - var serverData = this._getServerData(); - var unset = {}; - for (var attr in serverData) { - unset[attr] = undefined; - } - var stateController = _CoreManager2.default.getObjectStateController(); - stateController.setServerData(this._getStateIdentifier(), unset); - } - }, { - key: '_getPendingOps', - value: function () { - var stateController = _CoreManager2.default.getObjectStateController(); - return stateController.getPendingOps(this._getStateIdentifier()); - } - }, { - key: '_clearPendingOps', - value: function () { - var pending = this._getPendingOps(); - var latest = pending[pending.length - 1]; - var keys = (0, _keys2.default)(latest); - keys.forEach(function (key) { - delete latest[key]; - }); - } - }, { - key: '_getDirtyObjectAttributes', - value: function () { - var attributes = this.attributes; - var stateController = _CoreManager2.default.getObjectStateController(); - var objectCache = stateController.getObjectCache(this._getStateIdentifier()); - var dirty = {}; - for (var attr in attributes) { - var val = attributes[attr]; - if (val && (typeof val === 'undefined' ? 'undefined' : (0, _typeof3.default)(val)) === 'object' && !(val instanceof ParseObject) && !(val instanceof _ParseFile2.default) && !(val instanceof _ParseRelation2.default)) { - // Due to the way browsers construct maps, the key order will not change - // unless the object is changed - try { - var json = (0, _encode2.default)(val, false, true); - var stringified = (0, _stringify2.default)(json); - if (objectCache[attr] !== stringified) { - dirty[attr] = val; - } - } catch (e) { - // Error occurred, possibly by a nested unsaved pointer in a mutable container - // No matter how it happened, it indicates a change in the attribute - dirty[attr] = val; - } - } - } - return dirty; - } - }, { - key: '_toFullJSON', - value: function (seen) { - var json = this.toJSON(seen); - json.__type = 'Object'; - json.className = this.className; - return json; - } - }, { - key: '_getSaveJSON', - value: function () { - var pending = this._getPendingOps(); - var dirtyObjects = this._getDirtyObjectAttributes(); - var json = {}; - - for (var attr in dirtyObjects) { - json[attr] = new _ParseOp.SetOp(dirtyObjects[attr]).toJSON(); - } - for (attr in pending[0]) { - json[attr] = pending[0][attr].toJSON(); - } - return json; - } - }, { - key: '_getSaveParams', - value: function () { - var method = this.id ? 'PUT' : 'POST'; - var body = this._getSaveJSON(); - var path = 'classes/' + this.className; - if (this.id) { - path += '/' + this.id; - } else if (this.className === '_User') { - path = 'users'; - } - return { - method: method, - body: body, - path: path - }; - } - }, { - key: '_finishFetch', - value: function (serverData) { - if (!this.id && serverData.objectId) { - this.id = serverData.objectId; - } - var stateController = _CoreManager2.default.getObjectStateController(); - stateController.initializeState(this._getStateIdentifier()); - var decoded = {}; - for (var attr in serverData) { - if (attr === 'ACL') { - decoded[attr] = new _ParseACL2.default(serverData[attr]); - } else if (attr !== 'objectId') { - decoded[attr] = (0, _decode2.default)(serverData[attr]); - if (decoded[attr] instanceof _ParseRelation2.default) { - decoded[attr]._ensureParentAndKey(this, attr); - } - } - } - if (decoded.createdAt && typeof decoded.createdAt === 'string') { - decoded.createdAt = (0, _parseDate2.default)(decoded.createdAt); - } - if (decoded.updatedAt && typeof decoded.updatedAt === 'string') { - decoded.updatedAt = (0, _parseDate2.default)(decoded.updatedAt); - } - if (!decoded.updatedAt && decoded.createdAt) { - decoded.updatedAt = decoded.createdAt; - } - stateController.commitServerChanges(this._getStateIdentifier(), decoded); - } - }, { - key: '_setExisted', - value: function (existed) { - var stateController = _CoreManager2.default.getObjectStateController(); - var state = stateController.getState(this._getStateIdentifier()); - if (state) { - state.existed = existed; - } - } - }, { - key: '_migrateId', - value: function (serverId) { - if (this._localId && serverId) { - if (singleInstance) { - var stateController = _CoreManager2.default.getObjectStateController(); - var oldState = stateController.removeState(this._getStateIdentifier()); - this.id = serverId; - delete this._localId; - if (oldState) { - stateController.initializeState(this._getStateIdentifier(), oldState); - } - } else { - this.id = serverId; - delete this._localId; - } - } - } - }, { - key: '_handleSaveResponse', - value: function (response, status) { - var changes = {}; - - var stateController = _CoreManager2.default.getObjectStateController(); - var pending = stateController.popPendingState(this._getStateIdentifier()); - for (var attr in pending) { - if (pending[attr] instanceof _ParseOp.RelationOp) { - changes[attr] = pending[attr].applyTo(undefined, this, attr); - } else if (!(attr in response)) { - // Only SetOps and UnsetOps should not come back with results - changes[attr] = pending[attr].applyTo(undefined); - } - } - for (attr in response) { - if ((attr === 'createdAt' || attr === 'updatedAt') && typeof response[attr] === 'string') { - changes[attr] = (0, _parseDate2.default)(response[attr]); - } else if (attr === 'ACL') { - changes[attr] = new _ParseACL2.default(response[attr]); - } else if (attr !== 'objectId') { - changes[attr] = (0, _decode2.default)(response[attr]); - if (changes[attr] instanceof _ParseOp.UnsetOp) { - changes[attr] = undefined; - } - } - } - if (changes.createdAt && !changes.updatedAt) { - changes.updatedAt = changes.createdAt; - } - - this._migrateId(response.objectId); - - if (status !== 201) { - this._setExisted(true); - } - - stateController.commitServerChanges(this._getStateIdentifier(), changes); - } - }, { - key: '_handleSaveError', - value: function () { - this._getPendingOps(); - - var stateController = _CoreManager2.default.getObjectStateController(); - stateController.mergeFirstPendingState(this._getStateIdentifier()); - } - - /** Public methods **/ - - }, { - key: 'initialize', - value: function () {} - // NOOP - - - /** - * Returns a JSON version of the object suitable for saving to Parse. - * @method toJSON - * @return {Object} - */ - - }, { - key: 'toJSON', - value: function (seen) { - var seenEntry = this.id ? this.className + ':' + this.id : this; - var seen = seen || [seenEntry]; - var json = {}; - var attrs = this.attributes; - for (var attr in attrs) { - if ((attr === 'createdAt' || attr === 'updatedAt') && attrs[attr].toJSON) { - json[attr] = attrs[attr].toJSON(); - } else { - json[attr] = (0, _encode2.default)(attrs[attr], false, false, seen); - } - } - var pending = this._getPendingOps(); - for (var attr in pending[0]) { - json[attr] = pending[0][attr].toJSON(); - } - - if (this.id) { - json.objectId = this.id; - } - return json; - } - - /** - * Determines whether this ParseObject is equal to another ParseObject - * @method equals - * @return {Boolean} - */ - - }, { - key: 'equals', - value: function (other) { - if (this === other) { - return true; - } - return other instanceof ParseObject && this.className === other.className && this.id === other.id && typeof this.id !== 'undefined'; - } - - /** - * Returns true if this object has been modified since its last - * save/refresh. If an attribute is specified, it returns true only if that - * particular attribute has been modified since the last save/refresh. - * @method dirty - * @param {String} attr An attribute name (optional). - * @return {Boolean} - */ - - }, { - key: 'dirty', - value: function (attr) { - if (!this.id) { - return true; - } - var pendingOps = this._getPendingOps(); - var dirtyObjects = this._getDirtyObjectAttributes(); - if (attr) { - if (dirtyObjects.hasOwnProperty(attr)) { - return true; - } - for (var i = 0; i < pendingOps.length; i++) { - if (pendingOps[i].hasOwnProperty(attr)) { - return true; - } - } - return false; - } - if ((0, _keys2.default)(pendingOps[0]).length !== 0) { - return true; - } - if ((0, _keys2.default)(dirtyObjects).length !== 0) { - return true; - } - return false; - } - - /** - * Returns an array of keys that have been modified since last save/refresh - * @method dirtyKeys - * @return {Array of string} - */ - - }, { - key: 'dirtyKeys', - value: function () { - var pendingOps = this._getPendingOps(); - var keys = {}; - for (var i = 0; i < pendingOps.length; i++) { - for (var attr in pendingOps[i]) { - keys[attr] = true; - } - } - var dirtyObjects = this._getDirtyObjectAttributes(); - for (var attr in dirtyObjects) { - keys[attr] = true; - } - return (0, _keys2.default)(keys); - } - - /** - * Gets a Pointer referencing this Object. - * @method toPointer - * @return {Object} - */ - - }, { - key: 'toPointer', - value: function () { - if (!this.id) { - throw new Error('Cannot create a pointer to an unsaved ParseObject'); - } - return { - __type: 'Pointer', - className: this.className, - objectId: this.id - }; - } - - /** - * Gets the value of an attribute. - * @method get - * @param {String} attr The string name of an attribute. - */ - - }, { - key: 'get', - value: function (attr) { - return this.attributes[attr]; - } - - /** - * Gets a relation on the given class for the attribute. - * @method relation - * @param String attr The attribute to get the relation for. - */ - - }, { - key: 'relation', - value: function (attr) { - var value = this.get(attr); - if (value) { - if (!(value instanceof _ParseRelation2.default)) { - throw new Error('Called relation() on non-relation field ' + attr); - } - value._ensureParentAndKey(this, attr); - return value; - } - return new _ParseRelation2.default(this, attr); - } - - /** - * Gets the HTML-escaped value of an attribute. - * @method escape - * @param {String} attr The string name of an attribute. - */ - - }, { - key: 'escape', - value: function (attr) { - var val = this.attributes[attr]; - if (val == null) { - return ''; - } - - if (typeof val !== 'string') { - if (typeof val.toString !== 'function') { - return ''; - } - val = val.toString(); - } - return (0, _escape3.default)(val); - } - - /** - * Returns
true
if the attribute contains a value that is not
- * null or undefined.
- * @method has
- * @param {String} attr The string name of the attribute.
- * @return {Boolean}
- */
-
- }, {
- key: 'has',
- value: function (attr) {
- var attributes = this.attributes;
- if (attributes.hasOwnProperty(attr)) {
- return attributes[attr] != null;
- }
- return false;
- }
-
- /**
- * Sets a hash of model attributes on the object.
- *
- * You can call it with an object containing keys and values, or with one - * key and value. For example:
- * gameTurn.set({ - * player: player1, - * diceRoll: 2 - * }, { - * error: function(gameTurnAgain, error) { - * // The set failed validation. - * } - * }); - * - * game.set("currentPlayer", player2, { - * error: function(gameTurnAgain, error) { - * // The set failed validation. - * } - * }); - * - * game.set("finished", true);- * - * @method set - * @param {String} key The key to set. - * @param {} value The value to give it. - * @param {Object} options A set of options for the set. - * The only supported option is
error
.
- * @return {Boolean} true if the set succeeded.
- */
-
- }, {
- key: 'set',
- value: function (key, value, options) {
- var changes = {};
- var newOps = {};
- if (key && (typeof key === 'undefined' ? 'undefined' : (0, _typeof3.default)(key)) === 'object') {
- changes = key;
- options = value;
- } else if (typeof key === 'string') {
- changes[key] = value;
- } else {
- return this;
- }
-
- options = options || {};
- var readonly = [];
- if (typeof this.constructor.readOnlyAttributes === 'function') {
- readonly = readonly.concat(this.constructor.readOnlyAttributes());
- }
- for (var k in changes) {
- if (k === 'createdAt' || k === 'updatedAt') {
- // This property is read-only, but for legacy reasons we silently
- // ignore it
- continue;
- }
- if (readonly.indexOf(k) > -1) {
- throw new Error('Cannot modify readonly attribute: ' + k);
- }
- if (options.unset) {
- newOps[k] = new _ParseOp.UnsetOp();
- } else if (changes[k] instanceof _ParseOp.Op) {
- newOps[k] = changes[k];
- } else if (changes[k] && (0, _typeof3.default)(changes[k]) === 'object' && typeof changes[k].__op === 'string') {
- newOps[k] = (0, _ParseOp.opFromJSON)(changes[k]);
- } else if (k === 'objectId' || k === 'id') {
- if (typeof changes[k] === 'string') {
- this.id = changes[k];
- }
- } else if (k === 'ACL' && (0, _typeof3.default)(changes[k]) === 'object' && !(changes[k] instanceof _ParseACL2.default)) {
- newOps[k] = new _ParseOp.SetOp(new _ParseACL2.default(changes[k]));
- } else {
- newOps[k] = new _ParseOp.SetOp(changes[k]);
- }
- }
-
- // Calculate new values
- var currentAttributes = this.attributes;
- var newValues = {};
- for (var attr in newOps) {
- if (newOps[attr] instanceof _ParseOp.RelationOp) {
- newValues[attr] = newOps[attr].applyTo(currentAttributes[attr], this, attr);
- } else if (!(newOps[attr] instanceof _ParseOp.UnsetOp)) {
- newValues[attr] = newOps[attr].applyTo(currentAttributes[attr]);
- }
- }
-
- // Validate changes
- if (!options.ignoreValidation) {
- var validation = this.validate(newValues);
- if (validation) {
- if (typeof options.error === 'function') {
- options.error(this, validation);
- }
- return false;
- }
- }
-
- // Consolidate Ops
- var pendingOps = this._getPendingOps();
- var last = pendingOps.length - 1;
- var stateController = _CoreManager2.default.getObjectStateController();
- for (var attr in newOps) {
- var nextOp = newOps[attr].mergeWith(pendingOps[last][attr]);
- stateController.setPendingOp(this._getStateIdentifier(), attr, nextOp);
- }
-
- return this;
- }
-
- /**
- * Remove an attribute from the model. This is a noop if the attribute doesn't
- * exist.
- * @method unset
- * @param {String} attr The string name of an attribute.
- */
-
- }, {
- key: 'unset',
- value: function (attr, options) {
- options = options || {};
- options.unset = true;
- return this.set(attr, null, options);
- }
-
- /**
- * Atomically increments the value of the given attribute the next time the
- * object is saved. If no amount is specified, 1 is used by default.
- *
- * @method increment
- * @param attr {String} The key.
- * @param amount {Number} The amount to increment by (optional).
- */
-
- }, {
- key: 'increment',
- value: function (attr, amount) {
- if (typeof amount === 'undefined') {
- amount = 1;
- }
- if (typeof amount !== 'number') {
- throw new Error('Cannot increment by a non-numeric amount.');
- }
- return this.set(attr, new _ParseOp.IncrementOp(amount));
- }
-
- /**
- * Atomically add an object to the end of the array associated with a given
- * key.
- * @method add
- * @param attr {String} The key.
- * @param item {} The item to add.
- */
-
- }, {
- key: 'add',
- value: function (attr, item) {
- return this.set(attr, new _ParseOp.AddOp([item]));
- }
-
- /**
- * Atomically add an object to the array associated with a given key, only
- * if it is not already present in the array. The position of the insert is
- * not guaranteed.
- *
- * @method addUnique
- * @param attr {String} The key.
- * @param item {} The object to add.
- */
-
- }, {
- key: 'addUnique',
- value: function (attr, item) {
- return this.set(attr, new _ParseOp.AddUniqueOp([item]));
- }
-
- /**
- * Atomically remove all instances of an object from the array associated
- * with a given key.
- *
- * @method remove
- * @param attr {String} The key.
- * @param item {} The object to remove.
- */
-
- }, {
- key: 'remove',
- value: function (attr, item) {
- return this.set(attr, new _ParseOp.RemoveOp([item]));
- }
-
- /**
- * Returns an instance of a subclass of Parse.Op describing what kind of
- * modification has been performed on this field since the last time it was
- * saved. For example, after calling object.increment("x"), calling
- * object.op("x") would return an instance of Parse.Op.Increment.
- *
- * @method op
- * @param attr {String} The key.
- * @returns {Parse.Op} The operation, or undefined if none.
- */
-
- }, {
- key: 'op',
- value: function (attr) {
- var pending = this._getPendingOps();
- for (var i = pending.length; i--;) {
- if (pending[i][attr]) {
- return pending[i][attr];
- }
- }
- }
-
- /**
- * Creates a new model with identical attributes to this one, similar to Backbone.Model's clone()
- * @method clone
- * @return {Parse.Object}
- */
-
- }, {
- key: 'clone',
- value: function () {
- var clone = new this.constructor();
- if (!clone.className) {
- clone.className = this.className;
- }
- var attributes = this.attributes;
- if (typeof this.constructor.readOnlyAttributes === 'function') {
- var readonly = this.constructor.readOnlyAttributes() || [];
- // Attributes are frozen, so we have to rebuild an object,
- // rather than delete readonly keys
- var copy = {};
- for (var a in attributes) {
- if (readonly.indexOf(a) < 0) {
- copy[a] = attributes[a];
- }
- }
- attributes = copy;
- }
- if (clone.set) {
- clone.set(attributes);
- }
- return clone;
- }
-
- /**
- * Creates a new instance of this object. Not to be confused with clone()
- * @method newInstance
- * @return {Parse.Object}
- */
-
- }, {
- key: 'newInstance',
- value: function () {
- var clone = new this.constructor();
- if (!clone.className) {
- clone.className = this.className;
- }
- clone.id = this.id;
- if (singleInstance) {
- // Just return an object with the right id
- return clone;
- }
-
- var stateController = _CoreManager2.default.getObjectStateController();
- if (stateController) {
- stateController.duplicateState(this._getStateIdentifier(), clone._getStateIdentifier());
- }
- return clone;
- }
-
- /**
- * Returns true if this object has never been saved to Parse.
- * @method isNew
- * @return {Boolean}
- */
-
- }, {
- key: 'isNew',
- value: function () {
- return !this.id;
- }
-
- /**
- * Returns true if this object was created by the Parse server when the
- * object might have already been there (e.g. in the case of a Facebook
- * login)
- * @method existed
- * @return {Boolean}
- */
-
- }, {
- key: 'existed',
- value: function () {
- if (!this.id) {
- return false;
- }
- var stateController = _CoreManager2.default.getObjectStateController();
- var state = stateController.getState(this._getStateIdentifier());
- if (state) {
- return state.existed;
- }
- return false;
- }
-
- /**
- * Checks if the model is currently in a valid state.
- * @method isValid
- * @return {Boolean}
- */
-
- }, {
- key: 'isValid',
- value: function () {
- return !this.validate(this.attributes);
- }
-
- /**
- * You should not call this function directly unless you subclass
- * Parse.Object
, in which case you can override this method
- * to provide additional validation on set
and
- * save
. Your implementation should return
- *
- * @method validate
- * @param {Object} attrs The current data to validate.
- * @return {} False if the data is valid. An error object otherwise.
- * @see Parse.Object#set
- */
-
- }, {
- key: 'validate',
- value: function (attrs) {
- if (attrs.hasOwnProperty('ACL') && !(attrs.ACL instanceof _ParseACL2.default)) {
- return new _ParseError2.default(_ParseError2.default.OTHER_CAUSE, 'ACL must be a Parse ACL.');
- }
- for (var key in attrs) {
- if (!/^[A-Za-z][0-9A-Za-z_]*$/.test(key)) {
- return new _ParseError2.default(_ParseError2.default.INVALID_KEY_NAME);
- }
- }
- return false;
- }
-
- /**
- * Returns the ACL for this object.
- * @method getACL
- * @returns {Parse.ACL} An instance of Parse.ACL.
- * @see Parse.Object#get
- */
-
- }, {
- key: 'getACL',
- value: function () {
- var acl = this.get('ACL');
- if (acl instanceof _ParseACL2.default) {
- return acl;
- }
- return null;
- }
-
- /**
- * Sets the ACL to be used for this object.
- * @method setACL
- * @param {Parse.ACL} acl An instance of Parse.ACL.
- * @param {Object} options Optional Backbone-like options object to be
- * passed in to set.
- * @return {Boolean} Whether the set passed validation.
- * @see Parse.Object#set
- */
-
- }, {
- key: 'setACL',
- value: function (acl, options) {
- return this.set('ACL', acl, options);
- }
-
- /**
- * Clears any changes to this object made since the last call to save()
- * @method revert
- */
-
- }, {
- key: 'revert',
- value: function () {
- this._clearPendingOps();
- }
-
- /**
- * Clears all attributes on a model
- * @method clear
- */
-
- }, {
- key: 'clear',
- value: function () {
- var attributes = this.attributes;
- var erasable = {};
- var readonly = ['createdAt', 'updatedAt'];
- if (typeof this.constructor.readOnlyAttributes === 'function') {
- readonly = readonly.concat(this.constructor.readOnlyAttributes());
- }
- for (var attr in attributes) {
- if (readonly.indexOf(attr) < 0) {
- erasable[attr] = true;
- }
- }
- return this.set(erasable, { unset: true });
- }
-
- /**
- * Fetch the model from the server. If the server's representation of the
- * model differs from its current attributes, they will be overriden.
- *
- * @method fetch
- * @param {Object} options A Backbone-style callback object.
- * Valid options are:- * object.save();- * or
- * object.save(null, options);- * or
- * object.save(attrs, options);- * or
- * object.save(key, value, options);- * - * For example,
- * gameTurn.save({ - * player: "Jake Cutter", - * diceRoll: 2 - * }, { - * success: function(gameTurnAgain) { - * // The save was successful. - * }, - * error: function(gameTurnAgain, error) { - * // The save failed. Error is an instance of Parse.Error. - * } - * });- * or with promises:
- * gameTurn.save({ - * player: "Jake Cutter", - * diceRoll: 2 - * }).then(function(gameTurnAgain) { - * // The save was successful. - * }, function(error) { - * // The save failed. Error is an instance of Parse.Error. - * });- * - * @method save - * @param {Object} options A Backbone-style callback object. - * Valid options are:
- * Parse.Object.fetchAll([object1, object2, ...], { - * success: function(list) { - * // All the objects were fetched. - * }, - * error: function(error) { - * // An error occurred while fetching one of the objects. - * }, - * }); - *- * - * @method fetchAll - * @param {Array} list A list of
Parse.Object
.
- * @param {Object} options A Backbone-style callback object.
- * @static
- * Valid options are:- * Parse.Object.fetchAllIfNeeded([object1, ...], { - * success: function(list) { - * // Objects were fetched and updated. - * }, - * error: function(error) { - * // An error occurred while fetching one of the objects. - * }, - * }); - *- * - * @method fetchAllIfNeeded - * @param {Array} list A list of
Parse.Object
.
- * @param {Object} options A Backbone-style callback object.
- * @static
- * Valid options are:Unlike saveAll, if an error occurs while deleting an individual model, - * this method will continue trying to delete the rest of the models if - * possible, except in the case of a fatal error like a connection error. - * - *
In particular, the Parse.Error object returned in the case of error may - * be one of two types: - * - *
- * Parse.Object.destroyAll([object1, object2, ...], { - * success: function() { - * // All the objects were deleted. - * }, - * error: function(error) { - * // An error occurred while deleting one or more of the objects. - * // If this is an aggregate error, then we can inspect each error - * // object individually to determine the reason why a particular - * // object was not deleted. - * if (error.code === Parse.Error.AGGREGATE_ERROR) { - * for (var i = 0; i < error.errors.length; i++) { - * console.log("Couldn't delete " + error.errors[i].object.id + - * "due to " + error.errors[i].message); - * } - * } else { - * console.log("Delete aborted because of " + error.message); - * } - * }, - * }); - *- * - * @method destroyAll - * @param {Array} list A list of
Parse.Object
.
- * @param {Object} options A Backbone-style callback object.
- * @static
- * Valid options are:- * Parse.Object.saveAll([object1, object2, ...], { - * success: function(list) { - * // All the objects were saved. - * }, - * error: function(error) { - * // An error occurred while saving one of the objects. - * }, - * }); - *- * - * @method saveAll - * @param {Array} list A list of
Parse.Object
.
- * @param {Object} options A Backbone-style callback object.
- * @static
- * Valid options are:A shortcut for:
- * var Foo = Parse.Object.extend("Foo"); - * var pointerToFoo = new Foo(); - * pointerToFoo.id = "myObjectId"; - *- * - * @method createWithoutData - * @param {String} id The ID of the object to create a reference to. - * @static - * @return {Parse.Object} A Parse.Object reference. - */ - - }, { - key: 'createWithoutData', - value: function (id) { - var obj = new this(); - obj.id = id; - return obj; - } - - /** - * Creates a new instance of a Parse Object from a JSON representation. - * @method fromJSON - * @param {Object} json The JSON map of the Object's data - * @param {boolean} override In single instance mode, all old server data - * is overwritten if this is set to true - * @static - * @return {Parse.Object} A Parse.Object reference - */ - - }, { - key: 'fromJSON', - value: function (json, override) { - if (!json.className) { - throw new Error('Cannot create an object without a className'); - } - var constructor = classMap[json.className]; - var o = constructor ? new constructor() : new ParseObject(json.className); - var otherAttributes = {}; - for (var attr in json) { - if (attr !== 'className' && attr !== '__type') { - otherAttributes[attr] = json[attr]; - } - } - if (override) { - // id needs to be set before clearServerData can work - if (otherAttributes.objectId) { - o.id = otherAttributes.objectId; - } - var preserved = null; - if (typeof o._preserveFieldsOnFetch === 'function') { - preserved = o._preserveFieldsOnFetch(); - } - o._clearServerData(); - if (preserved) { - o._finishFetch(preserved); - } - } - o._finishFetch(otherAttributes); - if (json.objectId) { - o._setExisted(true); - } - return o; - } - - /** - * Registers a subclass of Parse.Object with a specific class name. - * When objects of that class are retrieved from a query, they will be - * instantiated with this subclass. - * This is only necessary when using ES6 subclassing. - * @method registerSubclass - * @param {String} className The class name of the subclass - * @param {Class} constructor The subclass - */ - - }, { - key: 'registerSubclass', - value: function (className, constructor) { - if (typeof className !== 'string') { - throw new TypeError('The first argument must be a valid class name.'); - } - if (typeof constructor === 'undefined') { - throw new TypeError('You must supply a subclass constructor.'); - } - if (typeof constructor !== 'function') { - throw new TypeError('You must register the subclass constructor. ' + 'Did you attempt to register an instance of the subclass?'); - } - classMap[className] = constructor; - if (!constructor.className) { - constructor.className = className; - } - } - - /** - * Creates a new subclass of Parse.Object for the given Parse class name. - * - *
Every extension of a Parse class will inherit from the most recent - * previous extension of that class. When a Parse.Object is automatically - * created by parsing JSON, it will use the most recent extension of that - * class.
- * - *You should call either:
- * var MyClass = Parse.Object.extend("MyClass", { - * Instance methods, - * initialize: function(attrs, options) { - * this.someInstanceProperty = [], - * Other instance properties - * } - * }, { - * Class properties - * });- * or, for Backbone compatibility:
- * var MyClass = Parse.Object.extend({ - * className: "MyClass", - * Instance methods, - * initialize: function(attrs, options) { - * this.someInstanceProperty = [], - * Other instance properties - * } - * }, { - * Class properties - * });- * - * @method extend - * @param {String} className The name of the Parse class backing this model. - * @param {Object} protoProps Instance properties to add to instances of the - * class returned from this method. - * @param {Object} classProps Class properties to add the class returned from - * this method. - * @return {Class} A new subclass of Parse.Object. - */ - - }, { - key: 'extend', - value: function (className, protoProps, classProps) { - if (typeof className !== 'string') { - if (className && typeof className.className === 'string') { - return ParseObject.extend(className.className, className, protoProps); - } else { - throw new Error('Parse.Object.extend\'s first argument should be the className.'); - } - } - var adjustedClassName = className; - - if (adjustedClassName === 'User' && _CoreManager2.default.get('PERFORM_USER_REWRITE')) { - adjustedClassName = '_User'; - } - - var parentProto = ParseObject.prototype; - if (this.hasOwnProperty('__super__') && this.__super__) { - parentProto = this.prototype; - } else if (classMap[adjustedClassName]) { - parentProto = classMap[adjustedClassName].prototype; - } - var ParseObjectSubclass = function (attributes, options) { - this.className = adjustedClassName; - this._objCount = objectCount++; - // Enable legacy initializers - if (typeof this.initialize === 'function') { - this.initialize.apply(this, arguments); - } - - if (attributes && (typeof attributes === 'undefined' ? 'undefined' : (0, _typeof3.default)(attributes)) === 'object') { - if (!this.set(attributes || {}, options)) { - throw new Error('Can\'t create an invalid Parse Object'); - } - } - }; - ParseObjectSubclass.className = adjustedClassName; - ParseObjectSubclass.__super__ = parentProto; - - ParseObjectSubclass.prototype = (0, _create2.default)(parentProto, { - constructor: { - value: ParseObjectSubclass, - enumerable: false, - writable: true, - configurable: true - } - }); - - if (protoProps) { - for (var prop in protoProps) { - if (prop !== 'className') { - (0, _defineProperty2.default)(ParseObjectSubclass.prototype, prop, { - value: protoProps[prop], - enumerable: false, - writable: true, - configurable: true - }); - } - } - } - - if (classProps) { - for (var prop in classProps) { - if (prop !== 'className') { - (0, _defineProperty2.default)(ParseObjectSubclass, prop, { - value: classProps[prop], - enumerable: false, - writable: true, - configurable: true - }); - } - } - } - - ParseObjectSubclass.extend = function (name, protoProps, classProps) { - if (typeof name === 'string') { - return ParseObject.extend.call(ParseObjectSubclass, name, protoProps, classProps); - } - return ParseObject.extend.call(ParseObjectSubclass, adjustedClassName, name, protoProps); - }; - ParseObjectSubclass.createWithoutData = ParseObject.createWithoutData; - - classMap[adjustedClassName] = ParseObjectSubclass; - return ParseObjectSubclass; - } - - /** - * Enable single instance objects, where any local objects with the same Id - * share the same attributes, and stay synchronized with each other. - * This is disabled by default in server environments, since it can lead to - * security issues. - * @method enableSingleInstance - */ - - }, { - key: 'enableSingleInstance', - value: function () { - singleInstance = true; - _CoreManager2.default.setObjectStateController(SingleInstanceStateController); - } - - /** - * Disable single instance objects, where any local objects with the same Id - * share the same attributes, and stay synchronized with each other. - * When disabled, you can have two instances of the same object in memory - * without them sharing attributes. - * @method disableSingleInstance - */ - - }, { - key: 'disableSingleInstance', - value: function () { - singleInstance = false; - _CoreManager2.default.setObjectStateController(UniqueInstanceStateController); - } - }]); - return ParseObject; -}(); - -exports.default = ParseObject; - -var DefaultController = { - fetch: function (target, forceFetch, options) { - if (Array.isArray(target)) { - if (target.length < 1) { - return _ParsePromise2.default.as([]); - } - var objs = []; - var ids = []; - var className = null; - var results = []; - var error = null; - target.forEach(function (el, i) { - if (error) { - return; - } - if (!className) { - className = el.className; - } - if (className !== el.className) { - error = new _ParseError2.default(_ParseError2.default.INVALID_CLASS_NAME, 'All objects should be of the same class'); - } - if (!el.id) { - error = new _ParseError2.default(_ParseError2.default.MISSING_OBJECT_ID, 'All objects must have an ID'); - } - if (forceFetch || (0, _keys2.default)(el._getServerData()).length === 0) { - ids.push(el.id); - objs.push(el); - } - results.push(el); - }); - if (error) { - return _ParsePromise2.default.error(error); - } - var query = new _ParseQuery2.default(className); - query.containedIn('objectId', ids); - query._limit = ids.length; - return query.find(options).then(function (objects) { - var idMap = {}; - objects.forEach(function (o) { - idMap[o.id] = o; - }); - for (var i = 0; i < objs.length; i++) { - var obj = objs[i]; - if (!obj || !obj.id || !idMap[obj.id]) { - if (forceFetch) { - return _ParsePromise2.default.error(new _ParseError2.default(_ParseError2.default.OBJECT_NOT_FOUND, 'All objects must exist on the server.')); - } - } - } - if (!singleInstance) { - // If single instance objects are disabled, we need to replace the - for (var i = 0; i < results.length; i++) { - var obj = results[i]; - if (obj && obj.id && idMap[obj.id]) { - var id = obj.id; - obj._finishFetch(idMap[id].toJSON()); - results[i] = idMap[id]; - } - } - } - return _ParsePromise2.default.as(results); - }); - } else { - var RESTController = _CoreManager2.default.getRESTController(); - return RESTController.request('GET', 'classes/' + target.className + '/' + target._getId(), {}, options).then(function (response, status, xhr) { - if (target instanceof ParseObject) { - target._clearPendingOps(); - target._clearServerData(); - target._finishFetch(response); - } - return target; - }); - } - }, - destroy: function (target, options) { - var RESTController = _CoreManager2.default.getRESTController(); - if (Array.isArray(target)) { - if (target.length < 1) { - return _ParsePromise2.default.as([]); - } - var batches = [[]]; - target.forEach(function (obj) { - if (!obj.id) { - return; - } - batches[batches.length - 1].push(obj); - if (batches[batches.length - 1].length >= 20) { - batches.push([]); - } - }); - if (batches[batches.length - 1].length === 0) { - // If the last batch is empty, remove it - batches.pop(); - } - var deleteCompleted = _ParsePromise2.default.as(); - var errors = []; - batches.forEach(function (batch) { - deleteCompleted = deleteCompleted.then(function () { - return RESTController.request('POST', 'batch', { - requests: batch.map(function (obj) { - return { - method: 'DELETE', - path: getServerUrlPath() + 'classes/' + obj.className + '/' + obj._getId(), - body: {} - }; - }) - }, options).then(function (results) { - for (var i = 0; i < results.length; i++) { - if (results[i] && results[i].hasOwnProperty('error')) { - var err = new _ParseError2.default(results[i].error.code, results[i].error.error); - err.object = batch[i]; - errors.push(err); - } - } - }); - }); - }); - return deleteCompleted.then(function () { - if (errors.length) { - var aggregate = new _ParseError2.default(_ParseError2.default.AGGREGATE_ERROR); - aggregate.errors = errors; - return _ParsePromise2.default.error(aggregate); - } - return _ParsePromise2.default.as(target); - }); - } else if (target instanceof ParseObject) { - return RESTController.request('DELETE', 'classes/' + target.className + '/' + target._getId(), {}, options).then(function () { - return _ParsePromise2.default.as(target); - }); - } - return _ParsePromise2.default.as(target); - }, - save: function (target, options) { - var RESTController = _CoreManager2.default.getRESTController(); - var stateController = _CoreManager2.default.getObjectStateController(); - if (Array.isArray(target)) { - if (target.length < 1) { - return _ParsePromise2.default.as([]); - } - - var unsaved = target.concat(); - for (var i = 0; i < target.length; i++) { - if (target[i] instanceof ParseObject) { - unsaved = unsaved.concat((0, _unsavedChildren2.default)(target[i], true)); - } - } - unsaved = (0, _unique2.default)(unsaved); - - var filesSaved = _ParsePromise2.default.as(); - var pending = []; - unsaved.forEach(function (el) { - if (el instanceof _ParseFile2.default) { - filesSaved = filesSaved.then(function () { - return el.save(); - }); - } else if (el instanceof ParseObject) { - pending.push(el); - } - }); - - return filesSaved.then(function () { - var objectError = null; - return _ParsePromise2.default._continueWhile(function () { - return pending.length > 0; - }, function () { - var batch = []; - var nextPending = []; - pending.forEach(function (el) { - if (batch.length < 20 && (0, _canBeSerialized2.default)(el)) { - batch.push(el); - } else { - nextPending.push(el); - } - }); - pending = nextPending; - if (batch.length < 1) { - return _ParsePromise2.default.error(new _ParseError2.default(_ParseError2.default.OTHER_CAUSE, 'Tried to save a batch with a cycle.')); - } - - // Queue up tasks for each object in the batch. - // When every task is ready, the API request will execute - var batchReturned = new _ParsePromise2.default(); - var batchReady = []; - var batchTasks = []; - batch.forEach(function (obj, index) { - var ready = new _ParsePromise2.default(); - batchReady.push(ready); - - stateController.pushPendingState(obj._getStateIdentifier()); - batchTasks.push(stateController.enqueueTask(obj._getStateIdentifier(), function () { - ready.resolve(); - return batchReturned.then(function (responses, status) { - if (responses[index].hasOwnProperty('success')) { - obj._handleSaveResponse(responses[index].success, status); - } else { - if (!objectError && responses[index].hasOwnProperty('error')) { - var serverError = responses[index].error; - objectError = new _ParseError2.default(serverError.code, serverError.error); - // Cancel the rest of the save - pending = []; - } - obj._handleSaveError(); - } - }); - })); - }); - - _ParsePromise2.default.when(batchReady).then(function () { - // Kick off the batch request - return RESTController.request('POST', 'batch', { - requests: batch.map(function (obj) { - var params = obj._getSaveParams(); - params.path = getServerUrlPath() + params.path; - return params; - }) - }, options); - }).then(function (response, status, xhr) { - batchReturned.resolve(response, status); - }); - - return _ParsePromise2.default.when(batchTasks); - }).then(function () { - if (objectError) { - return _ParsePromise2.default.error(objectError); - } - return _ParsePromise2.default.as(target); - }); - }); - } else if (target instanceof ParseObject) { - // copying target lets Flow guarantee the pointer isn't modified elsewhere - var targetCopy = target; - var task = function () { - var params = targetCopy._getSaveParams(); - return RESTController.request(params.method, params.path, params.body, options).then(function (response, status) { - targetCopy._handleSaveResponse(response, status); - }, function (error) { - targetCopy._handleSaveError(); - return _ParsePromise2.default.error(error); - }); - }; - - stateController.pushPendingState(target._getStateIdentifier()); - return stateController.enqueueTask(target._getStateIdentifier(), task).then(function () { - return target; - }, function (error) { - return _ParsePromise2.default.error(error); - }); - } - return _ParsePromise2.default.as(); - } -}; - -_CoreManager2.default.setObjectController(DefaultController); \ No newline at end of file diff --git a/lib/browser/ParseOp.js b/lib/browser/ParseOp.js deleted file mode 100644 index 1eba19303..000000000 --- a/lib/browser/ParseOp.js +++ /dev/null @@ -1,579 +0,0 @@ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.RelationOp = exports.RemoveOp = exports.AddUniqueOp = exports.AddOp = exports.IncrementOp = exports.UnsetOp = exports.SetOp = exports.Op = undefined; - -var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); - -var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); - -var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); - -var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); - -var _inherits2 = require('babel-runtime/helpers/inherits'); - -var _inherits3 = _interopRequireDefault(_inherits2); - -var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); - -var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); - -var _createClass2 = require('babel-runtime/helpers/createClass'); - -var _createClass3 = _interopRequireDefault(_createClass2); - -exports.opFromJSON = opFromJSON; - -var _arrayContainsObject = require('./arrayContainsObject'); - -var _arrayContainsObject2 = _interopRequireDefault(_arrayContainsObject); - -var _decode = require('./decode'); - -var _decode2 = _interopRequireDefault(_decode); - -var _encode = require('./encode'); - -var _encode2 = _interopRequireDefault(_encode); - -var _ParseObject = require('./ParseObject'); - -var _ParseObject2 = _interopRequireDefault(_ParseObject); - -var _ParseRelation = require('./ParseRelation'); - -var _ParseRelation2 = _interopRequireDefault(_ParseRelation); - -var _unique = require('./unique'); - -var _unique2 = _interopRequireDefault(_unique); - -function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { default: obj }; -} - -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * - */ - -function opFromJSON(json) { - if (!json || !json.__op) { - return null; - } - switch (json.__op) { - case 'Delete': - return new UnsetOp(); - case 'Increment': - return new IncrementOp(json.amount); - case 'Add': - return new AddOp((0, _decode2.default)(json.objects)); - case 'AddUnique': - return new AddUniqueOp((0, _decode2.default)(json.objects)); - case 'Remove': - return new RemoveOp((0, _decode2.default)(json.objects)); - case 'AddRelation': - var toAdd = (0, _decode2.default)(json.objects); - if (!Array.isArray(toAdd)) { - return new RelationOp([], []); - } - return new RelationOp(toAdd, []); - case 'RemoveRelation': - var toRemove = (0, _decode2.default)(json.objects); - if (!Array.isArray(toRemove)) { - return new RelationOp([], []); - } - return new RelationOp([], toRemove); - case 'Batch': - var toAdd = []; - var toRemove = []; - for (var i = 0; i < json.ops.length; i++) { - if (json.ops[i].__op === 'AddRelation') { - toAdd = toAdd.concat((0, _decode2.default)(json.ops[i].objects)); - } else if (json.ops[i].__op === 'RemoveRelation') { - toRemove = toRemove.concat((0, _decode2.default)(json.ops[i].objects)); - } - } - return new RelationOp(toAdd, toRemove); - } - return null; -} - -var Op = exports.Op = function () { - function Op() { - (0, _classCallCheck3.default)(this, Op); - } - - (0, _createClass3.default)(Op, [{ - key: 'applyTo', - - // Empty parent class - value: function (value) {} - }, { - key: 'mergeWith', - value: function (previous) {} - }, { - key: 'toJSON', - value: function () {} - }]); - return Op; -}(); - -var SetOp = exports.SetOp = function (_Op) { - (0, _inherits3.default)(SetOp, _Op); - - function SetOp(value) { - (0, _classCallCheck3.default)(this, SetOp); - - var _this = (0, _possibleConstructorReturn3.default)(this, (SetOp.__proto__ || (0, _getPrototypeOf2.default)(SetOp)).call(this)); - - _this._value = value; - return _this; - } - - (0, _createClass3.default)(SetOp, [{ - key: 'applyTo', - value: function (value) { - return this._value; - } - }, { - key: 'mergeWith', - value: function (previous) { - return new SetOp(this._value); - } - }, { - key: 'toJSON', - value: function () { - return (0, _encode2.default)(this._value, false, true); - } - }]); - return SetOp; -}(Op); - -var UnsetOp = exports.UnsetOp = function (_Op2) { - (0, _inherits3.default)(UnsetOp, _Op2); - - function UnsetOp() { - (0, _classCallCheck3.default)(this, UnsetOp); - return (0, _possibleConstructorReturn3.default)(this, (UnsetOp.__proto__ || (0, _getPrototypeOf2.default)(UnsetOp)).apply(this, arguments)); - } - - (0, _createClass3.default)(UnsetOp, [{ - key: 'applyTo', - value: function (value) { - return undefined; - } - }, { - key: 'mergeWith', - value: function (previous) { - return new UnsetOp(); - } - }, { - key: 'toJSON', - value: function () { - return { __op: 'Delete' }; - } - }]); - return UnsetOp; -}(Op); - -var IncrementOp = exports.IncrementOp = function (_Op3) { - (0, _inherits3.default)(IncrementOp, _Op3); - - function IncrementOp(amount) { - (0, _classCallCheck3.default)(this, IncrementOp); - - var _this3 = (0, _possibleConstructorReturn3.default)(this, (IncrementOp.__proto__ || (0, _getPrototypeOf2.default)(IncrementOp)).call(this)); - - if (typeof amount !== 'number') { - throw new TypeError('Increment Op must be initialized with a numeric amount.'); - } - _this3._amount = amount; - return _this3; - } - - (0, _createClass3.default)(IncrementOp, [{ - key: 'applyTo', - value: function (value) { - if (typeof value === 'undefined') { - return this._amount; - } - if (typeof value !== 'number') { - throw new TypeError('Cannot increment a non-numeric value.'); - } - return this._amount + value; - } - }, { - key: 'mergeWith', - value: function (previous) { - if (!previous) { - return this; - } - if (previous instanceof SetOp) { - return new SetOp(this.applyTo(previous._value)); - } - if (previous instanceof UnsetOp) { - return new SetOp(this._amount); - } - if (previous instanceof IncrementOp) { - return new IncrementOp(this.applyTo(previous._amount)); - } - throw new Error('Cannot merge Increment Op with the previous Op'); - } - }, { - key: 'toJSON', - value: function () { - return { __op: 'Increment', amount: this._amount }; - } - }]); - return IncrementOp; -}(Op); - -var AddOp = exports.AddOp = function (_Op4) { - (0, _inherits3.default)(AddOp, _Op4); - - function AddOp(value) { - (0, _classCallCheck3.default)(this, AddOp); - - var _this4 = (0, _possibleConstructorReturn3.default)(this, (AddOp.__proto__ || (0, _getPrototypeOf2.default)(AddOp)).call(this)); - - _this4._value = Array.isArray(value) ? value : [value]; - return _this4; - } - - (0, _createClass3.default)(AddOp, [{ - key: 'applyTo', - value: function (value) { - if (value == null) { - return this._value; - } - if (Array.isArray(value)) { - return value.concat(this._value); - } - throw new Error('Cannot add elements to a non-array value'); - } - }, { - key: 'mergeWith', - value: function (previous) { - if (!previous) { - return this; - } - if (previous instanceof SetOp) { - return new SetOp(this.applyTo(previous._value)); - } - if (previous instanceof UnsetOp) { - return new SetOp(this._value); - } - if (previous instanceof AddOp) { - return new AddOp(this.applyTo(previous._value)); - } - throw new Error('Cannot merge Add Op with the previous Op'); - } - }, { - key: 'toJSON', - value: function () { - return { __op: 'Add', objects: (0, _encode2.default)(this._value, false, true) }; - } - }]); - return AddOp; -}(Op); - -var AddUniqueOp = exports.AddUniqueOp = function (_Op5) { - (0, _inherits3.default)(AddUniqueOp, _Op5); - - function AddUniqueOp(value) { - (0, _classCallCheck3.default)(this, AddUniqueOp); - - var _this5 = (0, _possibleConstructorReturn3.default)(this, (AddUniqueOp.__proto__ || (0, _getPrototypeOf2.default)(AddUniqueOp)).call(this)); - - _this5._value = (0, _unique2.default)(Array.isArray(value) ? value : [value]); - return _this5; - } - - (0, _createClass3.default)(AddUniqueOp, [{ - key: 'applyTo', - value: function (value) { - if (value == null) { - return this._value || []; - } - if (Array.isArray(value)) { - // copying value lets Flow guarantee the pointer isn't modified elsewhere - var valueCopy = value; - var toAdd = []; - this._value.forEach(function (v) { - if (v instanceof _ParseObject2.default) { - if (!(0, _arrayContainsObject2.default)(valueCopy, v)) { - toAdd.push(v); - } - } else { - if (valueCopy.indexOf(v) < 0) { - toAdd.push(v); - } - } - }); - return value.concat(toAdd); - } - throw new Error('Cannot add elements to a non-array value'); - } - }, { - key: 'mergeWith', - value: function (previous) { - if (!previous) { - return this; - } - if (previous instanceof SetOp) { - return new SetOp(this.applyTo(previous._value)); - } - if (previous instanceof UnsetOp) { - return new SetOp(this._value); - } - if (previous instanceof AddUniqueOp) { - return new AddUniqueOp(this.applyTo(previous._value)); - } - throw new Error('Cannot merge AddUnique Op with the previous Op'); - } - }, { - key: 'toJSON', - value: function () { - return { __op: 'AddUnique', objects: (0, _encode2.default)(this._value, false, true) }; - } - }]); - return AddUniqueOp; -}(Op); - -var RemoveOp = exports.RemoveOp = function (_Op6) { - (0, _inherits3.default)(RemoveOp, _Op6); - - function RemoveOp(value) { - (0, _classCallCheck3.default)(this, RemoveOp); - - var _this6 = (0, _possibleConstructorReturn3.default)(this, (RemoveOp.__proto__ || (0, _getPrototypeOf2.default)(RemoveOp)).call(this)); - - _this6._value = (0, _unique2.default)(Array.isArray(value) ? value : [value]); - return _this6; - } - - (0, _createClass3.default)(RemoveOp, [{ - key: 'applyTo', - value: function (value) { - if (value == null) { - return []; - } - if (Array.isArray(value)) { - var i = value.indexOf(this._value); - var removed = value.concat([]); - for (var i = 0; i < this._value.length; i++) { - var index = removed.indexOf(this._value[i]); - while (index > -1) { - removed.splice(index, 1); - index = removed.indexOf(this._value[i]); - } - if (this._value[i] instanceof _ParseObject2.default && this._value[i].id) { - for (var j = 0; j < removed.length; j++) { - if (removed[j] instanceof _ParseObject2.default && this._value[i].id === removed[j].id) { - removed.splice(j, 1); - j--; - } - } - } - } - return removed; - } - throw new Error('Cannot remove elements from a non-array value'); - } - }, { - key: 'mergeWith', - value: function (previous) { - if (!previous) { - return this; - } - if (previous instanceof SetOp) { - return new SetOp(this.applyTo(previous._value)); - } - if (previous instanceof UnsetOp) { - return new UnsetOp(); - } - if (previous instanceof RemoveOp) { - var uniques = previous._value.concat([]); - for (var i = 0; i < this._value.length; i++) { - if (this._value[i] instanceof _ParseObject2.default) { - if (!(0, _arrayContainsObject2.default)(uniques, this._value[i])) { - uniques.push(this._value[i]); - } - } else { - if (uniques.indexOf(this._value[i]) < 0) { - uniques.push(this._value[i]); - } - } - } - return new RemoveOp(uniques); - } - throw new Error('Cannot merge Remove Op with the previous Op'); - } - }, { - key: 'toJSON', - value: function () { - return { __op: 'Remove', objects: (0, _encode2.default)(this._value, false, true) }; - } - }]); - return RemoveOp; -}(Op); - -var RelationOp = exports.RelationOp = function (_Op7) { - (0, _inherits3.default)(RelationOp, _Op7); - - function RelationOp(adds, removes) { - (0, _classCallCheck3.default)(this, RelationOp); - - var _this7 = (0, _possibleConstructorReturn3.default)(this, (RelationOp.__proto__ || (0, _getPrototypeOf2.default)(RelationOp)).call(this)); - - _this7._targetClassName = null; - - if (Array.isArray(adds)) { - _this7.relationsToAdd = (0, _unique2.default)(adds.map(_this7._extractId, _this7)); - } - - if (Array.isArray(removes)) { - _this7.relationsToRemove = (0, _unique2.default)(removes.map(_this7._extractId, _this7)); - } - return _this7; - } - - (0, _createClass3.default)(RelationOp, [{ - key: '_extractId', - value: function (obj) { - if (typeof obj === 'string') { - return obj; - } - if (!obj.id) { - throw new Error('You cannot add or remove an unsaved Parse Object from a relation'); - } - if (!this._targetClassName) { - this._targetClassName = obj.className; - } - if (this._targetClassName !== obj.className) { - throw new Error('Tried to create a Relation with 2 different object types: ' + this._targetClassName + ' and ' + obj.className + '.'); - } - return obj.id; - } - }, { - key: 'applyTo', - value: function (value, object, key) { - if (!value) { - if (!object || !key) { - throw new Error('Cannot apply a RelationOp without either a previous value, or an object and a key'); - } - var parent = new _ParseObject2.default(object.className); - if (object.id && object.id.indexOf('local') === 0) { - parent._localId = object.id; - } else if (object.id) { - parent.id = object.id; - } - var relation = new _ParseRelation2.default(parent, key); - relation.targetClassName = this._targetClassName; - return relation; - } - if (value instanceof _ParseRelation2.default) { - if (this._targetClassName) { - if (value.targetClassName) { - if (this._targetClassName !== value.targetClassName) { - throw new Error('Related object must be a ' + value.targetClassName + ', but a ' + this._targetClassName + ' was passed in.'); - } - } else { - value.targetClassName = this._targetClassName; - } - } - return value; - } else { - throw new Error('Relation cannot be applied to a non-relation field'); - } - } - }, { - key: 'mergeWith', - value: function (previous) { - if (!previous) { - return this; - } else if (previous instanceof UnsetOp) { - throw new Error('You cannot modify a relation after deleting it.'); - } else if (previous instanceof RelationOp) { - if (previous._targetClassName && previous._targetClassName !== this._targetClassName) { - throw new Error('Related object must be of class ' + previous._targetClassName + ', but ' + (this._targetClassName || 'null') + ' was passed in.'); - } - var newAdd = previous.relationsToAdd.concat([]); - this.relationsToRemove.forEach(function (r) { - var index = newAdd.indexOf(r); - if (index > -1) { - newAdd.splice(index, 1); - } - }); - this.relationsToAdd.forEach(function (r) { - var index = newAdd.indexOf(r); - if (index < 0) { - newAdd.push(r); - } - }); - - var newRemove = previous.relationsToRemove.concat([]); - this.relationsToAdd.forEach(function (r) { - var index = newRemove.indexOf(r); - if (index > -1) { - newRemove.splice(index, 1); - } - }); - this.relationsToRemove.forEach(function (r) { - var index = newRemove.indexOf(r); - if (index < 0) { - newRemove.push(r); - } - }); - - var newRelation = new RelationOp(newAdd, newRemove); - newRelation._targetClassName = this._targetClassName; - return newRelation; - } - throw new Error('Cannot merge Relation Op with the previous Op'); - } - }, { - key: 'toJSON', - value: function () { - var _this8 = this; - - var idToPointer = function (id) { - return { - __type: 'Pointer', - className: _this8._targetClassName, - objectId: id - }; - }; - - var adds = null; - var removes = null; - var pointers = null; - - if (this.relationsToAdd.length > 0) { - pointers = this.relationsToAdd.map(idToPointer); - adds = { __op: 'AddRelation', objects: pointers }; - } - if (this.relationsToRemove.length > 0) { - pointers = this.relationsToRemove.map(idToPointer); - removes = { __op: 'RemoveRelation', objects: pointers }; - } - - if (adds && removes) { - return { __op: 'Batch', ops: [adds, removes] }; - } - - return adds || removes || {}; - } - }]); - return RelationOp; -}(Op); \ No newline at end of file diff --git a/lib/browser/ParsePromise.js b/lib/browser/ParsePromise.js deleted file mode 100644 index a79b4afee..000000000 --- a/lib/browser/ParsePromise.js +++ /dev/null @@ -1,740 +0,0 @@ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _getIterator2 = require('babel-runtime/core-js/get-iterator'); - -var _getIterator3 = _interopRequireDefault(_getIterator2); - -var _typeof2 = require('babel-runtime/helpers/typeof'); - -var _typeof3 = _interopRequireDefault(_typeof2); - -var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); - -var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); - -var _createClass2 = require('babel-runtime/helpers/createClass'); - -var _createClass3 = _interopRequireDefault(_createClass2); - -function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { default: obj }; -} - -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -var _isPromisesAPlusCompliant = true; - -/** - * A Promise is returned by async methods as a hook to provide callbacks to be - * called when the async task is fulfilled. - * - *
Typical usage would be like:
- * query.find().then(function(results) { - * results[0].set("foo", "bar"); - * return results[0].saveAsync(); - * }).then(function(result) { - * console.log("Updated " + result.id); - * }); - *- * - * @class Parse.Promise - * @constructor - */ - -var ParsePromise = function () { - function ParsePromise(executor) { - (0, _classCallCheck3.default)(this, ParsePromise); - - this._resolved = false; - this._rejected = false; - this._resolvedCallbacks = []; - this._rejectedCallbacks = []; - - if (typeof executor === 'function') { - executor(this.resolve.bind(this), this.reject.bind(this)); - } - } - - /** - * Marks this promise as fulfilled, firing any callbacks waiting on it. - * @method resolve - * @param {Object} result the result to pass to the callbacks. - */ - - (0, _createClass3.default)(ParsePromise, [{ - key: 'resolve', - value: function () { - if (this._resolved || this._rejected) { - throw new Error('A promise was resolved even though it had already been ' + (this._resolved ? 'resolved' : 'rejected') + '.'); - } - this._resolved = true; - - for (var _len = arguments.length, results = Array(_len), _key = 0; _key < _len; _key++) { - results[_key] = arguments[_key]; - } - - this._result = results; - for (var i = 0; i < this._resolvedCallbacks.length; i++) { - this._resolvedCallbacks[i].apply(this, results); - } - - this._resolvedCallbacks = []; - this._rejectedCallbacks = []; - } - - /** - * Marks this promise as fulfilled, firing any callbacks waiting on it. - * @method reject - * @param {Object} error the error to pass to the callbacks. - */ - - }, { - key: 'reject', - value: function (error) { - if (this._resolved || this._rejected) { - throw new Error('A promise was rejected even though it had already been ' + (this._resolved ? 'resolved' : 'rejected') + '.'); - } - this._rejected = true; - this._error = error; - for (var i = 0; i < this._rejectedCallbacks.length; i++) { - this._rejectedCallbacks[i](error); - } - this._resolvedCallbacks = []; - this._rejectedCallbacks = []; - } - - /** - * Adds callbacks to be called when this promise is fulfilled. Returns a new - * Promise that will be fulfilled when the callback is complete. It allows - * chaining. If the callback itself returns a Promise, then the one returned - * by "then" will not be fulfilled until that one returned by the callback - * is fulfilled. - * @method then - * @param {Function} resolvedCallback Function that is called when this - * Promise is resolved. Once the callback is complete, then the Promise - * returned by "then" will also be fulfilled. - * @param {Function} rejectedCallback Function that is called when this - * Promise is rejected with an error. Once the callback is complete, then - * the promise returned by "then" with be resolved successfully. If - * rejectedCallback is null, or it returns a rejected Promise, then the - * Promise returned by "then" will be rejected with that error. - * @return {Parse.Promise} A new Promise that will be fulfilled after this - * Promise is fulfilled and either callback has completed. If the callback - * returned a Promise, then this Promise will not be fulfilled until that - * one is. - */ - - }, { - key: 'then', - value: function (resolvedCallback, rejectedCallback) { - var _this = this; - - var promise = new ParsePromise(); - - var wrappedResolvedCallback = function () { - for (var _len2 = arguments.length, results = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { - results[_key2] = arguments[_key2]; - } - - if (typeof resolvedCallback === 'function') { - if (_isPromisesAPlusCompliant) { - try { - results = [resolvedCallback.apply(this, results)]; - } catch (e) { - results = [ParsePromise.error(e)]; - } - } else { - results = [resolvedCallback.apply(this, results)]; - } - } - if (results.length === 1 && ParsePromise.is(results[0])) { - results[0].then(function () { - promise.resolve.apply(promise, arguments); - }, function (error) { - promise.reject(error); - }); - } else { - promise.resolve.apply(promise, results); - } - }; - - var wrappedRejectedCallback = function (error) { - var result = []; - if (typeof rejectedCallback === 'function') { - if (_isPromisesAPlusCompliant) { - try { - result = [rejectedCallback(error)]; - } catch (e) { - result = [ParsePromise.error(e)]; - } - } else { - result = [rejectedCallback(error)]; - } - if (result.length === 1 && ParsePromise.is(result[0])) { - result[0].then(function () { - promise.resolve.apply(promise, arguments); - }, function (error) { - promise.reject(error); - }); - } else { - if (_isPromisesAPlusCompliant) { - promise.resolve.apply(promise, result); - } else { - promise.reject(result[0]); - } - } - } else { - promise.reject(error); - } - }; - - var runLater = function (fn) { - fn.call(); - }; - if (_isPromisesAPlusCompliant) { - if (typeof process !== 'undefined' && typeof process.nextTick === 'function') { - runLater = function (fn) { - process.nextTick(fn); - }; - } else if (typeof setTimeout === 'function') { - runLater = function (fn) { - setTimeout(fn, 0); - }; - } - } - - if (this._resolved) { - runLater(function () { - wrappedResolvedCallback.apply(_this, _this._result); - }); - } else if (this._rejected) { - runLater(function () { - wrappedRejectedCallback(_this._error); - }); - } else { - this._resolvedCallbacks.push(wrappedResolvedCallback); - this._rejectedCallbacks.push(wrappedRejectedCallback); - } - - return promise; - } - - /** - * Add handlers to be called when the promise - * is either resolved or rejected - * @method always - */ - - }, { - key: 'always', - value: function (callback) { - return this.then(callback, callback); - } - - /** - * Add handlers to be called when the Promise object is resolved - * @method done - */ - - }, { - key: 'done', - value: function (callback) { - return this.then(callback); - } - - /** - * Add handlers to be called when the Promise object is rejected - * Alias for catch(). - * @method fail - */ - - }, { - key: 'fail', - value: function (callback) { - return this.then(null, callback); - } - - /** - * Add handlers to be called when the Promise object is rejected - * @method catch - */ - - }, { - key: 'catch', - value: function (callback) { - return this.then(null, callback); - } - - /** - * Run the given callbacks after this promise is fulfilled. - * @method _thenRunCallbacks - * @param optionsOrCallback {} A Backbone-style options callback, or a - * callback function. If this is an options object and contains a "model" - * attributes, that will be passed to error callbacks as the first argument. - * @param model {} If truthy, this will be passed as the first result of - * error callbacks. This is for Backbone-compatability. - * @return {Parse.Promise} A promise that will be resolved after the - * callbacks are run, with the same result as this. - */ - - }, { - key: '_thenRunCallbacks', - value: function (optionsOrCallback, model) { - var options = {}; - if (typeof optionsOrCallback === 'function') { - options.success = function (result) { - optionsOrCallback(result, null); - }; - options.error = function (error) { - optionsOrCallback(null, error); - }; - } else if ((typeof optionsOrCallback === 'undefined' ? 'undefined' : (0, _typeof3.default)(optionsOrCallback)) === 'object') { - if (typeof optionsOrCallback.success === 'function') { - options.success = optionsOrCallback.success; - } - if (typeof optionsOrCallback.error === 'function') { - options.error = optionsOrCallback.error; - } - } - - return this.then(function () { - for (var _len3 = arguments.length, results = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) { - results[_key3] = arguments[_key3]; - } - - if (options.success) { - options.success.apply(this, results); - } - return ParsePromise.as.apply(ParsePromise, arguments); - }, function (error) { - if (options.error) { - if (typeof model !== 'undefined') { - options.error(model, error); - } else { - options.error(error); - } - } - // By explicitly returning a rejected Promise, this will work with - // either jQuery or Promises/A+ semantics. - return ParsePromise.error(error); - }); - } - - /** - * Adds a callback function that should be called regardless of whether - * this promise failed or succeeded. The callback will be given either the - * array of results for its first argument, or the error as its second, - * depending on whether this Promise was rejected or resolved. Returns a - * new Promise, like "then" would. - * @method _continueWith - * @param {Function} continuation the callback. - */ - - }, { - key: '_continueWith', - value: function (continuation) { - return this.then(function () { - for (var _len4 = arguments.length, args = Array(_len4), _key4 = 0; _key4 < _len4; _key4++) { - args[_key4] = arguments[_key4]; - } - - return continuation(args, null); - }, function (error) { - return continuation(null, error); - }); - } - - /** - * Returns true iff the given object fulfils the Promise interface. - * @method is - * @param {Object} promise The object to test - * @static - * @return {Boolean} - */ - - }], [{ - key: 'is', - value: function (promise) { - return promise != null && typeof promise.then === 'function'; - } - - /** - * Returns a new promise that is resolved with a given value. - * @method as - * @param value The value to resolve the promise with - * @static - * @return {Parse.Promise} the new promise. - */ - - }, { - key: 'as', - value: function () { - var promise = new ParsePromise(); - - for (var _len5 = arguments.length, values = Array(_len5), _key5 = 0; _key5 < _len5; _key5++) { - values[_key5] = arguments[_key5]; - } - - promise.resolve.apply(promise, values); - return promise; - } - - /** - * Returns a new promise that is resolved with a given value. - * If that value is a thenable Promise (has a .then() prototype - * method), the new promise will be chained to the end of the - * value. - * @method resolve - * @param value The value to resolve the promise with - * @static - * @return {Parse.Promise} the new promise. - */ - - }, { - key: 'resolve', - value: function (value) { - return new ParsePromise(function (resolve, reject) { - if (ParsePromise.is(value)) { - value.then(resolve, reject); - } else { - resolve(value); - } - }); - } - - /** - * Returns a new promise that is rejected with a given error. - * @method error - * @param error The error to reject the promise with - * @static - * @return {Parse.Promise} the new promise. - */ - - }, { - key: 'error', - value: function () { - var promise = new ParsePromise(); - - for (var _len6 = arguments.length, errors = Array(_len6), _key6 = 0; _key6 < _len6; _key6++) { - errors[_key6] = arguments[_key6]; - } - - promise.reject.apply(promise, errors); - return promise; - } - - /** - * Returns a new promise that is rejected with a given error. - * This is an alias for Parse.Promise.error, for compliance with - * the ES6 implementation. - * @method reject - * @param error The error to reject the promise with - * @static - * @return {Parse.Promise} the new promise. - */ - - }, { - key: 'reject', - value: function () { - for (var _len7 = arguments.length, errors = Array(_len7), _key7 = 0; _key7 < _len7; _key7++) { - errors[_key7] = arguments[_key7]; - } - - return ParsePromise.error.apply(null, errors); - } - - /** - * Returns a new promise that is fulfilled when all of the input promises - * are resolved. If any promise in the list fails, then the returned promise - * will be rejected with an array containing the error from each promise. - * If they all succeed, then the returned promise will succeed, with the - * results being the results of all the input - * promises. For example:
- * var p1 = Parse.Promise.as(1); - * var p2 = Parse.Promise.as(2); - * var p3 = Parse.Promise.as(3); - * - * Parse.Promise.when(p1, p2, p3).then(function(r1, r2, r3) { - * console.log(r1); // prints 1 - * console.log(r2); // prints 2 - * console.log(r3); // prints 3 - * });- * - * The input promises can also be specified as an array:
- * var promises = [p1, p2, p3]; - * Parse.Promise.when(promises).then(function(results) { - * console.log(results); // prints [1,2,3] - * }); - *- * @method when - * @param {Array} promises a list of promises to wait for. - * @static - * @return {Parse.Promise} the new promise. - */ - - }, { - key: 'when', - value: function (promises) { - var objects; - var arrayArgument = Array.isArray(promises); - if (arrayArgument) { - objects = promises; - } else { - objects = arguments; - } - - var total = objects.length; - var hadError = false; - var results = []; - var returnValue = arrayArgument ? [results] : results; - var errors = []; - results.length = objects.length; - errors.length = objects.length; - - if (total === 0) { - return ParsePromise.as.apply(this, returnValue); - } - - var promise = new ParsePromise(); - - var resolveOne = function () { - total--; - if (total <= 0) { - if (hadError) { - promise.reject(errors); - } else { - promise.resolve.apply(promise, returnValue); - } - } - }; - - var chain = function (object, index) { - if (ParsePromise.is(object)) { - object.then(function (result) { - results[index] = result; - resolveOne(); - }, function (error) { - errors[index] = error; - hadError = true; - resolveOne(); - }); - } else { - results[i] = object; - resolveOne(); - } - }; - for (var i = 0; i < objects.length; i++) { - chain(objects[i], i); - } - - return promise; - } - - /** - * Returns a new promise that is fulfilled when all of the promises in the - * iterable argument are resolved. If any promise in the list fails, then - * the returned promise will be immediately rejected with the reason that - * single promise rejected. If they all succeed, then the returned promise - * will succeed, with the results being the results of all the input - * promises. If the iterable provided is empty, the returned promise will - * be immediately resolved. - * - * For example:
- * var p1 = Parse.Promise.as(1); - * var p2 = Parse.Promise.as(2); - * var p3 = Parse.Promise.as(3); - * - * Parse.Promise.all([p1, p2, p3]).then(function([r1, r2, r3]) { - * console.log(r1); // prints 1 - * console.log(r2); // prints 2 - * console.log(r3); // prints 3 - * });- * - * @method all - * @param {Iterable} promises an iterable of promises to wait for. - * @static - * @return {Parse.Promise} the new promise. - */ - - }, { - key: 'all', - value: function (promises) { - var total = 0; - var objects = []; - - var _iteratorNormalCompletion = true; - var _didIteratorError = false; - var _iteratorError = undefined; - - try { - for (var _iterator = (0, _getIterator3.default)(promises), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { - var p = _step.value; - - objects[total++] = p; - } - } catch (err) { - _didIteratorError = true; - _iteratorError = err; - } finally { - try { - if (!_iteratorNormalCompletion && _iterator.return) { - _iterator.return(); - } - } finally { - if (_didIteratorError) { - throw _iteratorError; - } - } - } - - if (total === 0) { - return ParsePromise.as([]); - } - - var hadError = false; - var promise = new ParsePromise(); - var resolved = 0; - var results = []; - objects.forEach(function (object, i) { - if (ParsePromise.is(object)) { - object.then(function (result) { - if (hadError) { - return false; - } - results[i] = result; - resolved++; - if (resolved >= total) { - promise.resolve(results); - } - }, function (error) { - // Reject immediately - promise.reject(error); - hadError = true; - }); - } else { - results[i] = object; - resolved++; - if (!hadError && resolved >= total) { - promise.resolve(results); - } - } - }); - - return promise; - } - - /** - * Returns a new promise that is immediately fulfilled when any of the - * promises in the iterable argument are resolved or rejected. If the - * first promise to complete is resolved, the returned promise will be - * resolved with the same value. Likewise, if the first promise to - * complete is rejected, the returned promise will be rejected with the - * same reason. - * - * @method race - * @param {Iterable} promises an iterable of promises to wait for. - * @static - * @return {Parse.Promise} the new promise. - */ - - }, { - key: 'race', - value: function (promises) { - var completed = false; - var promise = new ParsePromise(); - var _iteratorNormalCompletion2 = true; - var _didIteratorError2 = false; - var _iteratorError2 = undefined; - - try { - for (var _iterator2 = (0, _getIterator3.default)(promises), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { - var p = _step2.value; - - if (ParsePromise.is(p)) { - p.then(function (result) { - if (completed) { - return; - } - completed = true; - promise.resolve(result); - }, function (error) { - if (completed) { - return; - } - completed = true; - promise.reject(error); - }); - } else if (!completed) { - completed = true; - promise.resolve(p); - } - } - } catch (err) { - _didIteratorError2 = true; - _iteratorError2 = err; - } finally { - try { - if (!_iteratorNormalCompletion2 && _iterator2.return) { - _iterator2.return(); - } - } finally { - if (_didIteratorError2) { - throw _iteratorError2; - } - } - } - - return promise; - } - - /** - * Runs the given asyncFunction repeatedly, as long as the predicate - * function returns a truthy value. Stops repeating if asyncFunction returns - * a rejected promise. - * @method _continueWhile - * @param {Function} predicate should return false when ready to stop. - * @param {Function} asyncFunction should return a Promise. - * @static - */ - - }, { - key: '_continueWhile', - value: function (predicate, asyncFunction) { - if (predicate()) { - return asyncFunction().then(function () { - return ParsePromise._continueWhile(predicate, asyncFunction); - }); - } - return ParsePromise.as(); - } - }, { - key: 'isPromisesAPlusCompliant', - value: function () { - return _isPromisesAPlusCompliant; - } - }, { - key: 'enableAPlusCompliant', - value: function () { - _isPromisesAPlusCompliant = true; - } - }, { - key: 'disableAPlusCompliant', - value: function () { - _isPromisesAPlusCompliant = false; - } - }]); - return ParsePromise; -}(); - -exports.default = ParsePromise; \ No newline at end of file diff --git a/lib/browser/ParseQuery.js b/lib/browser/ParseQuery.js deleted file mode 100644 index 664f5a737..000000000 --- a/lib/browser/ParseQuery.js +++ /dev/null @@ -1,1340 +0,0 @@ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _typeof2 = require('babel-runtime/helpers/typeof'); - -var _typeof3 = _interopRequireDefault(_typeof2); - -var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); - -var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); - -var _createClass2 = require('babel-runtime/helpers/createClass'); - -var _createClass3 = _interopRequireDefault(_createClass2); - -var _keys = require('babel-runtime/core-js/object/keys'); - -var _keys2 = _interopRequireDefault(_keys); - -var _CoreManager = require('./CoreManager'); - -var _CoreManager2 = _interopRequireDefault(_CoreManager); - -var _encode = require('./encode'); - -var _encode2 = _interopRequireDefault(_encode); - -var _ParseError = require('./ParseError'); - -var _ParseError2 = _interopRequireDefault(_ParseError); - -var _ParseGeoPoint = require('./ParseGeoPoint'); - -var _ParseGeoPoint2 = _interopRequireDefault(_ParseGeoPoint); - -var _ParseObject = require('./ParseObject'); - -var _ParseObject2 = _interopRequireDefault(_ParseObject); - -var _ParsePromise = require('./ParsePromise'); - -var _ParsePromise2 = _interopRequireDefault(_ParsePromise); - -function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { default: obj }; -} - -/** - * Converts a string into a regex that matches it. - * Surrounding with \Q .. \E does this, we just need to escape any \E's in - * the text separately. - */ -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * - */ - -function quote(s) { - return '\\Q' + s.replace('\\E', '\\E\\\\E\\Q') + '\\E'; -} - -/** - * Handles pre-populating the result data of a query with select fields, - * making sure that the data object contains keys for all objects that have - * been requested with a select, so that our cached state updates correctly. - */ -function handleSelectResult(data, select) { - var serverDataMask = {}; - - select.forEach(function (field) { - var hasSubObjectSelect = field.indexOf(".") !== -1; - if (!hasSubObjectSelect && !data.hasOwnProperty(field)) { - // this field was selected, but is missing from the retrieved data - data[field] = undefined; - } else if (hasSubObjectSelect) { - // this field references a sub-object, - // so we need to walk down the path components - var pathComponents = field.split("."); - var obj = data; - var serverMask = serverDataMask; - - pathComponents.forEach(function (component, index, arr) { - // add keys if the expected data is missing - if (!obj[component]) { - obj[component] = index == arr.length - 1 ? undefined : {}; - } - obj = obj[component]; - - //add this path component to the server mask so we can fill it in later if needed - if (index < arr.length - 1) { - if (!serverMask[component]) { - serverMask[component] = {}; - } - } - }); - } - }); - - if ((0, _keys2.default)(serverDataMask).length > 0) { - var copyMissingDataWithMask = function copyMissingDataWithMask(src, dest, mask, copyThisLevel) { - //copy missing elements at this level - if (copyThisLevel) { - for (var key in src) { - if (src.hasOwnProperty(key) && !dest.hasOwnProperty(key)) { - dest[key] = src[key]; - } - } - } - for (var key in mask) { - //traverse into objects as needed - copyMissingDataWithMask(src[key], dest[key], mask[key], true); - } - }; - - // When selecting from sub-objects, we don't want to blow away the missing - // information that we may have retrieved before. We've already added any - // missing selected keys to sub-objects, but we still need to add in the - // data for any previously retrieved sub-objects that were not selected. - - var serverData = _CoreManager2.default.getObjectStateController().getServerData({ id: data.objectId, className: data.className }); - - copyMissingDataWithMask(serverData, data, serverDataMask, false); - } -} - -/** - * Creates a new parse Parse.Query for the given Parse.Object subclass. - * @class Parse.Query - * @constructor - * @param {} objectClass An instance of a subclass of Parse.Object, or a Parse className string. - * - *
Parse.Query defines a query that is used to fetch Parse.Objects. The
- * most common use case is finding all objects that match a query through the
- * find
method. For example, this sample code fetches all objects
- * of class MyClass
. It calls a different function depending on
- * whether the fetch succeeded or not.
- *
- *
- * var query = new Parse.Query(MyClass); - * query.find({ - * success: function(results) { - * // results is an array of Parse.Object. - * }, - * - * error: function(error) { - * // error is an instance of Parse.Error. - * } - * });- * - *
A Parse.Query can also be used to retrieve a single object whose id is
- * known, through the get method. For example, this sample code fetches an
- * object of class MyClass
and id myId
. It calls a
- * different function depending on whether the fetch succeeded or not.
- *
- *
- * var query = new Parse.Query(MyClass); - * query.get(myId, { - * success: function(object) { - * // object is an instance of Parse.Object. - * }, - * - * error: function(object, error) { - * // error is an instance of Parse.Error. - * } - * });- * - *
A Parse.Query can also be used to count the number of objects that match
- * the query without retrieving all of those objects. For example, this
- * sample code counts the number of objects of the class MyClass
- *
- * var query = new Parse.Query(MyClass); - * query.count({ - * success: function(number) { - * // There are number instances of MyClass. - * }, - * - * error: function(error) { - * // error is an instance of Parse.Error. - * } - * });- */ - -var ParseQuery = function () { - function ParseQuery(objectClass) { - (0, _classCallCheck3.default)(this, ParseQuery); - - if (typeof objectClass === 'string') { - if (objectClass === 'User' && _CoreManager2.default.get('PERFORM_USER_REWRITE')) { - this.className = '_User'; - } else { - this.className = objectClass; - } - } else if (objectClass instanceof _ParseObject2.default) { - this.className = objectClass.className; - } else if (typeof objectClass === 'function') { - if (typeof objectClass.className === 'string') { - this.className = objectClass.className; - } else { - var obj = new objectClass(); - this.className = obj.className; - } - } else { - throw new TypeError('A ParseQuery must be constructed with a ParseObject or class name.'); - } - - this._where = {}; - this._include = []; - this._limit = -1; // negative limit is not sent in the server request - this._skip = 0; - this._extraOptions = {}; - } - - /** - * Adds constraint that at least one of the passed in queries matches. - * @method _orQuery - * @param {Array} queries - * @return {Parse.Query} Returns the query, so you can chain this call. - */ - - (0, _createClass3.default)(ParseQuery, [{ - key: '_orQuery', - value: function (queries) { - var queryJSON = queries.map(function (q) { - return q.toJSON().where; - }); - - this._where.$or = queryJSON; - return this; - } - - /** - * Helper for condition queries - */ - - }, { - key: '_addCondition', - value: function (key, condition, value) { - if (!this._where[key] || typeof this._where[key] === 'string') { - this._where[key] = {}; - } - this._where[key][condition] = (0, _encode2.default)(value, false, true); - return this; - } - - /** - * Converts string for regular expression at the beginning - */ - - }, { - key: '_regexStartWith', - value: function (string) { - return '^' + quote(string); - } - - /** - * Returns a JSON representation of this query. - * @method toJSON - * @return {Object} The JSON representation of the query. - */ - - }, { - key: 'toJSON', - value: function () { - var params = { - where: this._where - }; - - if (this._include.length) { - params.include = this._include.join(','); - } - if (this._select) { - params.keys = this._select.join(','); - } - if (this._limit >= 0) { - params.limit = this._limit; - } - if (this._skip > 0) { - params.skip = this._skip; - } - if (this._order) { - params.order = this._order.join(','); - } - for (var key in this._extraOptions) { - params[key] = this._extraOptions[key]; - } - - return params; - } - - /** - * Constructs a Parse.Object whose id is already known by fetching data from - * the server. Either options.success or options.error is called when the - * find completes. - * - * @method get - * @param {String} objectId The id of the object to be fetched. - * @param {Object} options A Backbone-style options object. - * Valid options are:
var compoundQuery = Parse.Query.or(query1, query2, query3);- * - * will create a compoundQuery that is an or of the query1, query2, and - * query3. - * @method or - * @param {...Parse.Query} var_args The list of queries to OR. - * @static - * @return {Parse.Query} The query that is the OR of the passed in queries. - */ - - }], [{ - key: 'or', - value: function () { - var className = null; - - for (var _len7 = arguments.length, queries = Array(_len7), _key7 = 0; _key7 < _len7; _key7++) { - queries[_key7] = arguments[_key7]; - } - - queries.forEach(function (q) { - if (!className) { - className = q.className; - } - - if (className !== q.className) { - throw new Error('All queries must be for the same class.'); - } - }); - - var query = new ParseQuery(className); - query._orQuery(queries); - return query; - } - }]); - return ParseQuery; -}(); - -exports.default = ParseQuery; - -var DefaultController = { - find: function (className, params, options) { - var RESTController = _CoreManager2.default.getRESTController(); - - return RESTController.request('GET', 'classes/' + className, params, options); - } -}; - -_CoreManager2.default.setQueryController(DefaultController); \ No newline at end of file diff --git a/lib/browser/ParseRelation.js b/lib/browser/ParseRelation.js deleted file mode 100644 index fe9ea8769..000000000 --- a/lib/browser/ParseRelation.js +++ /dev/null @@ -1,182 +0,0 @@ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); - -var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); - -var _createClass2 = require('babel-runtime/helpers/createClass'); - -var _createClass3 = _interopRequireDefault(_createClass2); - -var _ParseOp = require('./ParseOp'); - -var _ParseObject = require('./ParseObject'); - -var _ParseObject2 = _interopRequireDefault(_ParseObject); - -var _ParseQuery = require('./ParseQuery'); - -var _ParseQuery2 = _interopRequireDefault(_ParseQuery); - -function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { default: obj }; -} - -/** - * Creates a new Relation for the given parent object and key. This - * constructor should rarely be used directly, but rather created by - * Parse.Object.relation. - * @class Parse.Relation - * @constructor - * @param {Parse.Object} parent The parent of this relation. - * @param {String} key The key for this relation on the parent. - * - *
- * A class that is used to access all of the children of a many-to-many - * relationship. Each instance of Parse.Relation is associated with a - * particular parent object and key. - *
- */ -var ParseRelation = function () { - function ParseRelation(parent, key) { - (0, _classCallCheck3.default)(this, ParseRelation); - - this.parent = parent; - this.key = key; - this.targetClassName = null; - } - - /** - * Makes sure that this relation has the right parent and key. - */ - - (0, _createClass3.default)(ParseRelation, [{ - key: '_ensureParentAndKey', - value: function (parent, key) { - this.key = this.key || key; - if (this.key !== key) { - throw new Error('Internal Error. Relation retrieved from two different keys.'); - } - if (this.parent) { - if (this.parent.className !== parent.className) { - throw new Error('Internal Error. Relation retrieved from two different Objects.'); - } - if (this.parent.id) { - if (this.parent.id !== parent.id) { - throw new Error('Internal Error. Relation retrieved from two different Objects.'); - } - } else if (parent.id) { - this.parent = parent; - } - } else { - this.parent = parent; - } - } - - /** - * Adds a Parse.Object or an array of Parse.Objects to the relation. - * @method add - * @param {} objects The item or items to add. - */ - - }, { - key: 'add', - value: function (objects) { - if (!Array.isArray(objects)) { - objects = [objects]; - } - - var change = new _ParseOp.RelationOp(objects, []); - var parent = this.parent; - if (!parent) { - throw new Error('Cannot add to a Relation without a parent'); - } - parent.set(this.key, change); - this.targetClassName = change._targetClassName; - return parent; - } - - /** - * Removes a Parse.Object or an array of Parse.Objects from this relation. - * @method remove - * @param {} objects The item or items to remove. - */ - - }, { - key: 'remove', - value: function (objects) { - if (!Array.isArray(objects)) { - objects = [objects]; - } - - var change = new _ParseOp.RelationOp([], objects); - if (!this.parent) { - throw new Error('Cannot remove from a Relation without a parent'); - } - this.parent.set(this.key, change); - this.targetClassName = change._targetClassName; - } - - /** - * Returns a JSON version of the object suitable for saving to disk. - * @method toJSON - * @return {Object} - */ - - }, { - key: 'toJSON', - value: function () { - return { - __type: 'Relation', - className: this.targetClassName - }; - } - - /** - * Returns a Parse.Query that is limited to objects in this - * relation. - * @method query - * @return {Parse.Query} - */ - - }, { - key: 'query', - value: function () { - var query; - var parent = this.parent; - if (!parent) { - throw new Error('Cannot construct a query for a Relation without a parent'); - } - if (!this.targetClassName) { - query = new _ParseQuery2.default(parent.className); - query._extraOptions.redirectClassNameForKey = this.key; - } else { - query = new _ParseQuery2.default(this.targetClassName); - } - query._addCondition('$relatedTo', 'object', { - __type: 'Pointer', - className: parent.className, - objectId: parent.id - }); - query._addCondition('$relatedTo', 'key', this.key); - - return query; - } - }]); - return ParseRelation; -}(); /** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * - */ - -exports.default = ParseRelation; \ No newline at end of file diff --git a/lib/browser/ParseRole.js b/lib/browser/ParseRole.js deleted file mode 100644 index 567af7a54..000000000 --- a/lib/browser/ParseRole.js +++ /dev/null @@ -1,196 +0,0 @@ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); - -var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); - -var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); - -var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); - -var _createClass2 = require('babel-runtime/helpers/createClass'); - -var _createClass3 = _interopRequireDefault(_createClass2); - -var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); - -var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); - -var _get2 = require('babel-runtime/helpers/get'); - -var _get3 = _interopRequireDefault(_get2); - -var _inherits2 = require('babel-runtime/helpers/inherits'); - -var _inherits3 = _interopRequireDefault(_inherits2); - -var _ParseACL = require('./ParseACL'); - -var _ParseACL2 = _interopRequireDefault(_ParseACL); - -var _ParseError = require('./ParseError'); - -var _ParseError2 = _interopRequireDefault(_ParseError); - -var _ParseObject2 = require('./ParseObject'); - -var _ParseObject3 = _interopRequireDefault(_ParseObject2); - -function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { default: obj }; -} - -/** - * Represents a Role on the Parse server. Roles represent groupings of - * Users for the purposes of granting permissions (e.g. specifying an ACL - * for an Object). Roles are specified by their sets of child users and - * child roles, all of which are granted any permissions that the parent - * role has. - * - *Roles must have a name (which cannot be changed after creation of the - * role), and must specify an ACL.
- * @class Parse.Role - * @constructor - * @param {String} name The name of the Role to create. - * @param {Parse.ACL} acl The ACL for this role. Roles must have an ACL. - * A Parse.Role is a local representation of a role persisted to the Parse - * cloud. - */ -var ParseRole = function (_ParseObject) { - (0, _inherits3.default)(ParseRole, _ParseObject); - - function ParseRole(name, acl) { - (0, _classCallCheck3.default)(this, ParseRole); - - var _this = (0, _possibleConstructorReturn3.default)(this, (ParseRole.__proto__ || (0, _getPrototypeOf2.default)(ParseRole)).call(this, '_Role')); - - if (typeof name === 'string' && acl instanceof _ParseACL2.default) { - _this.setName(name); - _this.setACL(acl); - } - return _this; - } - - /** - * Gets the name of the role. You can alternatively call role.get("name") - * - * @method getName - * @return {String} the name of the role. - */ - - (0, _createClass3.default)(ParseRole, [{ - key: 'getName', - value: function () { - var name = this.get('name'); - if (name == null || typeof name === 'string') { - return name; - } - return ''; - } - - /** - * Sets the name for a role. This value must be set before the role has - * been saved to the server, and cannot be set once the role has been - * saved. - * - *- * A role's name can only contain alphanumeric characters, _, -, and - * spaces. - *
- * - *This is equivalent to calling role.set("name", name)
- * - * @method setName - * @param {String} name The name of the role. - * @param {Object} options Standard options object with success and error - * callbacks. - */ - - }, { - key: 'setName', - value: function (name, options) { - return this.set('name', name, options); - } - - /** - * Gets the Parse.Relation for the Parse.Users that are direct - * children of this role. These users are granted any privileges that this - * role has been granted (e.g. read or write access through ACLs). You can - * add or remove users from the role through this relation. - * - *This is equivalent to calling role.relation("users")
- * - * @method getUsers - * @return {Parse.Relation} the relation for the users belonging to this - * role. - */ - - }, { - key: 'getUsers', - value: function () { - return this.relation('users'); - } - - /** - * Gets the Parse.Relation for the Parse.Roles that are direct - * children of this role. These roles' users are granted any privileges that - * this role has been granted (e.g. read or write access through ACLs). You - * can add or remove child roles from this role through this relation. - * - *This is equivalent to calling role.relation("roles")
- * - * @method getRoles - * @return {Parse.Relation} the relation for the roles belonging to this - * role. - */ - - }, { - key: 'getRoles', - value: function () { - return this.relation('roles'); - } - }, { - key: 'validate', - value: function (attrs, options) { - var isInvalid = (0, _get3.default)(ParseRole.prototype.__proto__ || (0, _getPrototypeOf2.default)(ParseRole.prototype), 'validate', this).call(this, attrs, options); - if (isInvalid) { - return isInvalid; - } - - if ('name' in attrs && attrs.name !== this.getName()) { - var newName = attrs.name; - if (this.id && this.id !== attrs.objectId) { - // Check to see if the objectId being set matches this.id - // This happens during a fetch -- the id is set before calling fetch - // Let the name be set in this case - return new _ParseError2.default(_ParseError2.default.OTHER_CAUSE, 'A role\'s name can only be set before it has been saved.'); - } - if (typeof newName !== 'string') { - return new _ParseError2.default(_ParseError2.default.OTHER_CAUSE, 'A role\'s name must be a String.'); - } - if (!/^[0-9a-zA-Z\-_ ]+$/.test(newName)) { - return new _ParseError2.default(_ParseError2.default.OTHER_CAUSE, 'A role\'s name can be only contain alphanumeric characters, _, ' + '-, and spaces.'); - } - } - return false; - } - }]); - return ParseRole; -}(_ParseObject3.default); /** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * - */ - -exports.default = ParseRole; - -_ParseObject3.default.registerSubclass('_Role', ParseRole); \ No newline at end of file diff --git a/lib/browser/ParseSession.js b/lib/browser/ParseSession.js deleted file mode 100644 index 7fb75c80d..000000000 --- a/lib/browser/ParseSession.js +++ /dev/null @@ -1,180 +0,0 @@ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _typeof2 = require('babel-runtime/helpers/typeof'); - -var _typeof3 = _interopRequireDefault(_typeof2); - -var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); - -var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); - -var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); - -var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); - -var _createClass2 = require('babel-runtime/helpers/createClass'); - -var _createClass3 = _interopRequireDefault(_createClass2); - -var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); - -var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); - -var _inherits2 = require('babel-runtime/helpers/inherits'); - -var _inherits3 = _interopRequireDefault(_inherits2); - -var _CoreManager = require('./CoreManager'); - -var _CoreManager2 = _interopRequireDefault(_CoreManager); - -var _isRevocableSession = require('./isRevocableSession'); - -var _isRevocableSession2 = _interopRequireDefault(_isRevocableSession); - -var _ParseObject2 = require('./ParseObject'); - -var _ParseObject3 = _interopRequireDefault(_ParseObject2); - -var _ParsePromise = require('./ParsePromise'); - -var _ParsePromise2 = _interopRequireDefault(_ParsePromise); - -var _ParseUser = require('./ParseUser'); - -var _ParseUser2 = _interopRequireDefault(_ParseUser); - -function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { default: obj }; -} - -/** - * @class Parse.Session - * @constructor - * - *A Parse.Session object is a local representation of a revocable session. - * This class is a subclass of a Parse.Object, and retains the same - * functionality of a Parse.Object.
- */ -var ParseSession = function (_ParseObject) { - (0, _inherits3.default)(ParseSession, _ParseObject); - - function ParseSession(attributes) { - (0, _classCallCheck3.default)(this, ParseSession); - - var _this = (0, _possibleConstructorReturn3.default)(this, (ParseSession.__proto__ || (0, _getPrototypeOf2.default)(ParseSession)).call(this, '_Session')); - - if (attributes && (typeof attributes === 'undefined' ? 'undefined' : (0, _typeof3.default)(attributes)) === 'object') { - if (!_this.set(attributes || {})) { - throw new Error('Can\'t create an invalid Session'); - } - } - return _this; - } - - /** - * Returns the session token string. - * @method getSessionToken - * @return {String} - */ - - (0, _createClass3.default)(ParseSession, [{ - key: 'getSessionToken', - value: function () { - var token = this.get('sessionToken'); - if (typeof token === 'string') { - return token; - } - return ''; - } - }], [{ - key: 'readOnlyAttributes', - value: function () { - return ['createdWith', 'expiresAt', 'installationId', 'restricted', 'sessionToken', 'user']; - } - - /** - * Retrieves the Session object for the currently logged in session. - * @method current - * @static - * @return {Parse.Promise} A promise that is resolved with the Parse.Session - * object after it has been fetched. If there is no current user, the - * promise will be rejected. - */ - - }, { - key: 'current', - value: function (options) { - options = options || {}; - var controller = _CoreManager2.default.getSessionController(); - - var sessionOptions = {}; - if (options.hasOwnProperty('useMasterKey')) { - sessionOptions.useMasterKey = options.useMasterKey; - } - return _ParseUser2.default.currentAsync().then(function (user) { - if (!user) { - return _ParsePromise2.default.error('There is no current user.'); - } - user.getSessionToken(); - - sessionOptions.sessionToken = user.getSessionToken(); - return controller.getSession(sessionOptions); - }); - } - - /** - * Determines whether the current session token is revocable. - * This method is useful for migrating Express.js or Node.js web apps to - * use revocable sessions. If you are migrating an app that uses the Parse - * SDK in the browser only, please use Parse.User.enableRevocableSession() - * instead, so that sessions can be automatically upgraded. - * @method isCurrentSessionRevocable - * @static - * @return {Boolean} - */ - - }, { - key: 'isCurrentSessionRevocable', - value: function () { - var currentUser = _ParseUser2.default.current(); - if (currentUser) { - return (0, _isRevocableSession2.default)(currentUser.getSessionToken() || ''); - } - return false; - } - }]); - return ParseSession; -}(_ParseObject3.default); /** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * - */ - -exports.default = ParseSession; - -_ParseObject3.default.registerSubclass('_Session', ParseSession); - -var DefaultController = { - getSession: function (options) { - var RESTController = _CoreManager2.default.getRESTController(); - var session = new ParseSession(); - - return RESTController.request('GET', 'sessions/me', {}, options).then(function (sessionData) { - session._finishFetch(sessionData); - session._setExisted(true); - return session; - }); - } -}; - -_CoreManager2.default.setSessionController(DefaultController); \ No newline at end of file diff --git a/lib/browser/ParseUser.js b/lib/browser/ParseUser.js deleted file mode 100644 index f18f42820..000000000 --- a/lib/browser/ParseUser.js +++ /dev/null @@ -1,1150 +0,0 @@ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _stringify = require('babel-runtime/core-js/json/stringify'); - -var _stringify2 = _interopRequireDefault(_stringify); - -var _defineProperty = require('babel-runtime/core-js/object/define-property'); - -var _defineProperty2 = _interopRequireDefault(_defineProperty); - -var _typeof2 = require('babel-runtime/helpers/typeof'); - -var _typeof3 = _interopRequireDefault(_typeof2); - -var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); - -var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); - -var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); - -var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); - -var _createClass2 = require('babel-runtime/helpers/createClass'); - -var _createClass3 = _interopRequireDefault(_createClass2); - -var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); - -var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); - -var _get2 = require('babel-runtime/helpers/get'); - -var _get3 = _interopRequireDefault(_get2); - -var _inherits2 = require('babel-runtime/helpers/inherits'); - -var _inherits3 = _interopRequireDefault(_inherits2); - -var _CoreManager = require('./CoreManager'); - -var _CoreManager2 = _interopRequireDefault(_CoreManager); - -var _isRevocableSession = require('./isRevocableSession'); - -var _isRevocableSession2 = _interopRequireDefault(_isRevocableSession); - -var _ParseError = require('./ParseError'); - -var _ParseError2 = _interopRequireDefault(_ParseError); - -var _ParseObject2 = require('./ParseObject'); - -var _ParseObject3 = _interopRequireDefault(_ParseObject2); - -var _ParsePromise = require('./ParsePromise'); - -var _ParsePromise2 = _interopRequireDefault(_ParsePromise); - -var _ParseSession = require('./ParseSession'); - -var _ParseSession2 = _interopRequireDefault(_ParseSession); - -var _Storage = require('./Storage'); - -var _Storage2 = _interopRequireDefault(_Storage); - -function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { default: obj }; -} - -var CURRENT_USER_KEY = 'currentUser'; /** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * - */ - -var canUseCurrentUser = !_CoreManager2.default.get('IS_NODE'); -var currentUserCacheMatchesDisk = false; -var currentUserCache = null; - -var authProviders = {}; - -/** - * @class Parse.User - * @constructor - * - *A Parse.User object is a local representation of a user persisted to the - * Parse cloud. This class is a subclass of a Parse.Object, and retains the - * same functionality of a Parse.Object, but also extends it with various - * user specific methods, like authentication, signing up, and validation of - * uniqueness.
- */ - -var ParseUser = function (_ParseObject) { - (0, _inherits3.default)(ParseUser, _ParseObject); - - function ParseUser(attributes) { - (0, _classCallCheck3.default)(this, ParseUser); - - var _this = (0, _possibleConstructorReturn3.default)(this, (ParseUser.__proto__ || (0, _getPrototypeOf2.default)(ParseUser)).call(this, '_User')); - - if (attributes && (typeof attributes === 'undefined' ? 'undefined' : (0, _typeof3.default)(attributes)) === 'object') { - if (!_this.set(attributes || {})) { - throw new Error('Can\'t create an invalid Parse User'); - } - } - return _this; - } - - /** - * Request a revocable session token to replace the older style of token. - * @method _upgradeToRevocableSession - * @param {Object} options A Backbone-style options object. - * @return {Parse.Promise} A promise that is resolved when the replacement - * token has been fetched. - */ - - (0, _createClass3.default)(ParseUser, [{ - key: '_upgradeToRevocableSession', - value: function (options) { - options = options || {}; - - var upgradeOptions = {}; - if (options.hasOwnProperty('useMasterKey')) { - upgradeOptions.useMasterKey = options.useMasterKey; - } - - var controller = _CoreManager2.default.getUserController(); - return controller.upgradeToRevocableSession(this, upgradeOptions)._thenRunCallbacks(options); - } - - /** - * Unlike in the Android/iOS SDKs, logInWith is unnecessary, since you can - * call linkWith on the user (even if it doesn't exist yet on the server). - * @method _linkWith - */ - - }, { - key: '_linkWith', - value: function (provider, options) { - var _this2 = this; - - var authType; - if (typeof provider === 'string') { - authType = provider; - provider = authProviders[provider]; - } else { - authType = provider.getAuthType(); - } - if (options && options.hasOwnProperty('authData')) { - var authData = this.get('authData') || {}; - if ((typeof authData === 'undefined' ? 'undefined' : (0, _typeof3.default)(authData)) !== 'object') { - throw new Error('Invalid type: authData field should be an object'); - } - authData[authType] = options.authData; - - var controller = _CoreManager2.default.getUserController(); - return controller.linkWith(this, authData)._thenRunCallbacks(options, this); - } else { - var promise = new _ParsePromise2.default(); - provider.authenticate({ - success: function (provider, result) { - var opts = {}; - opts.authData = result; - if (options.success) { - opts.success = options.success; - } - if (options.error) { - opts.error = options.error; - } - _this2._linkWith(provider, opts).then(function () { - promise.resolve(_this2); - }, function (error) { - promise.reject(error); - }); - }, - error: function (provider, _error) { - if (typeof options.error === 'function') { - options.error(_this2, _error); - } - promise.reject(_error); - } - }); - return promise; - } - } - - /** - * Synchronizes auth data for a provider (e.g. puts the access token in the - * right place to be used by the Facebook SDK). - * @method _synchronizeAuthData - */ - - }, { - key: '_synchronizeAuthData', - value: function (provider) { - if (!this.isCurrent() || !provider) { - return; - } - var authType; - if (typeof provider === 'string') { - authType = provider; - provider = authProviders[authType]; - } else { - authType = provider.getAuthType(); - } - var authData = this.get('authData'); - if (!provider || !authData || (typeof authData === 'undefined' ? 'undefined' : (0, _typeof3.default)(authData)) !== 'object') { - return; - } - var success = provider.restoreAuthentication(authData[authType]); - if (!success) { - this._unlinkFrom(provider); - } - } - - /** - * Synchronizes authData for all providers. - * @method _synchronizeAllAuthData - */ - - }, { - key: '_synchronizeAllAuthData', - value: function () { - var authData = this.get('authData'); - if ((typeof authData === 'undefined' ? 'undefined' : (0, _typeof3.default)(authData)) !== 'object') { - return; - } - - for (var key in authData) { - this._synchronizeAuthData(key); - } - } - - /** - * Removes null values from authData (which exist temporarily for - * unlinking) - * @method _cleanupAuthData - */ - - }, { - key: '_cleanupAuthData', - value: function () { - if (!this.isCurrent()) { - return; - } - var authData = this.get('authData'); - if ((typeof authData === 'undefined' ? 'undefined' : (0, _typeof3.default)(authData)) !== 'object') { - return; - } - - for (var key in authData) { - if (!authData[key]) { - delete authData[key]; - } - } - } - - /** - * Unlinks a user from a service. - * @method _unlinkFrom - */ - - }, { - key: '_unlinkFrom', - value: function (provider, options) { - var _this3 = this; - - if (typeof provider === 'string') { - provider = authProviders[provider]; - } else { - provider.getAuthType(); - } - return this._linkWith(provider, { authData: null }).then(function () { - _this3._synchronizeAuthData(provider); - return _ParsePromise2.default.as(_this3); - })._thenRunCallbacks(options); - } - - /** - * Checks whether a user is linked to a service. - * @method _isLinked - */ - - }, { - key: '_isLinked', - value: function (provider) { - var authType; - if (typeof provider === 'string') { - authType = provider; - } else { - authType = provider.getAuthType(); - } - var authData = this.get('authData') || {}; - if ((typeof authData === 'undefined' ? 'undefined' : (0, _typeof3.default)(authData)) !== 'object') { - return false; - } - return !!authData[authType]; - } - - /** - * Deauthenticates all providers. - * @method _logOutWithAll - */ - - }, { - key: '_logOutWithAll', - value: function () { - var authData = this.get('authData'); - if ((typeof authData === 'undefined' ? 'undefined' : (0, _typeof3.default)(authData)) !== 'object') { - return; - } - - for (var key in authData) { - this._logOutWith(key); - } - } - - /** - * Deauthenticates a single provider (e.g. removing access tokens from the - * Facebook SDK). - * @method _logOutWith - */ - - }, { - key: '_logOutWith', - value: function (provider) { - if (!this.isCurrent()) { - return; - } - if (typeof provider === 'string') { - provider = authProviders[provider]; - } - if (provider && provider.deauthenticate) { - provider.deauthenticate(); - } - } - - /** - * Class instance method used to maintain specific keys when a fetch occurs. - * Used to ensure that the session token is not lost. - */ - - }, { - key: '_preserveFieldsOnFetch', - value: function () { - return { - sessionToken: this.get('sessionToken') - }; - } - - /** - * Returns true ifcurrent
would return this user.
- * @method isCurrent
- * @return {Boolean}
- */
-
- }, {
- key: 'isCurrent',
- value: function () {
- var current = ParseUser.current();
- return !!current && current.id === this.id;
- }
-
- /**
- * Returns get("username").
- * @method getUsername
- * @return {String}
- */
-
- }, {
- key: 'getUsername',
- value: function () {
- var username = this.get('username');
- if (username == null || typeof username === 'string') {
- return username;
- }
- return '';
- }
-
- /**
- * Calls set("username", username, options) and returns the result.
- * @method setUsername
- * @param {String} username
- * @param {Object} options A Backbone-style options object.
- * @return {Boolean}
- */
-
- }, {
- key: 'setUsername',
- value: function (username) {
- // Strip anonymity, even we do not support anonymous user in js SDK, we may
- // encounter anonymous user created by android/iOS in cloud code.
- var authData = this.get('authData');
- if (authData && (typeof authData === 'undefined' ? 'undefined' : (0, _typeof3.default)(authData)) === 'object' && authData.hasOwnProperty('anonymous')) {
- // We need to set anonymous to null instead of deleting it in order to remove it from Parse.
- authData.anonymous = null;
- }
- this.set('username', username);
- }
-
- /**
- * Calls set("password", password, options) and returns the result.
- * @method setPassword
- * @param {String} password
- * @param {Object} options A Backbone-style options object.
- * @return {Boolean}
- */
-
- }, {
- key: 'setPassword',
- value: function (password) {
- this.set('password', password);
- }
-
- /**
- * Returns get("email").
- * @method getEmail
- * @return {String}
- */
-
- }, {
- key: 'getEmail',
- value: function () {
- var email = this.get('email');
- if (email == null || typeof email === 'string') {
- return email;
- }
- return '';
- }
-
- /**
- * Calls set("email", email, options) and returns the result.
- * @method setEmail
- * @param {String} email
- * @param {Object} options A Backbone-style options object.
- * @return {Boolean}
- */
-
- }, {
- key: 'setEmail',
- value: function (email) {
- this.set('email', email);
- }
-
- /**
- * Returns the session token for this user, if the user has been logged in,
- * or if it is the result of a query with the master key. Otherwise, returns
- * undefined.
- * @method getSessionToken
- * @return {String} the session token, or undefined
- */
-
- }, {
- key: 'getSessionToken',
- value: function () {
- var token = this.get('sessionToken');
- if (token == null || typeof token === 'string') {
- return token;
- }
- return '';
- }
-
- /**
- * Checks whether this user is the current user and has been authenticated.
- * @method authenticated
- * @return (Boolean) whether this user is the current user and is logged in.
- */
-
- }, {
- key: 'authenticated',
- value: function () {
- var current = ParseUser.current();
- return !!this.get('sessionToken') && !!current && current.id === this.id;
- }
-
- /**
- * Signs up a new user. You should call this instead of save for
- * new Parse.Users. This will create a new Parse.User on the server, and
- * also persist the session on disk so that you can access the user using
- * current
.
- *
- * A username and password must be set before calling signUp.
- * - *Calls options.success or options.error on completion.
- * - * @method signUp - * @param {Object} attrs Extra fields to set on the new user, or null. - * @param {Object} options A Backbone-style options object. - * @return {Parse.Promise} A promise that is fulfilled when the signup - * finishes. - */ - - }, { - key: 'signUp', - value: function (attrs, options) { - options = options || {}; - - var signupOptions = {}; - if (options.hasOwnProperty('useMasterKey')) { - signupOptions.useMasterKey = options.useMasterKey; - } - if (options.hasOwnProperty('installationId')) { - signupOptions.installationId = options.installationId; - } - - var controller = _CoreManager2.default.getUserController(); - return controller.signUp(this, attrs, signupOptions)._thenRunCallbacks(options, this); - } - - /** - * Logs in a Parse.User. On success, this saves the session to disk, - * so you can retrieve the currently logged in user using - *current
.
- *
- * A username and password must be set before calling logIn.
- * - *Calls options.success or options.error on completion.
- * - * @method logIn - * @param {Object} options A Backbone-style options object. - * @return {Parse.Promise} A promise that is fulfilled with the user when - * the login is complete. - */ - - }, { - key: 'logIn', - value: function (options) { - options = options || {}; - - var loginOptions = {}; - if (options.hasOwnProperty('useMasterKey')) { - loginOptions.useMasterKey = options.useMasterKey; - } - if (options.hasOwnProperty('installationId')) { - loginOptions.installationId = options.installationId; - } - - var controller = _CoreManager2.default.getUserController(); - return controller.logIn(this, loginOptions)._thenRunCallbacks(options, this); - } - - /** - * Wrap the default save behavior with functionality to save to local - * storage if this is current user. - */ - - }, { - key: 'save', - value: function () { - var _this4 = this; - - for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { - args[_key] = arguments[_key]; - } - - return (0, _get3.default)(ParseUser.prototype.__proto__ || (0, _getPrototypeOf2.default)(ParseUser.prototype), 'save', this).apply(this, args).then(function () { - if (_this4.isCurrent()) { - return _CoreManager2.default.getUserController().updateUserOnDisk(_this4); - } - return _this4; - }); - } - - /** - * Wrap the default destroy behavior with functionality that logs out - * the current user when it is destroyed - */ - - }, { - key: 'destroy', - value: function () { - var _this5 = this; - - for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { - args[_key2] = arguments[_key2]; - } - - return (0, _get3.default)(ParseUser.prototype.__proto__ || (0, _getPrototypeOf2.default)(ParseUser.prototype), 'destroy', this).apply(this, args).then(function () { - if (_this5.isCurrent()) { - return _CoreManager2.default.getUserController().removeUserFromDisk(); - } - return _this5; - }); - } - - /** - * Wrap the default fetch behavior with functionality to save to local - * storage if this is current user. - */ - - }, { - key: 'fetch', - value: function () { - var _this6 = this; - - for (var _len3 = arguments.length, args = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) { - args[_key3] = arguments[_key3]; - } - - return (0, _get3.default)(ParseUser.prototype.__proto__ || (0, _getPrototypeOf2.default)(ParseUser.prototype), 'fetch', this).apply(this, args).then(function () { - if (_this6.isCurrent()) { - return _CoreManager2.default.getUserController().updateUserOnDisk(_this6); - } - return _this6; - }); - } - }], [{ - key: 'readOnlyAttributes', - value: function () { - return ['sessionToken']; - } - - /** - * Adds functionality to the existing Parse.User class - * @method extend - * @param {Object} protoProps A set of properties to add to the prototype - * @param {Object} classProps A set of static properties to add to the class - * @static - * @return {Class} The newly extended Parse.User class - */ - - }, { - key: 'extend', - value: function (protoProps, classProps) { - if (protoProps) { - for (var prop in protoProps) { - if (prop !== 'className') { - (0, _defineProperty2.default)(ParseUser.prototype, prop, { - value: protoProps[prop], - enumerable: false, - writable: true, - configurable: true - }); - } - } - } - - if (classProps) { - for (var prop in classProps) { - if (prop !== 'className') { - (0, _defineProperty2.default)(ParseUser, prop, { - value: classProps[prop], - enumerable: false, - writable: true, - configurable: true - }); - } - } - } - - return ParseUser; - } - - /** - * Retrieves the currently logged in ParseUser with a valid session, - * either from memory or localStorage, if necessary. - * @method current - * @static - * @return {Parse.Object} The currently logged in Parse.User. - */ - - }, { - key: 'current', - value: function () { - if (!canUseCurrentUser) { - return null; - } - var controller = _CoreManager2.default.getUserController(); - return controller.currentUser(); - } - - /** - * Retrieves the currently logged in ParseUser from asynchronous Storage. - * @method currentAsync - * @static - * @return {Parse.Promise} A Promise that is resolved with the currently - * logged in Parse User - */ - - }, { - key: 'currentAsync', - value: function () { - if (!canUseCurrentUser) { - return _ParsePromise2.default.as(null); - } - var controller = _CoreManager2.default.getUserController(); - return controller.currentUserAsync(); - } - - /** - * Signs up a new user with a username (or email) and password. - * This will create a new Parse.User on the server, and also persist the - * session in localStorage so that you can access the user using - * {@link #current}. - * - *Calls options.success or options.error on completion.
- * - * @method signUp - * @param {String} username The username (or email) to sign up with. - * @param {String} password The password to sign up with. - * @param {Object} attrs Extra fields to set on the new user. - * @param {Object} options A Backbone-style options object. - * @static - * @return {Parse.Promise} A promise that is fulfilled with the user when - * the signup completes. - */ - - }, { - key: 'signUp', - value: function (username, password, attrs, options) { - attrs = attrs || {}; - attrs.username = username; - attrs.password = password; - var user = new ParseUser(attrs); - return user.signUp({}, options); - } - - /** - * Logs in a user with a username (or email) and password. On success, this - * saves the session to disk, so you can retrieve the currently logged in - * user usingcurrent
.
- *
- * Calls options.success or options.error on completion.
- * - * @method logIn - * @param {String} username The username (or email) to log in with. - * @param {String} password The password to log in with. - * @param {Object} options A Backbone-style options object. - * @static - * @return {Parse.Promise} A promise that is fulfilled with the user when - * the login completes. - */ - - }, { - key: 'logIn', - value: function (username, password, options) { - if (typeof username !== 'string') { - return _ParsePromise2.default.error(new _ParseError2.default(_ParseError2.default.OTHER_CAUSE, 'Username must be a string.')); - } else if (typeof password !== 'string') { - return _ParsePromise2.default.error(new _ParseError2.default(_ParseError2.default.OTHER_CAUSE, 'Password must be a string.')); - } - var user = new ParseUser(); - user._finishFetch({ username: username, password: password }); - return user.logIn(options); - } - - /** - * Logs in a user with a session token. On success, this saves the session - * to disk, so you can retrieve the currently logged in user using - *current
.
- *
- * Calls options.success or options.error on completion.
- * - * @method become - * @param {String} sessionToken The sessionToken to log in with. - * @param {Object} options A Backbone-style options object. - * @static - * @return {Parse.Promise} A promise that is fulfilled with the user when - * the login completes. - */ - - }, { - key: 'become', - value: function (sessionToken, options) { - if (!canUseCurrentUser) { - throw new Error('It is not memory-safe to become a user in a server environment'); - } - options = options || {}; - - var becomeOptions = { - sessionToken: sessionToken - }; - if (options.hasOwnProperty('useMasterKey')) { - becomeOptions.useMasterKey = options.useMasterKey; - } - - var controller = _CoreManager2.default.getUserController(); - return controller.become(becomeOptions)._thenRunCallbacks(options); - } - }, { - key: 'logInWith', - value: function (provider, options) { - return ParseUser._logInWith(provider, options); - } - - /** - * Logs out the currently logged in user session. This will remove the - * session from disk, log out of linked services, and future calls to - *current
will return null
.
- * @method logOut
- * @static
- * @return {Parse.Promise} A promise that is resolved when the session is
- * destroyed on the server.
- */
-
- }, {
- key: 'logOut',
- value: function () {
- if (!canUseCurrentUser) {
- throw new Error('There is no current user user on a node.js server environment.');
- }
-
- var controller = _CoreManager2.default.getUserController();
- return controller.logOut();
- }
-
- /**
- * Requests a password reset email to be sent to the specified email address
- * associated with the user account. This email allows the user to securely
- * reset their password on the Parse site.
- *
- * Calls options.success or options.error on completion.
- * - * @method requestPasswordReset - * @param {String} email The email address associated with the user that - * forgot their password. - * @param {Object} options A Backbone-style options object. - * @static - */ - - }, { - key: 'requestPasswordReset', - value: function (email, options) { - options = options || {}; - - var requestOptions = {}; - if (options.hasOwnProperty('useMasterKey')) { - requestOptions.useMasterKey = options.useMasterKey; - } - - var controller = _CoreManager2.default.getUserController(); - return controller.requestPasswordReset(email, requestOptions)._thenRunCallbacks(options); - } - - /** - * Allow someone to define a custom User class without className - * being rewritten to _User. The default behavior is to rewrite - * User to _User for legacy reasons. This allows developers to - * override that behavior. - * - * @method allowCustomUserClass - * @param {Boolean} isAllowed Whether or not to allow custom User class - * @static - */ - - }, { - key: 'allowCustomUserClass', - value: function (isAllowed) { - _CoreManager2.default.set('PERFORM_USER_REWRITE', !isAllowed); - } - - /** - * Allows a legacy application to start using revocable sessions. If the - * current session token is not revocable, a request will be made for a new, - * revocable session. - * It is not necessary to call this method from cloud code unless you are - * handling user signup or login from the server side. In a cloud code call, - * this function will not attempt to upgrade the current token. - * @method enableRevocableSession - * @param {Object} options A Backbone-style options object. - * @static - * @return {Parse.Promise} A promise that is resolved when the process has - * completed. If a replacement session token is requested, the promise - * will be resolved after a new token has been fetched. - */ - - }, { - key: 'enableRevocableSession', - value: function (options) { - options = options || {}; - _CoreManager2.default.set('FORCE_REVOCABLE_SESSION', true); - if (canUseCurrentUser) { - var current = ParseUser.current(); - if (current) { - return current._upgradeToRevocableSession(options); - } - } - return _ParsePromise2.default.as()._thenRunCallbacks(options); - } - - /** - * Enables the use of become or the current user in a server - * environment. These features are disabled by default, since they depend on - * global objects that are not memory-safe for most servers. - * @method enableUnsafeCurrentUser - * @static - */ - - }, { - key: 'enableUnsafeCurrentUser', - value: function () { - canUseCurrentUser = true; - } - - /** - * Disables the use of become or the current user in any environment. - * These features are disabled on servers by default, since they depend on - * global objects that are not memory-safe for most servers. - * @method disableUnsafeCurrentUser - * @static - */ - - }, { - key: 'disableUnsafeCurrentUser', - value: function () { - canUseCurrentUser = false; - } - }, { - key: '_registerAuthenticationProvider', - value: function (provider) { - authProviders[provider.getAuthType()] = provider; - // Synchronize the current user with the auth provider. - ParseUser.currentAsync().then(function (current) { - if (current) { - current._synchronizeAuthData(provider.getAuthType()); - } - }); - } - }, { - key: '_logInWith', - value: function (provider, options) { - var user = new ParseUser(); - return user._linkWith(provider, options); - } - }, { - key: '_clearCache', - value: function () { - currentUserCache = null; - currentUserCacheMatchesDisk = false; - } - }, { - key: '_setCurrentUserCache', - value: function (user) { - currentUserCache = user; - } - }]); - return ParseUser; -}(_ParseObject3.default); - -exports.default = ParseUser; - -_ParseObject3.default.registerSubclass('_User', ParseUser); - -var DefaultController = { - updateUserOnDisk: function (user) { - var path = _Storage2.default.generatePath(CURRENT_USER_KEY); - var json = user.toJSON(); - json.className = '_User'; - return _Storage2.default.setItemAsync(path, (0, _stringify2.default)(json)).then(function () { - return user; - }); - }, - removeUserFromDisk: function () { - var path = _Storage2.default.generatePath(CURRENT_USER_KEY); - currentUserCacheMatchesDisk = true; - currentUserCache = null; - return _Storage2.default.removeItemAsync(path); - }, - setCurrentUser: function (user) { - currentUserCache = user; - user._cleanupAuthData(); - user._synchronizeAllAuthData(); - return DefaultController.updateUserOnDisk(user); - }, - currentUser: function () { - if (currentUserCache) { - return currentUserCache; - } - if (currentUserCacheMatchesDisk) { - return null; - } - if (_Storage2.default.async()) { - throw new Error('Cannot call currentUser() when using a platform with an async ' + 'storage system. Call currentUserAsync() instead.'); - } - var path = _Storage2.default.generatePath(CURRENT_USER_KEY); - var userData = _Storage2.default.getItem(path); - currentUserCacheMatchesDisk = true; - if (!userData) { - currentUserCache = null; - return null; - } - userData = JSON.parse(userData); - if (!userData.className) { - userData.className = '_User'; - } - if (userData._id) { - if (userData.objectId !== userData._id) { - userData.objectId = userData._id; - } - delete userData._id; - } - if (userData._sessionToken) { - userData.sessionToken = userData._sessionToken; - delete userData._sessionToken; - } - var current = _ParseObject3.default.fromJSON(userData); - currentUserCache = current; - current._synchronizeAllAuthData(); - return current; - }, - currentUserAsync: function () { - if (currentUserCache) { - return _ParsePromise2.default.as(currentUserCache); - } - if (currentUserCacheMatchesDisk) { - return _ParsePromise2.default.as(null); - } - var path = _Storage2.default.generatePath(CURRENT_USER_KEY); - return _Storage2.default.getItemAsync(path).then(function (userData) { - currentUserCacheMatchesDisk = true; - if (!userData) { - currentUserCache = null; - return _ParsePromise2.default.as(null); - } - userData = JSON.parse(userData); - if (!userData.className) { - userData.className = '_User'; - } - if (userData._id) { - if (userData.objectId !== userData._id) { - userData.objectId = userData._id; - } - delete userData._id; - } - if (userData._sessionToken) { - userData.sessionToken = userData._sessionToken; - delete userData._sessionToken; - } - var current = _ParseObject3.default.fromJSON(userData); - currentUserCache = current; - current._synchronizeAllAuthData(); - return _ParsePromise2.default.as(current); - }); - }, - signUp: function (user, attrs, options) { - var username = attrs && attrs.username || user.get('username'); - var password = attrs && attrs.password || user.get('password'); - - if (!username || !username.length) { - return _ParsePromise2.default.error(new _ParseError2.default(_ParseError2.default.OTHER_CAUSE, 'Cannot sign up user with an empty name.')); - } - if (!password || !password.length) { - return _ParsePromise2.default.error(new _ParseError2.default(_ParseError2.default.OTHER_CAUSE, 'Cannot sign up user with an empty password.')); - } - - return user.save(attrs, options).then(function () { - // Clear the password field - user._finishFetch({ password: undefined }); - - if (canUseCurrentUser) { - return DefaultController.setCurrentUser(user); - } - return user; - }); - }, - logIn: function (user, options) { - var RESTController = _CoreManager2.default.getRESTController(); - var stateController = _CoreManager2.default.getObjectStateController(); - var auth = { - username: user.get('username'), - password: user.get('password') - }; - return RESTController.request('GET', 'login', auth, options).then(function (response, status) { - user._migrateId(response.objectId); - user._setExisted(true); - stateController.setPendingOp(user._getStateIdentifier(), 'username', undefined); - stateController.setPendingOp(user._getStateIdentifier(), 'password', undefined); - response.password = undefined; - user._finishFetch(response); - if (!canUseCurrentUser) { - // We can't set the current user, so just return the one we logged in - return _ParsePromise2.default.as(user); - } - return DefaultController.setCurrentUser(user); - }); - }, - become: function (options) { - var user = new ParseUser(); - var RESTController = _CoreManager2.default.getRESTController(); - return RESTController.request('GET', 'users/me', {}, options).then(function (response, status) { - user._finishFetch(response); - user._setExisted(true); - return DefaultController.setCurrentUser(user); - }); - }, - logOut: function () { - return DefaultController.currentUserAsync().then(function (currentUser) { - var path = _Storage2.default.generatePath(CURRENT_USER_KEY); - var promise = _Storage2.default.removeItemAsync(path); - var RESTController = _CoreManager2.default.getRESTController(); - if (currentUser !== null) { - var currentSession = currentUser.getSessionToken(); - if (currentSession && (0, _isRevocableSession2.default)(currentSession)) { - promise = promise.then(function () { - return RESTController.request('POST', 'logout', {}, { sessionToken: currentSession }); - }); - } - currentUser._logOutWithAll(); - currentUser._finishFetch({ sessionToken: undefined }); - } - currentUserCacheMatchesDisk = true; - currentUserCache = null; - - return promise; - }); - }, - requestPasswordReset: function (email, options) { - var RESTController = _CoreManager2.default.getRESTController(); - return RESTController.request('POST', 'requestPasswordReset', { email: email }, options); - }, - upgradeToRevocableSession: function (user, options) { - var token = user.getSessionToken(); - if (!token) { - return _ParsePromise2.default.error(new _ParseError2.default(_ParseError2.default.SESSION_MISSING, 'Cannot upgrade a user with no session token')); - } - - options.sessionToken = token; - - var RESTController = _CoreManager2.default.getRESTController(); - return RESTController.request('POST', 'upgradeToRevocableSession', {}, options).then(function (result) { - var session = new _ParseSession2.default(); - session._finishFetch(result); - user._finishFetch({ sessionToken: session.getSessionToken() }); - if (user.isCurrent()) { - return DefaultController.setCurrentUser(user); - } - return _ParsePromise2.default.as(user); - }); - }, - linkWith: function (user, authData) { - return user.save({ authData: authData }).then(function () { - if (canUseCurrentUser) { - return DefaultController.setCurrentUser(user); - } - return user; - }); - } -}; - -_CoreManager2.default.setUserController(DefaultController); \ No newline at end of file diff --git a/lib/browser/Push.js b/lib/browser/Push.js deleted file mode 100644 index cfb740767..000000000 --- a/lib/browser/Push.js +++ /dev/null @@ -1,98 +0,0 @@ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _typeof2 = require('babel-runtime/helpers/typeof'); - -var _typeof3 = _interopRequireDefault(_typeof2); - -exports.send = send; - -var _CoreManager = require('./CoreManager'); - -var _CoreManager2 = _interopRequireDefault(_CoreManager); - -var _ParseQuery = require('./ParseQuery'); - -var _ParseQuery2 = _interopRequireDefault(_ParseQuery); - -function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { default: obj }; -} - -/** - * Contains functions to deal with Push in Parse. - * @class Parse.Push - * @static - */ - -/** - * Sends a push notification. - * @method send - * @param {Object} data - The data of the push notification. Valid fields - * are: - *- * var dimensions = { - * gender: 'm', - * source: 'web', - * dayType: 'weekend' - * }; - * Parse.Analytics.track('signup', dimensions); - *- * - * There is a default limit of 8 dimensions per event tracked. - * - * @method track - * @param {String} name The name of the custom event to report to Parse as - * having happened. - * @param {Object} dimensions The dictionary of information by which to - * segment this event. - * @param {Object} options A Backbone-style callback object. - * @return {Parse.Promise} A promise that is resolved when the round-trip - * to the server completes. - */ -function track(name, dimensions, options) { - name = name || ''; - name = name.replace(/^\s*/, ''); - name = name.replace(/\s*$/, ''); - if (name.length === 0) { - throw new TypeError('A name for the custom event must be provided'); - } - - for (var key in dimensions) { - if (typeof key !== 'string' || typeof dimensions[key] !== 'string') { - throw new TypeError('track() dimensions expects keys and values of type "string".'); - } - } - - options = options || {}; - return _CoreManager2.default.getAnalyticsController().track(name, dimensions)._thenRunCallbacks(options); -} /** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * - */ - -var DefaultController = { - track: function (name, dimensions) { - var RESTController = _CoreManager2.default.getRESTController(); - return RESTController.request('POST', 'events/' + name, { dimensions: dimensions }); - } -}; - -_CoreManager2.default.setAnalyticsController(DefaultController); \ No newline at end of file diff --git a/lib/node/Cloud.js b/lib/node/Cloud.js deleted file mode 100644 index 72c333750..000000000 --- a/lib/node/Cloud.js +++ /dev/null @@ -1,109 +0,0 @@ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.run = run; - -var _CoreManager = require('./CoreManager'); - -var _CoreManager2 = _interopRequireDefault(_CoreManager); - -var _decode = require('./decode'); - -var _decode2 = _interopRequireDefault(_decode); - -var _encode = require('./encode'); - -var _encode2 = _interopRequireDefault(_encode); - -var _ParseError = require('./ParseError'); - -var _ParseError2 = _interopRequireDefault(_ParseError); - -var _ParsePromise = require('./ParsePromise'); - -var _ParsePromise2 = _interopRequireDefault(_ParsePromise); - -function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { default: obj }; -} - -/** - * Contains functions for calling and declaring - * cloud functions. - *
- * Some functions are only available from Cloud Code. - *
- * - * @class Parse.Cloud - * @static - */ - -/** - * Makes a call to a cloud function. - * @method run - * @param {String} name The function name. - * @param {Object} data The parameters to send to the cloud function. - * @param {Object} options A Backbone-style options object - * options.success, if set, should be a function to handle a successful - * call to a cloud function. options.error should be a function that - * handles an error running the cloud function. Both functions are - * optional. Both functions take a single argument. - * @return {Parse.Promise} A promise that will be resolved with the result - * of the function. - */ -function run(name, data, options) { - options = options || {}; - - if (typeof name !== 'string' || name.length === 0) { - throw new TypeError('Cloud function name must be a string.'); - } - - var requestOptions = {}; - if (options.useMasterKey) { - requestOptions.useMasterKey = options.useMasterKey; - } - if (options.sessionToken) { - requestOptions.sessionToken = options.sessionToken; - } - - return _CoreManager2.default.getCloudController().run(name, data, requestOptions)._thenRunCallbacks(options); -} /** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * - */ - -var DefaultController = { - run: function (name, data, options) { - var RESTController = _CoreManager2.default.getRESTController(); - - var payload = (0, _encode2.default)(data, true); - - var requestOptions = {}; - if (options.hasOwnProperty('useMasterKey')) { - requestOptions.useMasterKey = options.useMasterKey; - } - if (options.hasOwnProperty('sessionToken')) { - requestOptions.sessionToken = options.sessionToken; - } - - var request = RESTController.request('POST', 'functions/' + name, payload, requestOptions); - - return request.then(function (res) { - var decoded = (0, _decode2.default)(res); - if (decoded && decoded.hasOwnProperty('result')) { - return _ParsePromise2.default.as(decoded.result); - } - return _ParsePromise2.default.error(new _ParseError2.default(_ParseError2.default.INVALID_JSON, 'The server returned an invalid response.')); - })._thenRunCallbacks(options); - } -}; - -_CoreManager2.default.setCloudController(DefaultController); \ No newline at end of file diff --git a/lib/node/CoreManager.js b/lib/node/CoreManager.js deleted file mode 100644 index 20d66a5f1..000000000 --- a/lib/node/CoreManager.js +++ /dev/null @@ -1,161 +0,0 @@ -'use strict'; - -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * - */ - -var config = { - // Defaults - IS_NODE: typeof process !== 'undefined' && !!process.versions && !!process.versions.node && !process.versions.electron, - REQUEST_ATTEMPT_LIMIT: 5, - SERVER_URL: 'https://api.parse.com/1', - LIVEQUERY_SERVER_URL: null, - VERSION: 'js' + '1.9.2', - APPLICATION_ID: null, - JAVASCRIPT_KEY: null, - MASTER_KEY: null, - USE_MASTER_KEY: false, - PERFORM_USER_REWRITE: true, - FORCE_REVOCABLE_SESSION: false -}; - -function requireMethods(name, methods, controller) { - methods.forEach(function (func) { - if (typeof controller[func] !== 'function') { - throw new Error(name + ' must implement ' + func + '()'); - } - }); -} - -module.exports = { - get: function (key) { - if (config.hasOwnProperty(key)) { - return config[key]; - } - throw new Error('Configuration key not found: ' + key); - }, - - set: function (key, value) { - config[key] = value; - }, - - /* Specialized Controller Setters/Getters */ - - setAnalyticsController: function (controller) { - requireMethods('AnalyticsController', ['track'], controller); - config['AnalyticsController'] = controller; - }, - getAnalyticsController: function () { - return config['AnalyticsController']; - }, - setCloudController: function (controller) { - requireMethods('CloudController', ['run'], controller); - config['CloudController'] = controller; - }, - getCloudController: function () { - return config['CloudController']; - }, - setConfigController: function (controller) { - requireMethods('ConfigController', ['current', 'get'], controller); - config['ConfigController'] = controller; - }, - getConfigController: function () { - return config['ConfigController']; - }, - setFileController: function (controller) { - requireMethods('FileController', ['saveFile', 'saveBase64'], controller); - config['FileController'] = controller; - }, - getFileController: function () { - return config['FileController']; - }, - setInstallationController: function (controller) { - requireMethods('InstallationController', ['currentInstallationId'], controller); - config['InstallationController'] = controller; - }, - getInstallationController: function () { - return config['InstallationController']; - }, - setObjectController: function (controller) { - requireMethods('ObjectController', ['save', 'fetch', 'destroy'], controller); - config['ObjectController'] = controller; - }, - getObjectController: function () { - return config['ObjectController']; - }, - setObjectStateController: function (controller) { - requireMethods('ObjectStateController', ['getState', 'initializeState', 'removeState', 'getServerData', 'setServerData', 'getPendingOps', 'setPendingOp', 'pushPendingState', 'popPendingState', 'mergeFirstPendingState', 'getObjectCache', 'estimateAttribute', 'estimateAttributes', 'commitServerChanges', 'enqueueTask', 'clearAllState'], controller); - - config['ObjectStateController'] = controller; - }, - getObjectStateController: function () { - return config['ObjectStateController']; - }, - setPushController: function (controller) { - requireMethods('PushController', ['send'], controller); - config['PushController'] = controller; - }, - getPushController: function () { - return config['PushController']; - }, - setQueryController: function (controller) { - requireMethods('QueryController', ['find'], controller); - config['QueryController'] = controller; - }, - getQueryController: function () { - return config['QueryController']; - }, - setRESTController: function (controller) { - requireMethods('RESTController', ['request', 'ajax'], controller); - config['RESTController'] = controller; - }, - getRESTController: function () { - return config['RESTController']; - }, - setSessionController: function (controller) { - requireMethods('SessionController', ['getSession'], controller); - config['SessionController'] = controller; - }, - getSessionController: function () { - return config['SessionController']; - }, - setStorageController: function (controller) { - if (controller.async) { - requireMethods('An async StorageController', ['getItemAsync', 'setItemAsync', 'removeItemAsync'], controller); - } else { - requireMethods('A synchronous StorageController', ['getItem', 'setItem', 'removeItem'], controller); - } - config['StorageController'] = controller; - }, - getStorageController: function () { - return config['StorageController']; - }, - setUserController: function (controller) { - requireMethods('UserController', ['setCurrentUser', 'currentUser', 'currentUserAsync', 'signUp', 'logIn', 'become', 'logOut', 'requestPasswordReset', 'upgradeToRevocableSession', 'linkWith'], controller); - config['UserController'] = controller; - }, - getUserController: function () { - return config['UserController']; - }, - setLiveQueryController: function (controller) { - requireMethods('LiveQueryController', ['subscribe', 'unsubscribe', 'open', 'close'], controller); - config['LiveQueryController'] = controller; - }, - getLiveQueryController: function () { - return config['LiveQueryController']; - }, - setHooksController: function (controller) { - requireMethods('HooksController', ['create', 'get', 'update', 'remove'], controller); - config['HooksController'] = controller; - }, - getHooksController: function () { - return config['HooksController']; - } -}; \ No newline at end of file diff --git a/lib/node/EventEmitter.js b/lib/node/EventEmitter.js deleted file mode 100644 index e9414f0bf..000000000 --- a/lib/node/EventEmitter.js +++ /dev/null @@ -1,15 +0,0 @@ -'use strict'; - -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * This is a simple wrapper to unify EventEmitter implementations across platforms. - */ - -module.exports = require('events').EventEmitter; -var EventEmitter; \ No newline at end of file diff --git a/lib/node/FacebookUtils.js b/lib/node/FacebookUtils.js deleted file mode 100644 index bb8b7ef51..000000000 --- a/lib/node/FacebookUtils.js +++ /dev/null @@ -1,243 +0,0 @@ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _parseDate = require('./parseDate'); - -var _parseDate2 = _interopRequireDefault(_parseDate); - -var _ParseUser = require('./ParseUser'); - -var _ParseUser2 = _interopRequireDefault(_ParseUser); - -function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { default: obj }; -} - -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * -weak - */ - -var PUBLIC_KEY = "*"; - -var initialized = false; -var requestedPermissions; -var initOptions; -var provider = { - authenticate: function (options) { - var _this = this; - - if (typeof FB === 'undefined') { - options.error(this, 'Facebook SDK not found.'); - } - FB.login(function (response) { - if (response.authResponse) { - if (options.success) { - options.success(_this, { - id: response.authResponse.userID, - access_token: response.authResponse.accessToken, - expiration_date: new Date(response.authResponse.expiresIn * 1000 + new Date().getTime()).toJSON() - }); - } - } else { - if (options.error) { - options.error(_this, response); - } - } - }, { - scope: requestedPermissions - }); - }, - restoreAuthentication: function (authData) { - if (authData) { - var expiration = (0, _parseDate2.default)(authData.expiration_date); - var expiresIn = expiration ? (expiration.getTime() - new Date().getTime()) / 1000 : 0; - - var authResponse = { - userID: authData.id, - accessToken: authData.access_token, - expiresIn: expiresIn - }; - var newOptions = {}; - if (initOptions) { - for (var key in initOptions) { - newOptions[key] = initOptions[key]; - } - } - newOptions.authResponse = authResponse; - - // Suppress checks for login status from the browser. - newOptions.status = false; - - // If the user doesn't match the one known by the FB SDK, log out. - // Most of the time, the users will match -- it's only in cases where - // the FB SDK knows of a different user than the one being restored - // from a Parse User that logged in with username/password. - var existingResponse = FB.getAuthResponse(); - if (existingResponse && existingResponse.userID !== authResponse.userID) { - FB.logout(); - } - - FB.init(newOptions); - } - return true; - }, - getAuthType: function () { - return 'facebook'; - }, - deauthenticate: function () { - this.restoreAuthentication(null); - } -}; - -/** - * Provides a set of utilities for using Parse with Facebook. - * @class Parse.FacebookUtils - * @static - */ -var FacebookUtils = { - /** - * Initializes Parse Facebook integration. Call this function after you - * have loaded the Facebook Javascript SDK with the same parameters - * as you would pass to
- *
- * FB.init()
. Parse.FacebookUtils will invoke FB.init() for you
- * with these arguments.
- *
- * @method init
- * @param {Object} options Facebook options argument as described here:
- *
- * FB.init(). The status flag will be coerced to 'false' because it
- * interferes with Parse Facebook integration. Call FB.getLoginStatus()
- * explicitly if this behavior is required by your application.
- */
- init: function (options) {
- if (typeof FB === 'undefined') {
- throw new Error('The Facebook JavaScript SDK must be loaded before calling init.');
- }
- initOptions = {};
- if (options) {
- for (var key in options) {
- initOptions[key] = options[key];
- }
- }
- if (initOptions.status && typeof console !== 'undefined') {
- var warn = console.warn || console.log || function () {};
- warn.call(console, 'The "status" flag passed into' + ' FB.init, when set to true, can interfere with Parse Facebook' + ' integration, so it has been suppressed. Please call' + ' FB.getLoginStatus() explicitly if you require this behavior.');
- }
- initOptions.status = false;
- FB.init(initOptions);
- _ParseUser2.default._registerAuthenticationProvider(provider);
- initialized = true;
- },
-
- /**
- * Gets whether the user has their account linked to Facebook.
- *
- * @method isLinked
- * @param {Parse.User} user User to check for a facebook link.
- * The user must be logged in on this device.
- * @return {Boolean} true
if the user has their account
- * linked to Facebook.
- */
- isLinked: function (user) {
- return user._isLinked('facebook');
- },
-
- /**
- * Logs in a user using Facebook. This method delegates to the Facebook
- * SDK to authenticate the user, and then automatically logs in (or
- * creates, in the case where it is a new user) a Parse.User.
- *
- * @method logIn
- * @param {String, Object} permissions The permissions required for Facebook
- * log in. This is a comma-separated string of permissions.
- * Alternatively, supply a Facebook authData object as described in our
- * REST API docs if you want to handle getting facebook auth tokens
- * yourself.
- * @param {Object} options Standard options object with success and error
- * callbacks.
- */
- logIn: function (permissions, options) {
- if (!permissions || typeof permissions === 'string') {
- if (!initialized) {
- throw new Error('You must initialize FacebookUtils before calling logIn.');
- }
- requestedPermissions = permissions;
- return _ParseUser2.default._logInWith('facebook', options);
- } else {
- var newOptions = {};
- if (options) {
- for (var key in options) {
- newOptions[key] = options[key];
- }
- }
- newOptions.authData = permissions;
- return _ParseUser2.default._logInWith('facebook', newOptions);
- }
- },
-
- /**
- * Links Facebook to an existing PFUser. This method delegates to the
- * Facebook SDK to authenticate the user, and then automatically links
- * the account to the Parse.User.
- *
- * @method link
- * @param {Parse.User} user User to link to Facebook. This must be the
- * current user.
- * @param {String, Object} permissions The permissions required for Facebook
- * log in. This is a comma-separated string of permissions.
- * Alternatively, supply a Facebook authData object as described in our
- * REST API docs if you want to handle getting facebook auth tokens
- * yourself.
- * @param {Object} options Standard options object with success and error
- * callbacks.
- */
- link: function (user, permissions, options) {
- if (!permissions || typeof permissions === 'string') {
- if (!initialized) {
- throw new Error('You must initialize FacebookUtils before calling link.');
- }
- requestedPermissions = permissions;
- return user._linkWith('facebook', options);
- } else {
- var newOptions = {};
- if (options) {
- for (var key in options) {
- newOptions[key] = options[key];
- }
- }
- newOptions.authData = permissions;
- return user._linkWith('facebook', newOptions);
- }
- },
-
- /**
- * Unlinks the Parse.User from a Facebook account.
- *
- * @method unlink
- * @param {Parse.User} user User to unlink from Facebook. This must be the
- * current user.
- * @param {Object} options Standard options object with success and error
- * callbacks.
- */
- unlink: function (user, options) {
- if (!initialized) {
- throw new Error('You must initialize FacebookUtils before calling unlink.');
- }
- return user._unlinkFrom('facebook', options);
- }
-};
-
-exports.default = FacebookUtils;
\ No newline at end of file
diff --git a/lib/node/InstallationController.js b/lib/node/InstallationController.js
deleted file mode 100644
index 7b01e3cad..000000000
--- a/lib/node/InstallationController.js
+++ /dev/null
@@ -1,64 +0,0 @@
-'use strict';
-
-var _CoreManager = require('./CoreManager');
-
-var _CoreManager2 = _interopRequireDefault(_CoreManager);
-
-var _ParsePromise = require('./ParsePromise');
-
-var _ParsePromise2 = _interopRequireDefault(_ParsePromise);
-
-var _Storage = require('./Storage');
-
-var _Storage2 = _interopRequireDefault(_Storage);
-
-function _interopRequireDefault(obj) {
- return obj && obj.__esModule ? obj : { default: obj };
-}
-
-var iidCache = null; /**
- * Copyright (c) 2015-present, Parse, LLC.
- * All rights reserved.
- *
- * This source code is licensed under the BSD-style license found in the
- * LICENSE file in the root directory of this source tree. An additional grant
- * of patent rights can be found in the PATENTS file in the same directory.
- *
- *
- */
-
-function hexOctet() {
- return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
-}
-
-function generateId() {
- return hexOctet() + hexOctet() + '-' + hexOctet() + '-' + hexOctet() + '-' + hexOctet() + '-' + hexOctet() + hexOctet() + hexOctet();
-}
-
-var InstallationController = {
- currentInstallationId: function () {
- if (typeof iidCache === 'string') {
- return _ParsePromise2.default.as(iidCache);
- }
- var path = _Storage2.default.generatePath('installationId');
- return _Storage2.default.getItemAsync(path).then(function (iid) {
- if (!iid) {
- iid = generateId();
- return _Storage2.default.setItemAsync(path, iid).then(function () {
- iidCache = iid;
- return iid;
- });
- }
- iidCache = iid;
- return iid;
- });
- },
- _clearCache: function () {
- iidCache = null;
- },
- _setInstallationIdCache: function (iid) {
- iidCache = iid;
- }
-};
-
-module.exports = InstallationController;
\ No newline at end of file
diff --git a/lib/node/LiveQueryClient.js b/lib/node/LiveQueryClient.js
deleted file mode 100644
index e0420323f..000000000
--- a/lib/node/LiveQueryClient.js
+++ /dev/null
@@ -1,595 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-
-var _typeof2 = require('babel-runtime/helpers/typeof');
-
-var _typeof3 = _interopRequireDefault(_typeof2);
-
-var _getIterator2 = require('babel-runtime/core-js/get-iterator');
-
-var _getIterator3 = _interopRequireDefault(_getIterator2);
-
-var _stringify = require('babel-runtime/core-js/json/stringify');
-
-var _stringify2 = _interopRequireDefault(_stringify);
-
-var _map = require('babel-runtime/core-js/map');
-
-var _map2 = _interopRequireDefault(_map);
-
-var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of');
-
-var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
-
-var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
-
-var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
-
-var _createClass2 = require('babel-runtime/helpers/createClass');
-
-var _createClass3 = _interopRequireDefault(_createClass2);
-
-var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn');
-
-var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
-
-var _inherits2 = require('babel-runtime/helpers/inherits');
-
-var _inherits3 = _interopRequireDefault(_inherits2);
-
-var _EventEmitter2 = require('./EventEmitter');
-
-var _EventEmitter3 = _interopRequireDefault(_EventEmitter2);
-
-var _ParsePromise = require('./ParsePromise');
-
-var _ParsePromise2 = _interopRequireDefault(_ParsePromise);
-
-var _ParseObject = require('./ParseObject');
-
-var _ParseObject2 = _interopRequireDefault(_ParseObject);
-
-var _LiveQuerySubscription = require('./LiveQuerySubscription');
-
-var _LiveQuerySubscription2 = _interopRequireDefault(_LiveQuerySubscription);
-
-function _interopRequireDefault(obj) {
- return obj && obj.__esModule ? obj : { default: obj };
-}
-
-// The LiveQuery client inner state
-/**
- * Copyright (c) 2015-present, Parse, LLC.
- * All rights reserved.
- *
- * This source code is licensed under the BSD-style license found in the
- * LICENSE file in the root directory of this source tree. An additional grant
- * of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-var CLIENT_STATE = {
- INITIALIZED: 'initialized',
- CONNECTING: 'connecting',
- CONNECTED: 'connected',
- CLOSED: 'closed',
- RECONNECTING: 'reconnecting',
- DISCONNECTED: 'disconnected'
-};
-
-// The event type the LiveQuery client should sent to server
-var OP_TYPES = {
- CONNECT: 'connect',
- SUBSCRIBE: 'subscribe',
- UNSUBSCRIBE: 'unsubscribe',
- ERROR: 'error'
-};
-
-// The event we get back from LiveQuery server
-var OP_EVENTS = {
- CONNECTED: 'connected',
- SUBSCRIBED: 'subscribed',
- UNSUBSCRIBED: 'unsubscribed',
- ERROR: 'error',
- CREATE: 'create',
- UPDATE: 'update',
- ENTER: 'enter',
- LEAVE: 'leave',
- DELETE: 'delete'
-};
-
-// The event the LiveQuery client should emit
-var CLIENT_EMMITER_TYPES = {
- CLOSE: 'close',
- ERROR: 'error',
- OPEN: 'open'
-};
-
-// The event the LiveQuery subscription should emit
-var SUBSCRIPTION_EMMITER_TYPES = {
- OPEN: 'open',
- CLOSE: 'close',
- ERROR: 'error',
- CREATE: 'create',
- UPDATE: 'update',
- ENTER: 'enter',
- LEAVE: 'leave',
- DELETE: 'delete'
-};
-
-var generateInterval = function (k) {
- return Math.random() * Math.min(30, Math.pow(2, k) - 1) * 1000;
-};
-
-/**
- * Creates a new LiveQueryClient.
- * Extends events.EventEmitter
- * cloud functions.
- *
- * A wrapper of a standard WebSocket client. We add several useful methods to
- * help you connect/disconnect to LiveQueryServer, subscribe/unsubscribe a ParseQuery easily.
- *
- * javascriptKey and masterKey are used for verifying the LiveQueryClient when it tries
- * to connect to the LiveQuery server
- *
- * @class Parse.LiveQueryClient
- * @constructor
- * @param {Object} options
- * @param {string} options.applicationId - applicationId of your Parse app
- * @param {string} options.serverURL - the URL of your LiveQuery server
- * @param {string} options.javascriptKey (optional)
- * @param {string} options.masterKey (optional) Your Parse Master Key. (Node.js only!)
- * @param {string} options.sessionToken (optional)
- *
- *
- * We expose three events to help you monitor the status of the LiveQueryClient.
- *
- * - * let Parse = require('parse/node'); - * let LiveQueryClient = Parse.LiveQueryClient; - * let client = new LiveQueryClient({ - * applicationId: '', - * serverURL: '', - * javascriptKey: '', - * masterKey: '' - * }); - *- * - * Open - When we establish the WebSocket connection to the LiveQuery server, you'll get this event. - *
- * client.on('open', () => { - * - * });- * - * Close - When we lose the WebSocket connection to the LiveQuery server, you'll get this event. - *
- * client.on('close', () => { - * - * });- * - * Error - When some network error or LiveQuery server error happens, you'll get this event. - *
- * client.on('error', (error) => { - * - * });- * - * - */ - -var LiveQueryClient = function (_EventEmitter) { - (0, _inherits3.default)(LiveQueryClient, _EventEmitter); - - function LiveQueryClient(_ref) { - var applicationId = _ref.applicationId, - serverURL = _ref.serverURL, - javascriptKey = _ref.javascriptKey, - masterKey = _ref.masterKey, - sessionToken = _ref.sessionToken; - (0, _classCallCheck3.default)(this, LiveQueryClient); - - var _this = (0, _possibleConstructorReturn3.default)(this, (LiveQueryClient.__proto__ || (0, _getPrototypeOf2.default)(LiveQueryClient)).call(this)); - - if (!serverURL || serverURL.indexOf('ws') !== 0) { - throw new Error('You need to set a proper Parse LiveQuery server url before using LiveQueryClient'); - } - - _this.reconnectHandle = null; - _this.attempts = 1;; - _this.id = 0; - _this.requestId = 1; - _this.serverURL = serverURL; - _this.applicationId = applicationId; - _this.javascriptKey = javascriptKey; - _this.masterKey = masterKey; - _this.sessionToken = sessionToken; - _this.connectPromise = new _ParsePromise2.default(); - _this.subscriptions = new _map2.default(); - _this.state = CLIENT_STATE.INITIALIZED; - return _this; - } - - (0, _createClass3.default)(LiveQueryClient, [{ - key: 'shouldOpen', - value: function () { - return this.state === CLIENT_STATE.INITIALIZED || this.state === CLIENT_STATE.DISCONNECTED; - } - - /** - * Subscribes to a ParseQuery - * - * If you provide the sessionToken, when the LiveQuery server gets ParseObject's - * updates from parse server, it'll try to check whether the sessionToken fulfills - * the ParseObject's ACL. The LiveQuery server will only send updates to clients whose - * sessionToken is fit for the ParseObject's ACL. You can check the LiveQuery protocol - * here for more details. The subscription you get is the same subscription you get - * from our Standard API. - * - * @method subscribe - * @param {Object} query - the ParseQuery you want to subscribe to - * @param {string} sessionToken (optional) - * @return {Object} subscription - */ - - }, { - key: 'subscribe', - value: function (query, sessionToken) { - var _this2 = this; - - if (!query) { - return; - } - var where = query.toJSON().where; - var className = query.className; - var subscribeRequest = { - op: OP_TYPES.SUBSCRIBE, - requestId: this.requestId, - query: { - className: className, - where: where - } - }; - - if (sessionToken) { - subscribeRequest.sessionToken = sessionToken; - } - - var subscription = new _LiveQuerySubscription2.default(this.requestId, query, sessionToken); - this.subscriptions.set(this.requestId, subscription); - this.requestId += 1; - this.connectPromise.then(function () { - _this2.socket.send((0, _stringify2.default)(subscribeRequest)); - }); - - // adding listener so process does not crash - // best practice is for developer to register their own listener - subscription.on('error', function () {}); - - return subscription; - } - - /** - * After calling unsubscribe you'll stop receiving events from the subscription object. - * - * @method unsubscribe - * @param {Object} subscription - subscription you would like to unsubscribe from. - */ - - }, { - key: 'unsubscribe', - value: function (subscription) { - var _this3 = this; - - if (!subscription) { - return; - } - - this.subscriptions.delete(subscription.id); - var unsubscribeRequest = { - op: OP_TYPES.UNSUBSCRIBE, - requestId: subscription.id - }; - this.connectPromise.then(function () { - _this3.socket.send((0, _stringify2.default)(unsubscribeRequest)); - }); - } - - /** - * After open is called, the LiveQueryClient will try to send a connect request - * to the LiveQuery server. - * - * @method open - */ - - }, { - key: 'open', - value: function () { - var _this4 = this; - - var WebSocketImplementation = this._getWebSocketImplementation(); - if (!WebSocketImplementation) { - this.emit(CLIENT_EMMITER_TYPES.ERROR, 'Can not find WebSocket implementation'); - return; - } - - if (this.state !== CLIENT_STATE.RECONNECTING) { - this.state = CLIENT_STATE.CONNECTING; - } - - // Get WebSocket implementation - this.socket = new WebSocketImplementation(this.serverURL); - - // Bind WebSocket callbacks - this.socket.onopen = function () { - _this4._handleWebSocketOpen(); - }; - - this.socket.onmessage = function (event) { - _this4._handleWebSocketMessage(event); - }; - - this.socket.onclose = function () { - _this4._handleWebSocketClose(); - }; - - this.socket.onerror = function (error) { - _this4._handleWebSocketError(error); - }; - } - }, { - key: 'resubscribe', - value: function () { - var _this5 = this; - - this.subscriptions.forEach(function (subscription, requestId) { - var query = subscription.query; - var where = query.toJSON().where; - var className = query.className; - var sessionToken = subscription.sessionToken; - var subscribeRequest = { - op: OP_TYPES.SUBSCRIBE, - requestId: requestId, - query: { - className: className, - where: where - } - }; - - if (sessionToken) { - subscribeRequest.sessionToken = sessionToken; - } - - _this5.connectPromise.then(function () { - _this5.socket.send((0, _stringify2.default)(subscribeRequest)); - }); - }); - } - - /** - * This method will close the WebSocket connection to this LiveQueryClient, - * cancel the auto reconnect and unsubscribe all subscriptions based on it. - * - * @method close - */ - - }, { - key: 'close', - value: function () { - if (this.state === CLIENT_STATE.INITIALIZED || this.state === CLIENT_STATE.DISCONNECTED) { - return; - } - this.state = CLIENT_STATE.DISCONNECTED; - this.socket.close(); - // Notify each subscription about the close - var _iteratorNormalCompletion = true; - var _didIteratorError = false; - var _iteratorError = undefined; - - try { - for (var _iterator = (0, _getIterator3.default)(this.subscriptions.values()), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { - var subscription = _step.value; - - subscription.emit(SUBSCRIPTION_EMMITER_TYPES.CLOSE); - } - } catch (err) { - _didIteratorError = true; - _iteratorError = err; - } finally { - try { - if (!_iteratorNormalCompletion && _iterator.return) { - _iterator.return(); - } - } finally { - if (_didIteratorError) { - throw _iteratorError; - } - } - } - - this._handleReset(); - this.emit(CLIENT_EMMITER_TYPES.CLOSE); - } - }, { - key: '_getWebSocketImplementation', - value: function () { - return require('ws'); - } - - // ensure we start with valid state if connect is called again after close - - }, { - key: '_handleReset', - value: function () { - this.attempts = 1;; - this.id = 0; - this.requestId = 1; - this.connectPromise = new _ParsePromise2.default(); - this.subscriptions = new _map2.default(); - } - }, { - key: '_handleWebSocketOpen', - value: function () { - this.attempts = 1; - var connectRequest = { - op: OP_TYPES.CONNECT, - applicationId: this.applicationId, - javascriptKey: this.javascriptKey, - masterKey: this.masterKey, - sessionToken: this.sessionToken - }; - this.socket.send((0, _stringify2.default)(connectRequest)); - } - }, { - key: '_handleWebSocketMessage', - value: function (event) { - var data = event.data; - if (typeof data === 'string') { - data = JSON.parse(data); - } - var subscription = null; - if (data.requestId) { - subscription = this.subscriptions.get(data.requestId); - } - switch (data.op) { - case OP_EVENTS.CONNECTED: - if (this.state === CLIENT_STATE.RECONNECTING) { - this.resubscribe(); - } - this.emit(CLIENT_EMMITER_TYPES.OPEN); - this.id = data.clientId; - this.connectPromise.resolve(); - this.state = CLIENT_STATE.CONNECTED; - break; - case OP_EVENTS.SUBSCRIBED: - if (subscription) { - subscription.emit(SUBSCRIPTION_EMMITER_TYPES.OPEN); - } - break; - case OP_EVENTS.ERROR: - if (data.requestId) { - if (subscription) { - subscription.emit(SUBSCRIPTION_EMMITER_TYPES.ERROR, data.error); - } - } else { - this.emit(CLIENT_EMMITER_TYPES.ERROR, data.error); - } - break; - case OP_EVENTS.UNSUBSCRIBED: - // We have already deleted subscription in unsubscribe(), do nothing here - break; - default: - // create, update, enter, leave, delete cases - var className = data.object.className; - // Delete the extrea __type and className fields during transfer to full JSON - delete data.object.__type; - delete data.object.className; - var parseObject = new _ParseObject2.default(className); - parseObject._finishFetch(data.object); - if (!subscription) { - break; - } - subscription.emit(data.op, parseObject); - } - } - }, { - key: '_handleWebSocketClose', - value: function () { - if (this.state === CLIENT_STATE.DISCONNECTED) { - return; - } - this.state = CLIENT_STATE.CLOSED; - this.emit(CLIENT_EMMITER_TYPES.CLOSE); - // Notify each subscription about the close - var _iteratorNormalCompletion2 = true; - var _didIteratorError2 = false; - var _iteratorError2 = undefined; - - try { - for (var _iterator2 = (0, _getIterator3.default)(this.subscriptions.values()), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { - var subscription = _step2.value; - - subscription.emit(SUBSCRIPTION_EMMITER_TYPES.CLOSE); - } - } catch (err) { - _didIteratorError2 = true; - _iteratorError2 = err; - } finally { - try { - if (!_iteratorNormalCompletion2 && _iterator2.return) { - _iterator2.return(); - } - } finally { - if (_didIteratorError2) { - throw _iteratorError2; - } - } - } - - this._handleReconnect(); - } - }, { - key: '_handleWebSocketError', - value: function (error) { - this.emit(CLIENT_EMMITER_TYPES.ERROR, error); - var _iteratorNormalCompletion3 = true; - var _didIteratorError3 = false; - var _iteratorError3 = undefined; - - try { - for (var _iterator3 = (0, _getIterator3.default)(this.subscriptions.values()), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) { - var subscription = _step3.value; - - subscription.emit(SUBSCRIPTION_EMMITER_TYPES.ERROR); - } - } catch (err) { - _didIteratorError3 = true; - _iteratorError3 = err; - } finally { - try { - if (!_iteratorNormalCompletion3 && _iterator3.return) { - _iterator3.return(); - } - } finally { - if (_didIteratorError3) { - throw _iteratorError3; - } - } - } - - this._handleReconnect(); - } - }, { - key: '_handleReconnect', - value: function () { - var _this6 = this; - - // if closed or currently reconnecting we stop attempting to reconnect - if (this.state === CLIENT_STATE.DISCONNECTED) { - return; - } - - this.state = CLIENT_STATE.RECONNECTING; - var time = generateInterval(this.attempts); - - // handle case when both close/error occur at frequent rates we ensure we do not reconnect unnecessarily. - // we're unable to distinguish different between close/error when we're unable to reconnect therefore - // we try to reonnect in both cases - // server side ws and browser WebSocket behave differently in when close/error get triggered - - if (this.reconnectHandle) { - clearTimeout(this.reconnectHandle); - } - - this.reconnectHandle = setTimeout(function () { - _this6.attempts++; - _this6.connectPromise = new _ParsePromise2.default(); - _this6.open(); - }.bind(this), time); - } - }]); - return LiveQueryClient; -}(_EventEmitter3.default); - -exports.default = LiveQueryClient; \ No newline at end of file diff --git a/lib/node/LiveQuerySubscription.js b/lib/node/LiveQuerySubscription.js deleted file mode 100644 index 4078b65d5..000000000 --- a/lib/node/LiveQuerySubscription.js +++ /dev/null @@ -1,162 +0,0 @@ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); - -var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); - -var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); - -var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); - -var _createClass2 = require('babel-runtime/helpers/createClass'); - -var _createClass3 = _interopRequireDefault(_createClass2); - -var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); - -var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); - -var _inherits2 = require('babel-runtime/helpers/inherits'); - -var _inherits3 = _interopRequireDefault(_inherits2); - -var _EventEmitter2 = require('./EventEmitter'); - -var _EventEmitter3 = _interopRequireDefault(_EventEmitter2); - -var _CoreManager = require('./CoreManager'); - -var _CoreManager2 = _interopRequireDefault(_CoreManager); - -function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { default: obj }; -} - -/** - * Creates a new LiveQuery Subscription. - * Extends events.EventEmitter - * cloud functions. - * - * @constructor - * @param {string} id - subscription id - * @param {string} query - query to subscribe to - * @param {string} sessionToken - optional session token - * - *
Open Event - When you call query.subscribe(), we send a subscribe request to - * the LiveQuery server, when we get the confirmation from the LiveQuery server, - * this event will be emitted. When the client loses WebSocket connection to the - * LiveQuery server, we will try to auto reconnect the LiveQuery server. If we - * reconnect the LiveQuery server and successfully resubscribe the ParseQuery, - * you'll also get this event. - * - *
- * subscription.on('open', () => { - * - * });- * - *
Create Event - When a new ParseObject is created and it fulfills the ParseQuery you subscribe, - * you'll get this event. The object is the ParseObject which is created. - * - *
- * subscription.on('create', (object) => { - * - * });- * - *
Update Event - When an existing ParseObject which fulfills the ParseQuery you subscribe - * is updated (The ParseObject fulfills the ParseQuery before and after changes), - * you'll get this event. The object is the ParseObject which is updated. - * Its content is the latest value of the ParseObject. - * - *
- * subscription.on('update', (object) => { - * - * });- * - *
Enter Event - When an existing ParseObject's old value doesn't fulfill the ParseQuery - * but its new value fulfills the ParseQuery, you'll get this event. The object is the - * ParseObject which enters the ParseQuery. Its content is the latest value of the ParseObject. - * - *
- * subscription.on('enter', (object) => { - * - * });- * - * - *
Update Event - When an existing ParseObject's old value fulfills the ParseQuery but its new value - * doesn't fulfill the ParseQuery, you'll get this event. The object is the ParseObject - * which leaves the ParseQuery. Its content is the latest value of the ParseObject. - * - *
- * subscription.on('leave', (object) => { - * - * });- * - * - *
Delete Event - When an existing ParseObject which fulfills the ParseQuery is deleted, you'll - * get this event. The object is the ParseObject which is deleted. - * - *
- * subscription.on('delete', (object) => { - * - * });- * - * - *
Close Event - When the client loses the WebSocket connection to the LiveQuery - * server and we stop receiving events, you'll get this event. - * - *
- * subscription.on('close', () => { - * - * });- * - * - */ -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -var Subscription = function (_EventEmitter) { - (0, _inherits3.default)(Subscription, _EventEmitter); - - function Subscription(id, query, sessionToken) { - (0, _classCallCheck3.default)(this, Subscription); - - var _this2 = (0, _possibleConstructorReturn3.default)(this, (Subscription.__proto__ || (0, _getPrototypeOf2.default)(Subscription)).call(this)); - - _this2.id = id; - _this2.query = query; - _this2.sessionToken = sessionToken; - return _this2; - } - - /** - * @method unsubscribe - */ - - (0, _createClass3.default)(Subscription, [{ - key: 'unsubscribe', - value: function () { - var _this3 = this; - - var _this = this; - _CoreManager2.default.getLiveQueryController().getDefaultLiveQueryClient().then(function (liveQueryClient) { - liveQueryClient.unsubscribe(_this); - _this.emit('close'); - _this3.resolve(); - }); - } - }]); - return Subscription; -}(_EventEmitter3.default); - -exports.default = Subscription; \ No newline at end of file diff --git a/lib/node/ObjectStateMutations.js b/lib/node/ObjectStateMutations.js deleted file mode 100644 index b476a0e2f..000000000 --- a/lib/node/ObjectStateMutations.js +++ /dev/null @@ -1,165 +0,0 @@ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _stringify = require('babel-runtime/core-js/json/stringify'); - -var _stringify2 = _interopRequireDefault(_stringify); - -var _typeof2 = require('babel-runtime/helpers/typeof'); - -var _typeof3 = _interopRequireDefault(_typeof2); - -exports.defaultState = defaultState; -exports.setServerData = setServerData; -exports.setPendingOp = setPendingOp; -exports.pushPendingState = pushPendingState; -exports.popPendingState = popPendingState; -exports.mergeFirstPendingState = mergeFirstPendingState; -exports.estimateAttribute = estimateAttribute; -exports.estimateAttributes = estimateAttributes; -exports.commitServerChanges = commitServerChanges; - -var _encode = require('./encode'); - -var _encode2 = _interopRequireDefault(_encode); - -var _ParseFile = require('./ParseFile'); - -var _ParseFile2 = _interopRequireDefault(_ParseFile); - -var _ParseObject = require('./ParseObject'); - -var _ParseObject2 = _interopRequireDefault(_ParseObject); - -var _ParsePromise = require('./ParsePromise'); - -var _ParsePromise2 = _interopRequireDefault(_ParsePromise); - -var _ParseRelation = require('./ParseRelation'); - -var _ParseRelation2 = _interopRequireDefault(_ParseRelation); - -var _TaskQueue = require('./TaskQueue'); - -var _TaskQueue2 = _interopRequireDefault(_TaskQueue); - -var _ParseOp = require('./ParseOp'); - -function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { default: obj }; -} - -function defaultState() { - return { - serverData: {}, - pendingOps: [{}], - objectCache: {}, - tasks: new _TaskQueue2.default(), - existed: false - }; -} /** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * - */ - -function setServerData(serverData, attributes) { - for (var _attr in attributes) { - if (typeof attributes[_attr] !== 'undefined') { - serverData[_attr] = attributes[_attr]; - } else { - delete serverData[_attr]; - } - } -} - -function setPendingOp(pendingOps, attr, op) { - var last = pendingOps.length - 1; - if (op) { - pendingOps[last][attr] = op; - } else { - delete pendingOps[last][attr]; - } -} - -function pushPendingState(pendingOps) { - pendingOps.push({}); -} - -function popPendingState(pendingOps) { - var first = pendingOps.shift(); - if (!pendingOps.length) { - pendingOps[0] = {}; - } - return first; -} - -function mergeFirstPendingState(pendingOps) { - var first = popPendingState(pendingOps); - var next = pendingOps[0]; - for (var _attr2 in first) { - if (next[_attr2] && first[_attr2]) { - var merged = next[_attr2].mergeWith(first[_attr2]); - if (merged) { - next[_attr2] = merged; - } - } else { - next[_attr2] = first[_attr2]; - } - } -} - -function estimateAttribute(serverData, pendingOps, className, id, attr) { - var value = serverData[attr]; - for (var i = 0; i < pendingOps.length; i++) { - if (pendingOps[i][attr]) { - if (pendingOps[i][attr] instanceof _ParseOp.RelationOp) { - if (id) { - value = pendingOps[i][attr].applyTo(value, { className: className, id: id }, attr); - } - } else { - value = pendingOps[i][attr].applyTo(value); - } - } - } - return value; -} - -function estimateAttributes(serverData, pendingOps, className, id) { - var data = {}; - var attr = void 0; - for (attr in serverData) { - data[attr] = serverData[attr]; - } - for (var i = 0; i < pendingOps.length; i++) { - for (attr in pendingOps[i]) { - if (pendingOps[i][attr] instanceof _ParseOp.RelationOp) { - if (id) { - data[attr] = pendingOps[i][attr].applyTo(data[attr], { className: className, id: id }, attr); - } - } else { - data[attr] = pendingOps[i][attr].applyTo(data[attr]); - } - } - } - return data; -} - -function commitServerChanges(serverData, objectCache, changes) { - for (var _attr3 in changes) { - var val = changes[_attr3]; - serverData[_attr3] = val; - if (val && (typeof val === 'undefined' ? 'undefined' : (0, _typeof3.default)(val)) === 'object' && !(val instanceof _ParseObject2.default) && !(val instanceof _ParseFile2.default) && !(val instanceof _ParseRelation2.default)) { - var json = (0, _encode2.default)(val, false, true); - objectCache[_attr3] = (0, _stringify2.default)(json); - } - } -} \ No newline at end of file diff --git a/lib/node/Parse.js b/lib/node/Parse.js deleted file mode 100644 index 280cd4015..000000000 --- a/lib/node/Parse.js +++ /dev/null @@ -1,190 +0,0 @@ -'use strict'; - -var _decode = require('./decode'); - -var _decode2 = _interopRequireDefault(_decode); - -var _encode = require('./encode'); - -var _encode2 = _interopRequireDefault(_encode); - -var _CoreManager = require('./CoreManager'); - -var _CoreManager2 = _interopRequireDefault(_CoreManager); - -var _InstallationController = require('./InstallationController'); - -var _InstallationController2 = _interopRequireDefault(_InstallationController); - -var _ParseOp = require('./ParseOp'); - -var ParseOp = _interopRequireWildcard(_ParseOp); - -var _RESTController = require('./RESTController'); - -var _RESTController2 = _interopRequireDefault(_RESTController); - -function _interopRequireWildcard(obj) { - if (obj && obj.__esModule) { - return obj; - } else { - var newObj = {};if (obj != null) { - for (var key in obj) { - if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; - } - }newObj.default = obj;return newObj; - } -} - -function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { default: obj }; -} - -/** - * Contains all Parse API classes and functions. - * @class Parse - * @static - */ -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -var Parse = { - /** - * Call this method first to set up your authentication tokens for Parse. - * You can get your keys from the Data Browser on parse.com. - * @method initialize - * @param {String} applicationId Your Parse Application ID. - * @param {String} javaScriptKey (optional) Your Parse JavaScript Key (Not needed for parse-server) - * @param {String} masterKey (optional) Your Parse Master Key. (Node.js only!) - * @static - */ - initialize: function (applicationId, javaScriptKey) { - Parse._initialize(applicationId, javaScriptKey); - }, - _initialize: function (applicationId, javaScriptKey, masterKey) { - _CoreManager2.default.set('APPLICATION_ID', applicationId); - _CoreManager2.default.set('JAVASCRIPT_KEY', javaScriptKey); - _CoreManager2.default.set('MASTER_KEY', masterKey); - _CoreManager2.default.set('USE_MASTER_KEY', false); - } -}; - -/** These legacy setters may eventually be deprecated **/ -Object.defineProperty(Parse, 'applicationId', { - get: function () { - return _CoreManager2.default.get('APPLICATION_ID'); - }, - set: function (value) { - _CoreManager2.default.set('APPLICATION_ID', value); - } -}); -Object.defineProperty(Parse, 'javaScriptKey', { - get: function () { - return _CoreManager2.default.get('JAVASCRIPT_KEY'); - }, - set: function (value) { - _CoreManager2.default.set('JAVASCRIPT_KEY', value); - } -}); -Object.defineProperty(Parse, 'masterKey', { - get: function () { - return _CoreManager2.default.get('MASTER_KEY'); - }, - set: function (value) { - _CoreManager2.default.set('MASTER_KEY', value); - } -}); -Object.defineProperty(Parse, 'serverURL', { - get: function () { - return _CoreManager2.default.get('SERVER_URL'); - }, - set: function (value) { - _CoreManager2.default.set('SERVER_URL', value); - } -}); -Object.defineProperty(Parse, 'liveQueryServerURL', { - get: function () { - return _CoreManager2.default.get('LIVEQUERY_SERVER_URL'); - }, - set: function (value) { - _CoreManager2.default.set('LIVEQUERY_SERVER_URL', value); - } -}); -/** End setters **/ - -Parse.ACL = require('./ParseACL').default; -Parse.Analytics = require('./Analytics'); -Parse.Cloud = require('./Cloud'); -Parse.CoreManager = require('./CoreManager'); -Parse.Config = require('./ParseConfig').default; -Parse.Error = require('./ParseError').default; -Parse.FacebookUtils = require('./FacebookUtils').default; -Parse.File = require('./ParseFile').default; -Parse.GeoPoint = require('./ParseGeoPoint').default; -Parse.Installation = require('./ParseInstallation').default; -Parse.Object = require('./ParseObject').default; -Parse.Op = { - Set: ParseOp.SetOp, - Unset: ParseOp.UnsetOp, - Increment: ParseOp.IncrementOp, - Add: ParseOp.AddOp, - Remove: ParseOp.RemoveOp, - AddUnique: ParseOp.AddUniqueOp, - Relation: ParseOp.RelationOp -}; -Parse.Promise = require('./ParsePromise').default; -Parse.Push = require('./Push'); -Parse.Query = require('./ParseQuery').default; -Parse.Relation = require('./ParseRelation').default; -Parse.Role = require('./ParseRole').default; -Parse.Session = require('./ParseSession').default; -Parse.Storage = require('./Storage'); -Parse.User = require('./ParseUser').default; -Parse.LiveQuery = require('./ParseLiveQuery').default; -Parse.LiveQueryClient = require('./LiveQueryClient').default; - -Parse._request = function () { - for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { - args[_key] = arguments[_key]; - } - - return _CoreManager2.default.getRESTController().request.apply(null, args); -}; -Parse._ajax = function () { - for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { - args[_key2] = arguments[_key2]; - } - - return _CoreManager2.default.getRESTController().ajax.apply(null, args); -}; -// We attempt to match the signatures of the legacy versions of these methods -Parse._decode = function (_, value) { - return (0, _decode2.default)(value); -}; -Parse._encode = function (value, _, disallowObjects) { - return (0, _encode2.default)(value, disallowObjects); -}; -Parse._getInstallationId = function () { - return _CoreManager2.default.getInstallationController().currentInstallationId(); -}; - -_CoreManager2.default.setInstallationController(_InstallationController2.default); -_CoreManager2.default.setRESTController(_RESTController2.default); - -Parse.initialize = Parse._initialize; -Parse.Cloud = Parse.Cloud || {}; -Parse.Cloud.useMasterKey = function () { - _CoreManager2.default.set('USE_MASTER_KEY', true); -}; -Parse.Hooks = require('./ParseHooks'); - -// For legacy requires, of the form `var Parse = require('parse').Parse` -Parse.Parse = Parse; - -module.exports = Parse; \ No newline at end of file diff --git a/lib/node/ParseACL.js b/lib/node/ParseACL.js deleted file mode 100644 index 2aa232c18..000000000 --- a/lib/node/ParseACL.js +++ /dev/null @@ -1,406 +0,0 @@ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _keys = require('babel-runtime/core-js/object/keys'); - -var _keys2 = _interopRequireDefault(_keys); - -var _typeof2 = require('babel-runtime/helpers/typeof'); - -var _typeof3 = _interopRequireDefault(_typeof2); - -var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); - -var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); - -var _createClass2 = require('babel-runtime/helpers/createClass'); - -var _createClass3 = _interopRequireDefault(_createClass2); - -var _ParseRole = require('./ParseRole'); - -var _ParseRole2 = _interopRequireDefault(_ParseRole); - -var _ParseUser = require('./ParseUser'); - -var _ParseUser2 = _interopRequireDefault(_ParseUser); - -function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { default: obj }; -} - -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * - */ - -var PUBLIC_KEY = '*'; - -/** - * Creates a new ACL. - * If no argument is given, the ACL has no permissions for anyone. - * If the argument is a Parse.User, the ACL will have read and write - * permission for only that user. - * If the argument is any other JSON object, that object will be interpretted - * as a serialized ACL created with toJSON(). - * @class Parse.ACL - * @constructor - * - *
An ACL, or Access Control List can be added to any
- * Parse.Object
to restrict access to only a subset of users
- * of your application.
Parse.Error
.
- * @param {String} message A detailed description of the error.
- */
-var ParseError = function ParseError(code, message) {
- (0, _classCallCheck3.default)(this, ParseError);
-
- this.code = code;
- this.message = message;
-};
-
-/**
- * Error code indicating some error other than those enumerated here.
- * @property OTHER_CAUSE
- * @static
- * @final
- */
-
-exports.default = ParseError;
-ParseError.OTHER_CAUSE = -1;
-
-/**
- * Error code indicating that something has gone wrong with the server.
- * If you get this error code, it is Parse's fault. Contact us at
- * https://parse.com/help
- * @property INTERNAL_SERVER_ERROR
- * @static
- * @final
- */
-ParseError.INTERNAL_SERVER_ERROR = 1;
-
-/**
- * Error code indicating the connection to the Parse servers failed.
- * @property CONNECTION_FAILED
- * @static
- * @final
- */
-ParseError.CONNECTION_FAILED = 100;
-
-/**
- * Error code indicating the specified object doesn't exist.
- * @property OBJECT_NOT_FOUND
- * @static
- * @final
- */
-ParseError.OBJECT_NOT_FOUND = 101;
-
-/**
- * Error code indicating you tried to query with a datatype that doesn't
- * support it, like exact matching an array or object.
- * @property INVALID_QUERY
- * @static
- * @final
- */
-ParseError.INVALID_QUERY = 102;
-
-/**
- * Error code indicating a missing or invalid classname. Classnames are
- * case-sensitive. They must start with a letter, and a-zA-Z0-9_ are the
- * only valid characters.
- * @property INVALID_CLASS_NAME
- * @static
- * @final
- */
-ParseError.INVALID_CLASS_NAME = 103;
-
-/**
- * Error code indicating an unspecified object id.
- * @property MISSING_OBJECT_ID
- * @static
- * @final
- */
-ParseError.MISSING_OBJECT_ID = 104;
-
-/**
- * Error code indicating an invalid key name. Keys are case-sensitive. They
- * must start with a letter, and a-zA-Z0-9_ are the only valid characters.
- * @property INVALID_KEY_NAME
- * @static
- * @final
- */
-ParseError.INVALID_KEY_NAME = 105;
-
-/**
- * Error code indicating a malformed pointer. You should not see this unless
- * you have been mucking about changing internal Parse code.
- * @property INVALID_POINTER
- * @static
- * @final
- */
-ParseError.INVALID_POINTER = 106;
-
-/**
- * Error code indicating that badly formed JSON was received upstream. This
- * either indicates you have done something unusual with modifying how
- * things encode to JSON, or the network is failing badly.
- * @property INVALID_JSON
- * @static
- * @final
- */
-ParseError.INVALID_JSON = 107;
-
-/**
- * Error code indicating that the feature you tried to access is only
- * available internally for testing purposes.
- * @property COMMAND_UNAVAILABLE
- * @static
- * @final
- */
-ParseError.COMMAND_UNAVAILABLE = 108;
-
-/**
- * You must call Parse.initialize before using the Parse library.
- * @property NOT_INITIALIZED
- * @static
- * @final
- */
-ParseError.NOT_INITIALIZED = 109;
-
-/**
- * Error code indicating that a field was set to an inconsistent type.
- * @property INCORRECT_TYPE
- * @static
- * @final
- */
-ParseError.INCORRECT_TYPE = 111;
-
-/**
- * Error code indicating an invalid channel name. A channel name is either
- * an empty string (the broadcast channel) or contains only a-zA-Z0-9_
- * characters and starts with a letter.
- * @property INVALID_CHANNEL_NAME
- * @static
- * @final
- */
-ParseError.INVALID_CHANNEL_NAME = 112;
-
-/**
- * Error code indicating that push is misconfigured.
- * @property PUSH_MISCONFIGURED
- * @static
- * @final
- */
-ParseError.PUSH_MISCONFIGURED = 115;
-
-/**
- * Error code indicating that the object is too large.
- * @property OBJECT_TOO_LARGE
- * @static
- * @final
- */
-ParseError.OBJECT_TOO_LARGE = 116;
-
-/**
- * Error code indicating that the operation isn't allowed for clients.
- * @property OPERATION_FORBIDDEN
- * @static
- * @final
- */
-ParseError.OPERATION_FORBIDDEN = 119;
-
-/**
- * Error code indicating the result was not found in the cache.
- * @property CACHE_MISS
- * @static
- * @final
- */
-ParseError.CACHE_MISS = 120;
-
-/**
- * Error code indicating that an invalid key was used in a nested
- * JSONObject.
- * @property INVALID_NESTED_KEY
- * @static
- * @final
- */
-ParseError.INVALID_NESTED_KEY = 121;
-
-/**
- * Error code indicating that an invalid filename was used for ParseFile.
- * A valid file name contains only a-zA-Z0-9_. characters and is between 1
- * and 128 characters.
- * @property INVALID_FILE_NAME
- * @static
- * @final
- */
-ParseError.INVALID_FILE_NAME = 122;
-
-/**
- * Error code indicating an invalid ACL was provided.
- * @property INVALID_ACL
- * @static
- * @final
- */
-ParseError.INVALID_ACL = 123;
-
-/**
- * Error code indicating that the request timed out on the server. Typically
- * this indicates that the request is too expensive to run.
- * @property TIMEOUT
- * @static
- * @final
- */
-ParseError.TIMEOUT = 124;
-
-/**
- * Error code indicating that the email address was invalid.
- * @property INVALID_EMAIL_ADDRESS
- * @static
- * @final
- */
-ParseError.INVALID_EMAIL_ADDRESS = 125;
-
-/**
- * Error code indicating a missing content type.
- * @property MISSING_CONTENT_TYPE
- * @static
- * @final
- */
-ParseError.MISSING_CONTENT_TYPE = 126;
-
-/**
- * Error code indicating a missing content length.
- * @property MISSING_CONTENT_LENGTH
- * @static
- * @final
- */
-ParseError.MISSING_CONTENT_LENGTH = 127;
-
-/**
- * Error code indicating an invalid content length.
- * @property INVALID_CONTENT_LENGTH
- * @static
- * @final
- */
-ParseError.INVALID_CONTENT_LENGTH = 128;
-
-/**
- * Error code indicating a file that was too large.
- * @property FILE_TOO_LARGE
- * @static
- * @final
- */
-ParseError.FILE_TOO_LARGE = 129;
-
-/**
- * Error code indicating an error saving a file.
- * @property FILE_SAVE_ERROR
- * @static
- * @final
- */
-ParseError.FILE_SAVE_ERROR = 130;
-
-/**
- * Error code indicating that a unique field was given a value that is
- * already taken.
- * @property DUPLICATE_VALUE
- * @static
- * @final
- */
-ParseError.DUPLICATE_VALUE = 137;
-
-/**
- * Error code indicating that a role's name is invalid.
- * @property INVALID_ROLE_NAME
- * @static
- * @final
- */
-ParseError.INVALID_ROLE_NAME = 139;
-
-/**
- * Error code indicating that an application quota was exceeded. Upgrade to
- * resolve.
- * @property EXCEEDED_QUOTA
- * @static
- * @final
- */
-ParseError.EXCEEDED_QUOTA = 140;
-
-/**
- * Error code indicating that a Cloud Code script failed.
- * @property SCRIPT_FAILED
- * @static
- * @final
- */
-ParseError.SCRIPT_FAILED = 141;
-
-/**
- * Error code indicating that a Cloud Code validation failed.
- * @property VALIDATION_ERROR
- * @static
- * @final
- */
-ParseError.VALIDATION_ERROR = 142;
-
-/**
- * Error code indicating that invalid image data was provided.
- * @property INVALID_IMAGE_DATA
- * @static
- * @final
- */
-ParseError.INVALID_IMAGE_DATA = 143;
-
-/**
- * Error code indicating an unsaved file.
- * @property UNSAVED_FILE_ERROR
- * @static
- * @final
- */
-ParseError.UNSAVED_FILE_ERROR = 151;
-
-/**
- * Error code indicating an invalid push time.
- * @property INVALID_PUSH_TIME_ERROR
- * @static
- * @final
- */
-ParseError.INVALID_PUSH_TIME_ERROR = 152;
-
-/**
- * Error code indicating an error deleting a file.
- * @property FILE_DELETE_ERROR
- * @static
- * @final
- */
-ParseError.FILE_DELETE_ERROR = 153;
-
-/**
- * Error code indicating that the application has exceeded its request
- * limit.
- * @property REQUEST_LIMIT_EXCEEDED
- * @static
- * @final
- */
-ParseError.REQUEST_LIMIT_EXCEEDED = 155;
-
-/**
- * Error code indicating an invalid event name.
- * @property INVALID_EVENT_NAME
- * @static
- * @final
- */
-ParseError.INVALID_EVENT_NAME = 160;
-
-/**
- * Error code indicating that the username is missing or empty.
- * @property USERNAME_MISSING
- * @static
- * @final
- */
-ParseError.USERNAME_MISSING = 200;
-
-/**
- * Error code indicating that the password is missing or empty.
- * @property PASSWORD_MISSING
- * @static
- * @final
- */
-ParseError.PASSWORD_MISSING = 201;
-
-/**
- * Error code indicating that the username has already been taken.
- * @property USERNAME_TAKEN
- * @static
- * @final
- */
-ParseError.USERNAME_TAKEN = 202;
-
-/**
- * Error code indicating that the email has already been taken.
- * @property EMAIL_TAKEN
- * @static
- * @final
- */
-ParseError.EMAIL_TAKEN = 203;
-
-/**
- * Error code indicating that the email is missing, but must be specified.
- * @property EMAIL_MISSING
- * @static
- * @final
- */
-ParseError.EMAIL_MISSING = 204;
-
-/**
- * Error code indicating that a user with the specified email was not found.
- * @property EMAIL_NOT_FOUND
- * @static
- * @final
- */
-ParseError.EMAIL_NOT_FOUND = 205;
-
-/**
- * Error code indicating that a user object without a valid session could
- * not be altered.
- * @property SESSION_MISSING
- * @static
- * @final
- */
-ParseError.SESSION_MISSING = 206;
-
-/**
- * Error code indicating that a user can only be created through signup.
- * @property MUST_CREATE_USER_THROUGH_SIGNUP
- * @static
- * @final
- */
-ParseError.MUST_CREATE_USER_THROUGH_SIGNUP = 207;
-
-/**
- * Error code indicating that an an account being linked is already linked
- * to another user.
- * @property ACCOUNT_ALREADY_LINKED
- * @static
- * @final
- */
-ParseError.ACCOUNT_ALREADY_LINKED = 208;
-
-/**
- * Error code indicating that the current session token is invalid.
- * @property INVALID_SESSION_TOKEN
- * @static
- * @final
- */
-ParseError.INVALID_SESSION_TOKEN = 209;
-
-/**
- * Error code indicating that a user cannot be linked to an account because
- * that account's id could not be found.
- * @property LINKED_ID_MISSING
- * @static
- * @final
- */
-ParseError.LINKED_ID_MISSING = 250;
-
-/**
- * Error code indicating that a user with a linked (e.g. Facebook) account
- * has an invalid session.
- * @property INVALID_LINKED_SESSION
- * @static
- * @final
- */
-ParseError.INVALID_LINKED_SESSION = 251;
-
-/**
- * Error code indicating that a service being linked (e.g. Facebook or
- * Twitter) is unsupported.
- * @property UNSUPPORTED_SERVICE
- * @static
- * @final
- */
-ParseError.UNSUPPORTED_SERVICE = 252;
-
-/**
- * Error code indicating that there were multiple errors. Aggregate errors
- * have an "errors" property, which is an array of error objects with more
- * detail about each error that occurred.
- * @property AGGREGATE_ERROR
- * @static
- * @final
- */
-ParseError.AGGREGATE_ERROR = 600;
-
-/**
- * Error code indicating the client was unable to read an input file.
- * @property FILE_READ_ERROR
- * @static
- * @final
- */
-ParseError.FILE_READ_ERROR = 601;
-
-/**
- * Error code indicating a real error code is unavailable because
- * we had to use an XDomainRequest object to allow CORS requests in
- * Internet Explorer, which strips the body from HTTP responses that have
- * a non-2XX status code.
- * @property X_DOMAIN_REQUEST
- * @static
- * @final
- */
-ParseError.X_DOMAIN_REQUEST = 602;
\ No newline at end of file
diff --git a/lib/node/ParseFile.js b/lib/node/ParseFile.js
deleted file mode 100644
index 48efdb9d8..000000000
--- a/lib/node/ParseFile.js
+++ /dev/null
@@ -1,291 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-
-var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
-
-var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
-
-var _createClass2 = require('babel-runtime/helpers/createClass');
-
-var _createClass3 = _interopRequireDefault(_createClass2);
-
-var _CoreManager = require('./CoreManager');
-
-var _CoreManager2 = _interopRequireDefault(_CoreManager);
-
-var _ParsePromise = require('./ParsePromise');
-
-var _ParsePromise2 = _interopRequireDefault(_ParsePromise);
-
-function _interopRequireDefault(obj) {
- return obj && obj.__esModule ? obj : { default: obj };
-}
-
-/**
- * Copyright (c) 2015-present, Parse, LLC.
- * All rights reserved.
- *
- * This source code is licensed under the BSD-style license found in the
- * LICENSE file in the root directory of this source tree. An additional grant
- * of patent rights can be found in the PATENTS file in the same directory.
- *
- *
- */
-
-var dataUriRegexp = /^data:([a-zA-Z]*\/[a-zA-Z+.-]*);(charset=[a-zA-Z0-9\-\/\s]*,)?base64,/;
-
-function b64Digit(number) {
- if (number < 26) {
- return String.fromCharCode(65 + number);
- }
- if (number < 52) {
- return String.fromCharCode(97 + (number - 26));
- }
- if (number < 62) {
- return String.fromCharCode(48 + (number - 52));
- }
- if (number === 62) {
- return '+';
- }
- if (number === 63) {
- return '/';
- }
- throw new TypeError('Tried to encode large digit ' + number + ' in base64.');
-}
-
-/**
- * A Parse.File is a local representation of a file that is saved to the Parse
- * cloud.
- * @class Parse.File
- * @constructor
- * @param name {String} The file's name. This will be prefixed by a unique
- * value once the file has finished saving. The file name must begin with
- * an alphanumeric character, and consist of alphanumeric characters,
- * periods, spaces, underscores, or dashes.
- * @param data {Array} The data for the file, as either:
- * 1. an Array of byte value Numbers, or
- * 2. an Object like { base64: "..." } with a base64-encoded String.
- * 3. a File object selected with a file upload control. (3) only works
- * in Firefox 3.6+, Safari 6.0.2+, Chrome 7+, and IE 10+.
- * For example:- * var fileUploadControl = $("#profilePhotoFileUpload")[0]; - * if (fileUploadControl.files.length > 0) { - * var file = fileUploadControl.files[0]; - * var name = "photo.jpg"; - * var parseFile = new Parse.File(name, file); - * parseFile.save().then(function() { - * // The file has been saved to Parse. - * }, function(error) { - * // The file either could not be read, or could not be saved to Parse. - * }); - * }- * @param type {String} Optional Content-Type header to use for the file. If - * this is omitted, the content type will be inferred from the name's - * extension. - */ - -var ParseFile = function () { - function ParseFile(name, data, type) { - (0, _classCallCheck3.default)(this, ParseFile); - - var specifiedType = type || ''; - - this._name = name; - - if (data !== undefined) { - if (Array.isArray(data)) { - this._source = { - format: 'base64', - base64: ParseFile.encodeBase64(data), - type: specifiedType - }; - } else if (typeof File !== 'undefined' && data instanceof File) { - this._source = { - format: 'file', - file: data, - type: specifiedType - }; - } else if (data && typeof data.base64 === 'string') { - var _base = data.base64; - var commaIndex = _base.indexOf(','); - - if (commaIndex !== -1) { - var matches = dataUriRegexp.exec(_base.slice(0, commaIndex + 1)); - // if data URI with type and charset, there will be 4 matches. - this._source = { - format: 'base64', - base64: _base.slice(commaIndex + 1), - type: matches[1] - }; - } else { - this._source = { - format: 'base64', - base64: _base, - type: specifiedType - }; - } - } else { - throw new TypeError('Cannot create a Parse.File with that data.'); - } - } - } - - /** - * Gets the name of the file. Before save is called, this is the filename - * given by the user. After save is called, that name gets prefixed with a - * unique identifier. - * @method name - * @return {String} - */ - - (0, _createClass3.default)(ParseFile, [{ - key: 'name', - value: function () { - return this._name; - } - - /** - * Gets the url of the file. It is only available after you save the file or - * after you get the file from a Parse.Object. - * @method url - * @param {Object} options An object to specify url options - * @return {String} - */ - - }, { - key: 'url', - value: function (options) { - options = options || {}; - if (!this._url) { - return; - } - if (options.forceSecure) { - return this._url.replace(/^http:\/\//i, 'https://'); - } else { - return this._url; - } - } - - /** - * Saves the file to the Parse cloud. - * @method save - * @param {Object} options A Backbone-style options object. - * @return {Parse.Promise} Promise that is resolved when the save finishes. - */ - - }, { - key: 'save', - value: function (options) { - var _this = this; - - options = options || {}; - var controller = _CoreManager2.default.getFileController(); - if (!this._previousSave) { - if (this._source.format === 'file') { - this._previousSave = controller.saveFile(this._name, this._source).then(function (res) { - _this._name = res.name; - _this._url = res.url; - return _this; - }); - } else { - this._previousSave = controller.saveBase64(this._name, this._source).then(function (res) { - _this._name = res.name; - _this._url = res.url; - return _this; - }); - } - } - if (this._previousSave) { - return this._previousSave._thenRunCallbacks(options); - } - } - }, { - key: 'toJSON', - value: function () { - return { - __type: 'File', - name: this._name, - url: this._url - }; - } - }, { - key: 'equals', - value: function (other) { - if (this === other) { - return true; - } - // Unsaved Files are never equal, since they will be saved to different URLs - return other instanceof ParseFile && this.name() === other.name() && this.url() === other.url() && typeof this.url() !== 'undefined'; - } - }], [{ - key: 'fromJSON', - value: function (obj) { - if (obj.__type !== 'File') { - throw new TypeError('JSON object does not represent a ParseFile'); - } - var file = new ParseFile(obj.name); - file._url = obj.url; - return file; - } - }, { - key: 'encodeBase64', - value: function (bytes) { - var chunks = []; - chunks.length = Math.ceil(bytes.length / 3); - for (var i = 0; i < chunks.length; i++) { - var b1 = bytes[i * 3]; - var b2 = bytes[i * 3 + 1] || 0; - var b3 = bytes[i * 3 + 2] || 0; - - var has2 = i * 3 + 1 < bytes.length; - var has3 = i * 3 + 2 < bytes.length; - - chunks[i] = [b64Digit(b1 >> 2 & 0x3F), b64Digit(b1 << 4 & 0x30 | b2 >> 4 & 0x0F), has2 ? b64Digit(b2 << 2 & 0x3C | b3 >> 6 & 0x03) : '=', has3 ? b64Digit(b3 & 0x3F) : '='].join(''); - } - - return chunks.join(''); - } - }]); - return ParseFile; -}(); - -exports.default = ParseFile; - -var DefaultController = { - saveFile: function (name, source) { - if (source.format !== 'file') { - throw new Error('saveFile can only be used with File-type sources.'); - } - // To directly upload a File, we use a REST-style AJAX request - var headers = { - 'X-Parse-Application-ID': _CoreManager2.default.get('APPLICATION_ID'), - 'X-Parse-JavaScript-Key': _CoreManager2.default.get('JAVASCRIPT_KEY'), - 'Content-Type': source.type || (source.file ? source.file.type : null) - }; - var url = _CoreManager2.default.get('SERVER_URL'); - if (url[url.length - 1] !== '/') { - url += '/'; - } - url += 'files/' + name; - return _CoreManager2.default.getRESTController().ajax('POST', url, source.file, headers); - }, - - saveBase64: function (name, source) { - if (source.format !== 'base64') { - throw new Error('saveBase64 can only be used with Base64-type sources.'); - } - var data = { - base64: source.base64 - }; - if (source.type) { - data._ContentType = source.type; - } - - return _CoreManager2.default.getRESTController().request('POST', 'files/' + name, data); - } -}; - -_CoreManager2.default.setFileController(DefaultController); \ No newline at end of file diff --git a/lib/node/ParseGeoPoint.js b/lib/node/ParseGeoPoint.js deleted file mode 100644 index 98691f85b..000000000 --- a/lib/node/ParseGeoPoint.js +++ /dev/null @@ -1,235 +0,0 @@ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _typeof2 = require('babel-runtime/helpers/typeof'); - -var _typeof3 = _interopRequireDefault(_typeof2); - -var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); - -var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); - -var _createClass2 = require('babel-runtime/helpers/createClass'); - -var _createClass3 = _interopRequireDefault(_createClass2); - -var _ParsePromise = require('./ParsePromise'); - -var _ParsePromise2 = _interopRequireDefault(_ParsePromise); - -function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { default: obj }; -} - -/** - * Creates a new GeoPoint with any of the following forms:
- * new GeoPoint(otherGeoPoint) - * new GeoPoint(30, 30) - * new GeoPoint([30, 30]) - * new GeoPoint({latitude: 30, longitude: 30}) - * new GeoPoint() // defaults to (0, 0) - *- * @class Parse.GeoPoint - * @constructor - * - *
Represents a latitude / longitude point that may be associated - * with a key in a ParseObject or used as a reference point for geo queries. - * This allows proximity-based queries on the key.
- * - *Only one key in a class may contain a GeoPoint.
- * - *Example:
- * var point = new Parse.GeoPoint(30.0, -20.0); - * var object = new Parse.Object("PlaceObject"); - * object.set("location", point); - * object.save();- */ -var ParseGeoPoint = function () { - function ParseGeoPoint(arg1, arg2) { - (0, _classCallCheck3.default)(this, ParseGeoPoint); - - if (Array.isArray(arg1)) { - ParseGeoPoint._validate(arg1[0], arg1[1]); - this._latitude = arg1[0]; - this._longitude = arg1[1]; - } else if ((typeof arg1 === 'undefined' ? 'undefined' : (0, _typeof3.default)(arg1)) === 'object') { - ParseGeoPoint._validate(arg1.latitude, arg1.longitude); - this._latitude = arg1.latitude; - this._longitude = arg1.longitude; - } else if (typeof arg1 === 'number' && typeof arg2 === 'number') { - ParseGeoPoint._validate(arg1, arg2); - this._latitude = arg1; - this._longitude = arg2; - } else { - this._latitude = 0; - this._longitude = 0; - } - } - - /** - * North-south portion of the coordinate, in range [-90, 90]. - * Throws an exception if set out of range in a modern browser. - * @property latitude - * @type Number - */ - - (0, _createClass3.default)(ParseGeoPoint, [{ - key: 'toJSON', - - /** - * Returns a JSON representation of the GeoPoint, suitable for Parse. - * @method toJSON - * @return {Object} - */ - value: function () { - ParseGeoPoint._validate(this._latitude, this._longitude); - return { - __type: 'GeoPoint', - latitude: this._latitude, - longitude: this._longitude - }; - } - }, { - key: 'equals', - value: function (other) { - return other instanceof ParseGeoPoint && this.latitude === other.latitude && this.longitude === other.longitude; - } - - /** - * Returns the distance from this GeoPoint to another in radians. - * @method radiansTo - * @param {Parse.GeoPoint} point the other Parse.GeoPoint. - * @return {Number} - */ - - }, { - key: 'radiansTo', - value: function (point) { - var d2r = Math.PI / 180.0; - var lat1rad = this.latitude * d2r; - var long1rad = this.longitude * d2r; - var lat2rad = point.latitude * d2r; - var long2rad = point.longitude * d2r; - - var sinDeltaLatDiv2 = Math.sin((lat1rad - lat2rad) / 2); - var sinDeltaLongDiv2 = Math.sin((long1rad - long2rad) / 2); - // Square of half the straight line chord distance between both points. - var a = sinDeltaLatDiv2 * sinDeltaLatDiv2 + Math.cos(lat1rad) * Math.cos(lat2rad) * sinDeltaLongDiv2 * sinDeltaLongDiv2; - a = Math.min(1.0, a); - return 2 * Math.asin(Math.sqrt(a)); - } - - /** - * Returns the distance from this GeoPoint to another in kilometers. - * @method kilometersTo - * @param {Parse.GeoPoint} point the other Parse.GeoPoint. - * @return {Number} - */ - - }, { - key: 'kilometersTo', - value: function (point) { - return this.radiansTo(point) * 6371.0; - } - - /** - * Returns the distance from this GeoPoint to another in miles. - * @method milesTo - * @param {Parse.GeoPoint} point the other Parse.GeoPoint. - * @return {Number} - */ - - }, { - key: 'milesTo', - value: function (point) { - return this.radiansTo(point) * 3958.8; - } - - /** - * Throws an exception if the given lat-long is out of bounds. - */ - - }, { - key: 'latitude', - get: function () { - return this._latitude; - }, - set: function (val) { - ParseGeoPoint._validate(val, this.longitude); - this._latitude = val; - } - - /** - * East-west portion of the coordinate, in range [-180, 180]. - * Throws if set out of range in a modern browser. - * @property longitude - * @type Number - */ - - }, { - key: 'longitude', - get: function () { - return this._longitude; - }, - set: function (val) { - ParseGeoPoint._validate(this.latitude, val); - this._longitude = val; - } - }], [{ - key: '_validate', - value: function (latitude, longitude) { - if (latitude !== latitude || longitude !== longitude) { - throw new TypeError('GeoPoint latitude and longitude must be valid numbers'); - } - if (latitude < -90.0) { - throw new TypeError('GeoPoint latitude out of bounds: ' + latitude + ' < -90.0.'); - } - if (latitude > 90.0) { - throw new TypeError('GeoPoint latitude out of bounds: ' + latitude + ' > 90.0.'); - } - if (longitude < -180.0) { - throw new TypeError('GeoPoint longitude out of bounds: ' + longitude + ' < -180.0.'); - } - if (longitude > 180.0) { - throw new TypeError('GeoPoint longitude out of bounds: ' + longitude + ' > 180.0.'); - } - } - - /** - * Creates a GeoPoint with the user's current location, if available. - * Calls options.success with a new GeoPoint instance or calls options.error. - * @method current - * @param {Object} options An object with success and error callbacks. - * @static - */ - - }, { - key: 'current', - value: function (options) { - var promise = new _ParsePromise2.default(); - navigator.geolocation.getCurrentPosition(function (location) { - promise.resolve(new ParseGeoPoint(location.coords.latitude, location.coords.longitude)); - }, function (error) { - promise.reject(error); - }); - - return promise._thenRunCallbacks(options); - } - }]); - return ParseGeoPoint; -}(); /** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * - */ - -exports.default = ParseGeoPoint; \ No newline at end of file diff --git a/lib/node/ParseHooks.js b/lib/node/ParseHooks.js deleted file mode 100644 index a2057e899..000000000 --- a/lib/node/ParseHooks.js +++ /dev/null @@ -1,162 +0,0 @@ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _promise = require('babel-runtime/core-js/promise'); - -var _promise2 = _interopRequireDefault(_promise); - -exports.getFunctions = getFunctions; -exports.getTriggers = getTriggers; -exports.getFunction = getFunction; -exports.getTrigger = getTrigger; -exports.createFunction = createFunction; -exports.createTrigger = createTrigger; -exports.create = create; -exports.updateFunction = updateFunction; -exports.updateTrigger = updateTrigger; -exports.update = update; -exports.removeFunction = removeFunction; -exports.removeTrigger = removeTrigger; -exports.remove = remove; - -var _CoreManager = require('./CoreManager'); - -var _CoreManager2 = _interopRequireDefault(_CoreManager); - -var _decode = require('./decode'); - -var _decode2 = _interopRequireDefault(_decode); - -var _encode = require('./encode'); - -var _encode2 = _interopRequireDefault(_encode); - -var _ParseError = require('./ParseError'); - -var _ParseError2 = _interopRequireDefault(_ParseError); - -var _ParsePromise = require('./ParsePromise'); - -var _ParsePromise2 = _interopRequireDefault(_ParsePromise); - -function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { default: obj }; -} - -function getFunctions() { - return _CoreManager2.default.getHooksController().get("functions"); -} - -function getTriggers() { - return _CoreManager2.default.getHooksController().get("triggers"); -} - -function getFunction(name) { - return _CoreManager2.default.getHooksController().get("functions", name); -} - -function getTrigger(className, triggerName) { - return _CoreManager2.default.getHooksController().get("triggers", className, triggerName); -} - -function createFunction(functionName, url) { - return create({ functionName: functionName, url: url }); -} - -function createTrigger(className, triggerName, url) { - return create({ className: className, triggerName: triggerName, url: url }); -} - -function create(hook) { - return _CoreManager2.default.getHooksController().create(hook); -} - -function updateFunction(functionName, url) { - return update({ functionName: functionName, url: url }); -} - -function updateTrigger(className, triggerName, url) { - return update({ className: className, triggerName: triggerName, url: url }); -} - -function update(hook) { - return _CoreManager2.default.getHooksController().update(hook); -} - -function removeFunction(functionName) { - return remove({ functionName: functionName }); -} - -function removeTrigger(className, triggerName) { - return remove({ className: className, triggerName: triggerName }); -} - -function remove(hook) { - return _CoreManager2.default.getHooksController().remove(hook); -} - -var DefaultController = { - get: function (type, functionName, triggerName) { - var url = "/hooks/" + type; - if (functionName) { - url += "/" + functionName; - if (triggerName) { - url += "/" + triggerName; - } - } - return this.sendRequest("GET", url); - }, - create: function (hook) { - var url; - if (hook.functionName && hook.url) { - url = "/hooks/functions"; - } else if (hook.className && hook.triggerName && hook.url) { - url = "/hooks/triggers"; - } else { - return _promise2.default.reject({ error: 'invalid hook declaration', code: 143 }); - } - return this.sendRequest("POST", url, hook); - }, - remove: function (hook) { - var url; - if (hook.functionName) { - url = "/hooks/functions/" + hook.functionName; - delete hook.functionName; - } else if (hook.className && hook.triggerName) { - url = "/hooks/triggers/" + hook.className + "/" + hook.triggerName; - delete hook.className; - delete hook.triggerName; - } else { - return _promise2.default.reject({ error: 'invalid hook declaration', code: 143 }); - } - return this.sendRequest("PUT", url, { "__op": "Delete" }); - }, - update: function (hook) { - var url; - if (hook.functionName && hook.url) { - url = "/hooks/functions/" + hook.functionName; - delete hook.functionName; - } else if (hook.className && hook.triggerName && hook.url) { - url = "/hooks/triggers/" + hook.className + "/" + hook.triggerName; - delete hook.className; - delete hook.triggerName; - } else { - return _promise2.default.reject({ error: 'invalid hook declaration', code: 143 }); - } - return this.sendRequest('PUT', url, hook); - }, - sendRequest: function (method, url, body) { - return _CoreManager2.default.getRESTController().request(method, url, body, { useMasterKey: true }).then(function (res) { - var decoded = (0, _decode2.default)(res); - if (decoded) { - return _ParsePromise2.default.as(decoded); - } - return _ParsePromise2.default.error(new _ParseError2.default(_ParseError2.default.INVALID_JSON, 'The server returned an invalid response.')); - }); - } -}; - -_CoreManager2.default.setHooksController(DefaultController); \ No newline at end of file diff --git a/lib/node/ParseInstallation.js b/lib/node/ParseInstallation.js deleted file mode 100644 index 02d7be1d8..000000000 --- a/lib/node/ParseInstallation.js +++ /dev/null @@ -1,65 +0,0 @@ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _typeof2 = require('babel-runtime/helpers/typeof'); - -var _typeof3 = _interopRequireDefault(_typeof2); - -var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); - -var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); - -var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); - -var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); - -var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); - -var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); - -var _inherits2 = require('babel-runtime/helpers/inherits'); - -var _inherits3 = _interopRequireDefault(_inherits2); - -var _ParseObject2 = require('./ParseObject'); - -var _ParseObject3 = _interopRequireDefault(_ParseObject2); - -function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { default: obj }; -} - -var Installation = function (_ParseObject) { - (0, _inherits3.default)(Installation, _ParseObject); - - function Installation(attributes) { - (0, _classCallCheck3.default)(this, Installation); - - var _this = (0, _possibleConstructorReturn3.default)(this, (Installation.__proto__ || (0, _getPrototypeOf2.default)(Installation)).call(this, '_Installation')); - - if (attributes && (typeof attributes === 'undefined' ? 'undefined' : (0, _typeof3.default)(attributes)) === 'object') { - if (!_this.set(attributes || {})) { - throw new Error('Can\'t create an invalid Session'); - } - } - return _this; - } - - return Installation; -}(_ParseObject3.default); /** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * - */ - -exports.default = Installation; - -_ParseObject3.default.registerSubclass('_Installation', Installation); \ No newline at end of file diff --git a/lib/node/ParseLiveQuery.js b/lib/node/ParseLiveQuery.js deleted file mode 100644 index a3e5fe31d..000000000 --- a/lib/node/ParseLiveQuery.js +++ /dev/null @@ -1,241 +0,0 @@ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _EventEmitter = require('./EventEmitter'); - -var _EventEmitter2 = _interopRequireDefault(_EventEmitter); - -var _LiveQueryClient = require('./LiveQueryClient'); - -var _LiveQueryClient2 = _interopRequireDefault(_LiveQueryClient); - -var _CoreManager = require('./CoreManager'); - -var _CoreManager2 = _interopRequireDefault(_CoreManager); - -var _ParsePromise = require('./ParsePromise'); - -var _ParsePromise2 = _interopRequireDefault(_ParsePromise); - -function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { default: obj }; -} - -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * - */ - -function open() { - var LiveQueryController = _CoreManager2.default.getLiveQueryController(); - LiveQueryController.open(); -} - -function close() { - var LiveQueryController = _CoreManager2.default.getLiveQueryController(); - LiveQueryController.close(); -} - -/** - * - * We expose three events to help you monitor the status of the WebSocket connection: - * - *
Open - When we establish the WebSocket connection to the LiveQuery server, you'll get this event. - * - *
- * Parse.LiveQuery.on('open', () => { - * - * });- * - *
Close - When we lose the WebSocket connection to the LiveQuery server, you'll get this event. - * - *
- * Parse.LiveQuery.on('close', () => { - * - * });- * - *
Error - When some network error or LiveQuery server error happens, you'll get this event. - * - *
- * Parse.LiveQuery.on('error', (error) => { - * - * });- * - * @class Parse.LiveQuery - * @static - * - */ -var LiveQuery = new _EventEmitter2.default(); - -/** - * After open is called, the LiveQuery will try to send a connect request - * to the LiveQuery server. - * - * @method open - */ -LiveQuery.open = open; - -/** - * When you're done using LiveQuery, you can call Parse.LiveQuery.close(). - * This function will close the WebSocket connection to the LiveQuery server, - * cancel the auto reconnect, and unsubscribe all subscriptions based on it. - * If you call query.subscribe() after this, we'll create a new WebSocket - * connection to the LiveQuery server. - * - * @method close - */ - -LiveQuery.close = close; -// Register a default onError callback to make sure we do not crash on error -LiveQuery.on('error', function () {}); - -exports.default = LiveQuery; - -function getSessionToken() { - var controller = _CoreManager2.default.getUserController(); - return controller.currentUserAsync().then(function (currentUser) { - return currentUser ? currentUser.getSessionToken() : undefined; - }); -} - -function getLiveQueryClient() { - return _CoreManager2.default.getLiveQueryController().getDefaultLiveQueryClient(); -} - -var defaultLiveQueryClient = void 0; -var DefaultLiveQueryController = { - setDefaultLiveQueryClient: function (liveQueryClient) { - defaultLiveQueryClient = liveQueryClient; - }, - getDefaultLiveQueryClient: function () { - if (defaultLiveQueryClient) { - return _ParsePromise2.default.as(defaultLiveQueryClient); - } - - return getSessionToken().then(function (sessionToken) { - var liveQueryServerURL = _CoreManager2.default.get('LIVEQUERY_SERVER_URL'); - - if (liveQueryServerURL && liveQueryServerURL.indexOf('ws') !== 0) { - throw new Error('You need to set a proper Parse LiveQuery server url before using LiveQueryClient'); - } - - // If we can not find Parse.liveQueryServerURL, we try to extract it from Parse.serverURL - if (!liveQueryServerURL) { - var tempServerURL = _CoreManager2.default.get('SERVER_URL'); - var protocol = 'ws://'; - // If Parse is being served over SSL/HTTPS, ensure LiveQuery Server uses 'wss://' prefix - if (tempServerURL.indexOf('https') === 0) { - protocol = 'wss://'; - } - var host = tempServerURL.replace(/^https?:\/\//, ''); - liveQueryServerURL = protocol + host; - _CoreManager2.default.set('LIVEQUERY_SERVER_URL', liveQueryServerURL); - } - - var applicationId = _CoreManager2.default.get('APPLICATION_ID'); - var javascriptKey = _CoreManager2.default.get('JAVASCRIPT_KEY'); - var masterKey = _CoreManager2.default.get('MASTER_KEY'); - // Get currentUser sessionToken if possible - defaultLiveQueryClient = new _LiveQueryClient2.default({ - applicationId: applicationId, - serverURL: liveQueryServerURL, - javascriptKey: javascriptKey, - masterKey: masterKey, - sessionToken: sessionToken - }); - // Register a default onError callback to make sure we do not crash on error - // Cannot create these events on a nested way because of EventEmiiter from React Native - defaultLiveQueryClient.on('error', function (error) { - LiveQuery.emit('error', error); - }); - defaultLiveQueryClient.on('open', function () { - LiveQuery.emit('open'); - }); - defaultLiveQueryClient.on('close', function () { - LiveQuery.emit('close'); - }); - - return defaultLiveQueryClient; - }); - }, - open: function () { - var _this = this; - - getLiveQueryClient().then(function (liveQueryClient) { - _this.resolve(liveQueryClient.open()); - }); - }, - close: function () { - var _this2 = this; - - getLiveQueryClient().then(function (liveQueryClient) { - _this2.resolve(liveQueryClient.close()); - }); - }, - subscribe: function (query) { - var _this3 = this; - - var subscriptionWrap = new _EventEmitter2.default(); - - getLiveQueryClient().then(function (liveQueryClient) { - if (liveQueryClient.shouldOpen()) { - liveQueryClient.open(); - } - var promiseSessionToken = getSessionToken(); - // new event emitter - return promiseSessionToken.then(function (sessionToken) { - - var subscription = liveQueryClient.subscribe(query, sessionToken); - // enter, leave create, etc - - subscriptionWrap.id = subscription.id; - subscriptionWrap.query = subscription.query; - subscriptionWrap.sessionToken = subscription.sessionToken; - subscriptionWrap.unsubscribe = subscription.unsubscribe; - // Cannot create these events on a nested way because of EventEmiiter from React Native - subscription.on('open', function () { - subscriptionWrap.emit('open'); - }); - subscription.on('create', function (object) { - subscriptionWrap.emit('create', object); - }); - subscription.on('update', function (object) { - subscriptionWrap.emit('update', object); - }); - subscription.on('enter', function (object) { - subscriptionWrap.emit('enter', object); - }); - subscription.on('leave', function (object) { - subscriptionWrap.emit('leave', object); - }); - subscription.on('delete', function (object) { - subscriptionWrap.emit('delete', object); - }); - - _this3.resolve(); - }); - }); - return subscriptionWrap; - }, - unsubscribe: function (subscription) { - var _this4 = this; - - getLiveQueryClient().then(function (liveQueryClient) { - _this4.resolve(liveQueryClient.unsubscribe(subscription)); - }); - }, - _clearCachedDefaultClient: function () { - defaultLiveQueryClient = null; - } -}; - -_CoreManager2.default.setLiveQueryController(DefaultLiveQueryController); \ No newline at end of file diff --git a/lib/node/ParseObject.js b/lib/node/ParseObject.js deleted file mode 100644 index f08795e52..000000000 --- a/lib/node/ParseObject.js +++ /dev/null @@ -1,2001 +0,0 @@ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _defineProperty = require('babel-runtime/core-js/object/define-property'); - -var _defineProperty2 = _interopRequireDefault(_defineProperty); - -var _create = require('babel-runtime/core-js/object/create'); - -var _create2 = _interopRequireDefault(_create); - -var _freeze = require('babel-runtime/core-js/object/freeze'); - -var _freeze2 = _interopRequireDefault(_freeze); - -var _stringify = require('babel-runtime/core-js/json/stringify'); - -var _stringify2 = _interopRequireDefault(_stringify); - -var _keys = require('babel-runtime/core-js/object/keys'); - -var _keys2 = _interopRequireDefault(_keys); - -var _typeof2 = require('babel-runtime/helpers/typeof'); - -var _typeof3 = _interopRequireDefault(_typeof2); - -var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); - -var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); - -var _createClass2 = require('babel-runtime/helpers/createClass'); - -var _createClass3 = _interopRequireDefault(_createClass2); - -var _CoreManager = require('./CoreManager'); - -var _CoreManager2 = _interopRequireDefault(_CoreManager); - -var _canBeSerialized = require('./canBeSerialized'); - -var _canBeSerialized2 = _interopRequireDefault(_canBeSerialized); - -var _decode = require('./decode'); - -var _decode2 = _interopRequireDefault(_decode); - -var _encode = require('./encode'); - -var _encode2 = _interopRequireDefault(_encode); - -var _equals = require('./equals'); - -var _equals2 = _interopRequireDefault(_equals); - -var _escape2 = require('./escape'); - -var _escape3 = _interopRequireDefault(_escape2); - -var _ParseACL = require('./ParseACL'); - -var _ParseACL2 = _interopRequireDefault(_ParseACL); - -var _parseDate = require('./parseDate'); - -var _parseDate2 = _interopRequireDefault(_parseDate); - -var _ParseError = require('./ParseError'); - -var _ParseError2 = _interopRequireDefault(_ParseError); - -var _ParseFile = require('./ParseFile'); - -var _ParseFile2 = _interopRequireDefault(_ParseFile); - -var _ParseOp = require('./ParseOp'); - -var _ParsePromise = require('./ParsePromise'); - -var _ParsePromise2 = _interopRequireDefault(_ParsePromise); - -var _ParseQuery = require('./ParseQuery'); - -var _ParseQuery2 = _interopRequireDefault(_ParseQuery); - -var _ParseRelation = require('./ParseRelation'); - -var _ParseRelation2 = _interopRequireDefault(_ParseRelation); - -var _SingleInstanceStateController = require('./SingleInstanceStateController'); - -var SingleInstanceStateController = _interopRequireWildcard(_SingleInstanceStateController); - -var _unique = require('./unique'); - -var _unique2 = _interopRequireDefault(_unique); - -var _UniqueInstanceStateController = require('./UniqueInstanceStateController'); - -var UniqueInstanceStateController = _interopRequireWildcard(_UniqueInstanceStateController); - -var _unsavedChildren = require('./unsavedChildren'); - -var _unsavedChildren2 = _interopRequireDefault(_unsavedChildren); - -function _interopRequireWildcard(obj) { - if (obj && obj.__esModule) { - return obj; - } else { - var newObj = {};if (obj != null) { - for (var key in obj) { - if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; - } - }newObj.default = obj;return newObj; - } -} - -function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { default: obj }; -} - -// Mapping of class names to constructors, so we can populate objects from the -// server with appropriate subclasses of ParseObject -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * - */ - -var classMap = {}; - -// Global counter for generating unique local Ids -var localCount = 0; -// Global counter for generating unique Ids for non-single-instance objects -var objectCount = 0; -// On web clients, objects are single-instance: any two objects with the same Id -// will have the same attributes. However, this may be dangerous default -// behavior in a server scenario -var singleInstance = !_CoreManager2.default.get('IS_NODE'); -if (singleInstance) { - _CoreManager2.default.setObjectStateController(SingleInstanceStateController); -} else { - _CoreManager2.default.setObjectStateController(UniqueInstanceStateController); -} - -function getServerUrlPath() { - var serverUrl = _CoreManager2.default.get('SERVER_URL'); - if (serverUrl[serverUrl.length - 1] !== '/') { - serverUrl += '/'; - } - var url = serverUrl.replace(/https?:\/\//, ''); - return url.substr(url.indexOf('/')); -} - -/** - * Creates a new model with defined attributes. - * - *
You won't normally call this method directly. It is recommended that
- * you use a subclass of Parse.Object
instead, created by calling
- * extend
.
However, if you don't want to use a subclass, or aren't sure which - * subclass is appropriate, you can use this form:
- * var object = new Parse.Object("ClassName"); - *- * That is basically equivalent to:
- * var MyClass = Parse.Object.extend("ClassName"); - * var object = new MyClass(); - *- * - * @class Parse.Object - * @constructor - * @param {String} className The class name for the object - * @param {Object} attributes The initial set of data to store in the object. - * @param {Object} options The options for this object instance. - */ - -var ParseObject = function () { - /** - * The ID of this object, unique within its class. - * @property id - * @type String - */ - function ParseObject(className, attributes, options) { - (0, _classCallCheck3.default)(this, ParseObject); - - // Enable legacy initializers - if (typeof this.initialize === 'function') { - this.initialize.apply(this, arguments); - } - - var toSet = null; - this._objCount = objectCount++; - if (typeof className === 'string') { - this.className = className; - if (attributes && (typeof attributes === 'undefined' ? 'undefined' : (0, _typeof3.default)(attributes)) === 'object') { - toSet = attributes; - } - } else if (className && (typeof className === 'undefined' ? 'undefined' : (0, _typeof3.default)(className)) === 'object') { - this.className = className.className; - toSet = {}; - for (var attr in className) { - if (attr !== 'className') { - toSet[attr] = className[attr]; - } - } - if (attributes && (typeof attributes === 'undefined' ? 'undefined' : (0, _typeof3.default)(attributes)) === 'object') { - options = attributes; - } - } - if (toSet && !this.set(toSet, options)) { - throw new Error('Can\'t create an invalid Parse Object'); - } - } - - /** Prototype getters / setters **/ - - (0, _createClass3.default)(ParseObject, [{ - key: '_getId', - - /** Private methods **/ - - /** - * Returns a local or server Id used uniquely identify this object - */ - value: function () { - if (typeof this.id === 'string') { - return this.id; - } - if (typeof this._localId === 'string') { - return this._localId; - } - var localId = 'local' + String(localCount++); - this._localId = localId; - return localId; - } - - /** - * Returns a unique identifier used to pull data from the State Controller. - */ - - }, { - key: '_getStateIdentifier', - value: function () { - if (singleInstance) { - var _id = this.id; - if (!_id) { - _id = this._getId(); - } - return { - id: _id, - className: this.className - }; - } else { - return this; - } - } - }, { - key: '_getServerData', - value: function () { - var stateController = _CoreManager2.default.getObjectStateController(); - return stateController.getServerData(this._getStateIdentifier()); - } - }, { - key: '_clearServerData', - value: function () { - var serverData = this._getServerData(); - var unset = {}; - for (var attr in serverData) { - unset[attr] = undefined; - } - var stateController = _CoreManager2.default.getObjectStateController(); - stateController.setServerData(this._getStateIdentifier(), unset); - } - }, { - key: '_getPendingOps', - value: function () { - var stateController = _CoreManager2.default.getObjectStateController(); - return stateController.getPendingOps(this._getStateIdentifier()); - } - }, { - key: '_clearPendingOps', - value: function () { - var pending = this._getPendingOps(); - var latest = pending[pending.length - 1]; - var keys = (0, _keys2.default)(latest); - keys.forEach(function (key) { - delete latest[key]; - }); - } - }, { - key: '_getDirtyObjectAttributes', - value: function () { - var attributes = this.attributes; - var stateController = _CoreManager2.default.getObjectStateController(); - var objectCache = stateController.getObjectCache(this._getStateIdentifier()); - var dirty = {}; - for (var attr in attributes) { - var val = attributes[attr]; - if (val && (typeof val === 'undefined' ? 'undefined' : (0, _typeof3.default)(val)) === 'object' && !(val instanceof ParseObject) && !(val instanceof _ParseFile2.default) && !(val instanceof _ParseRelation2.default)) { - // Due to the way browsers construct maps, the key order will not change - // unless the object is changed - try { - var json = (0, _encode2.default)(val, false, true); - var stringified = (0, _stringify2.default)(json); - if (objectCache[attr] !== stringified) { - dirty[attr] = val; - } - } catch (e) { - // Error occurred, possibly by a nested unsaved pointer in a mutable container - // No matter how it happened, it indicates a change in the attribute - dirty[attr] = val; - } - } - } - return dirty; - } - }, { - key: '_toFullJSON', - value: function (seen) { - var json = this.toJSON(seen); - json.__type = 'Object'; - json.className = this.className; - return json; - } - }, { - key: '_getSaveJSON', - value: function () { - var pending = this._getPendingOps(); - var dirtyObjects = this._getDirtyObjectAttributes(); - var json = {}; - - for (var attr in dirtyObjects) { - json[attr] = new _ParseOp.SetOp(dirtyObjects[attr]).toJSON(); - } - for (attr in pending[0]) { - json[attr] = pending[0][attr].toJSON(); - } - return json; - } - }, { - key: '_getSaveParams', - value: function () { - var method = this.id ? 'PUT' : 'POST'; - var body = this._getSaveJSON(); - var path = 'classes/' + this.className; - if (this.id) { - path += '/' + this.id; - } else if (this.className === '_User') { - path = 'users'; - } - return { - method: method, - body: body, - path: path - }; - } - }, { - key: '_finishFetch', - value: function (serverData) { - if (!this.id && serverData.objectId) { - this.id = serverData.objectId; - } - var stateController = _CoreManager2.default.getObjectStateController(); - stateController.initializeState(this._getStateIdentifier()); - var decoded = {}; - for (var attr in serverData) { - if (attr === 'ACL') { - decoded[attr] = new _ParseACL2.default(serverData[attr]); - } else if (attr !== 'objectId') { - decoded[attr] = (0, _decode2.default)(serverData[attr]); - if (decoded[attr] instanceof _ParseRelation2.default) { - decoded[attr]._ensureParentAndKey(this, attr); - } - } - } - if (decoded.createdAt && typeof decoded.createdAt === 'string') { - decoded.createdAt = (0, _parseDate2.default)(decoded.createdAt); - } - if (decoded.updatedAt && typeof decoded.updatedAt === 'string') { - decoded.updatedAt = (0, _parseDate2.default)(decoded.updatedAt); - } - if (!decoded.updatedAt && decoded.createdAt) { - decoded.updatedAt = decoded.createdAt; - } - stateController.commitServerChanges(this._getStateIdentifier(), decoded); - } - }, { - key: '_setExisted', - value: function (existed) { - var stateController = _CoreManager2.default.getObjectStateController(); - var state = stateController.getState(this._getStateIdentifier()); - if (state) { - state.existed = existed; - } - } - }, { - key: '_migrateId', - value: function (serverId) { - if (this._localId && serverId) { - if (singleInstance) { - var stateController = _CoreManager2.default.getObjectStateController(); - var oldState = stateController.removeState(this._getStateIdentifier()); - this.id = serverId; - delete this._localId; - if (oldState) { - stateController.initializeState(this._getStateIdentifier(), oldState); - } - } else { - this.id = serverId; - delete this._localId; - } - } - } - }, { - key: '_handleSaveResponse', - value: function (response, status) { - var changes = {}; - - var stateController = _CoreManager2.default.getObjectStateController(); - var pending = stateController.popPendingState(this._getStateIdentifier()); - for (var attr in pending) { - if (pending[attr] instanceof _ParseOp.RelationOp) { - changes[attr] = pending[attr].applyTo(undefined, this, attr); - } else if (!(attr in response)) { - // Only SetOps and UnsetOps should not come back with results - changes[attr] = pending[attr].applyTo(undefined); - } - } - for (attr in response) { - if ((attr === 'createdAt' || attr === 'updatedAt') && typeof response[attr] === 'string') { - changes[attr] = (0, _parseDate2.default)(response[attr]); - } else if (attr === 'ACL') { - changes[attr] = new _ParseACL2.default(response[attr]); - } else if (attr !== 'objectId') { - changes[attr] = (0, _decode2.default)(response[attr]); - if (changes[attr] instanceof _ParseOp.UnsetOp) { - changes[attr] = undefined; - } - } - } - if (changes.createdAt && !changes.updatedAt) { - changes.updatedAt = changes.createdAt; - } - - this._migrateId(response.objectId); - - if (status !== 201) { - this._setExisted(true); - } - - stateController.commitServerChanges(this._getStateIdentifier(), changes); - } - }, { - key: '_handleSaveError', - value: function () { - this._getPendingOps(); - - var stateController = _CoreManager2.default.getObjectStateController(); - stateController.mergeFirstPendingState(this._getStateIdentifier()); - } - - /** Public methods **/ - - }, { - key: 'initialize', - value: function () {} - // NOOP - - - /** - * Returns a JSON version of the object suitable for saving to Parse. - * @method toJSON - * @return {Object} - */ - - }, { - key: 'toJSON', - value: function (seen) { - var seenEntry = this.id ? this.className + ':' + this.id : this; - var seen = seen || [seenEntry]; - var json = {}; - var attrs = this.attributes; - for (var attr in attrs) { - if ((attr === 'createdAt' || attr === 'updatedAt') && attrs[attr].toJSON) { - json[attr] = attrs[attr].toJSON(); - } else { - json[attr] = (0, _encode2.default)(attrs[attr], false, false, seen); - } - } - var pending = this._getPendingOps(); - for (var attr in pending[0]) { - json[attr] = pending[0][attr].toJSON(); - } - - if (this.id) { - json.objectId = this.id; - } - return json; - } - - /** - * Determines whether this ParseObject is equal to another ParseObject - * @method equals - * @return {Boolean} - */ - - }, { - key: 'equals', - value: function (other) { - if (this === other) { - return true; - } - return other instanceof ParseObject && this.className === other.className && this.id === other.id && typeof this.id !== 'undefined'; - } - - /** - * Returns true if this object has been modified since its last - * save/refresh. If an attribute is specified, it returns true only if that - * particular attribute has been modified since the last save/refresh. - * @method dirty - * @param {String} attr An attribute name (optional). - * @return {Boolean} - */ - - }, { - key: 'dirty', - value: function (attr) { - if (!this.id) { - return true; - } - var pendingOps = this._getPendingOps(); - var dirtyObjects = this._getDirtyObjectAttributes(); - if (attr) { - if (dirtyObjects.hasOwnProperty(attr)) { - return true; - } - for (var i = 0; i < pendingOps.length; i++) { - if (pendingOps[i].hasOwnProperty(attr)) { - return true; - } - } - return false; - } - if ((0, _keys2.default)(pendingOps[0]).length !== 0) { - return true; - } - if ((0, _keys2.default)(dirtyObjects).length !== 0) { - return true; - } - return false; - } - - /** - * Returns an array of keys that have been modified since last save/refresh - * @method dirtyKeys - * @return {Array of string} - */ - - }, { - key: 'dirtyKeys', - value: function () { - var pendingOps = this._getPendingOps(); - var keys = {}; - for (var i = 0; i < pendingOps.length; i++) { - for (var attr in pendingOps[i]) { - keys[attr] = true; - } - } - var dirtyObjects = this._getDirtyObjectAttributes(); - for (var attr in dirtyObjects) { - keys[attr] = true; - } - return (0, _keys2.default)(keys); - } - - /** - * Gets a Pointer referencing this Object. - * @method toPointer - * @return {Object} - */ - - }, { - key: 'toPointer', - value: function () { - if (!this.id) { - throw new Error('Cannot create a pointer to an unsaved ParseObject'); - } - return { - __type: 'Pointer', - className: this.className, - objectId: this.id - }; - } - - /** - * Gets the value of an attribute. - * @method get - * @param {String} attr The string name of an attribute. - */ - - }, { - key: 'get', - value: function (attr) { - return this.attributes[attr]; - } - - /** - * Gets a relation on the given class for the attribute. - * @method relation - * @param String attr The attribute to get the relation for. - */ - - }, { - key: 'relation', - value: function (attr) { - var value = this.get(attr); - if (value) { - if (!(value instanceof _ParseRelation2.default)) { - throw new Error('Called relation() on non-relation field ' + attr); - } - value._ensureParentAndKey(this, attr); - return value; - } - return new _ParseRelation2.default(this, attr); - } - - /** - * Gets the HTML-escaped value of an attribute. - * @method escape - * @param {String} attr The string name of an attribute. - */ - - }, { - key: 'escape', - value: function (attr) { - var val = this.attributes[attr]; - if (val == null) { - return ''; - } - - if (typeof val !== 'string') { - if (typeof val.toString !== 'function') { - return ''; - } - val = val.toString(); - } - return (0, _escape3.default)(val); - } - - /** - * Returns
true
if the attribute contains a value that is not
- * null or undefined.
- * @method has
- * @param {String} attr The string name of the attribute.
- * @return {Boolean}
- */
-
- }, {
- key: 'has',
- value: function (attr) {
- var attributes = this.attributes;
- if (attributes.hasOwnProperty(attr)) {
- return attributes[attr] != null;
- }
- return false;
- }
-
- /**
- * Sets a hash of model attributes on the object.
- *
- * You can call it with an object containing keys and values, or with one - * key and value. For example:
- * gameTurn.set({ - * player: player1, - * diceRoll: 2 - * }, { - * error: function(gameTurnAgain, error) { - * // The set failed validation. - * } - * }); - * - * game.set("currentPlayer", player2, { - * error: function(gameTurnAgain, error) { - * // The set failed validation. - * } - * }); - * - * game.set("finished", true);- * - * @method set - * @param {String} key The key to set. - * @param {} value The value to give it. - * @param {Object} options A set of options for the set. - * The only supported option is
error
.
- * @return {Boolean} true if the set succeeded.
- */
-
- }, {
- key: 'set',
- value: function (key, value, options) {
- var changes = {};
- var newOps = {};
- if (key && (typeof key === 'undefined' ? 'undefined' : (0, _typeof3.default)(key)) === 'object') {
- changes = key;
- options = value;
- } else if (typeof key === 'string') {
- changes[key] = value;
- } else {
- return this;
- }
-
- options = options || {};
- var readonly = [];
- if (typeof this.constructor.readOnlyAttributes === 'function') {
- readonly = readonly.concat(this.constructor.readOnlyAttributes());
- }
- for (var k in changes) {
- if (k === 'createdAt' || k === 'updatedAt') {
- // This property is read-only, but for legacy reasons we silently
- // ignore it
- continue;
- }
- if (readonly.indexOf(k) > -1) {
- throw new Error('Cannot modify readonly attribute: ' + k);
- }
- if (options.unset) {
- newOps[k] = new _ParseOp.UnsetOp();
- } else if (changes[k] instanceof _ParseOp.Op) {
- newOps[k] = changes[k];
- } else if (changes[k] && (0, _typeof3.default)(changes[k]) === 'object' && typeof changes[k].__op === 'string') {
- newOps[k] = (0, _ParseOp.opFromJSON)(changes[k]);
- } else if (k === 'objectId' || k === 'id') {
- if (typeof changes[k] === 'string') {
- this.id = changes[k];
- }
- } else if (k === 'ACL' && (0, _typeof3.default)(changes[k]) === 'object' && !(changes[k] instanceof _ParseACL2.default)) {
- newOps[k] = new _ParseOp.SetOp(new _ParseACL2.default(changes[k]));
- } else {
- newOps[k] = new _ParseOp.SetOp(changes[k]);
- }
- }
-
- // Calculate new values
- var currentAttributes = this.attributes;
- var newValues = {};
- for (var attr in newOps) {
- if (newOps[attr] instanceof _ParseOp.RelationOp) {
- newValues[attr] = newOps[attr].applyTo(currentAttributes[attr], this, attr);
- } else if (!(newOps[attr] instanceof _ParseOp.UnsetOp)) {
- newValues[attr] = newOps[attr].applyTo(currentAttributes[attr]);
- }
- }
-
- // Validate changes
- if (!options.ignoreValidation) {
- var validation = this.validate(newValues);
- if (validation) {
- if (typeof options.error === 'function') {
- options.error(this, validation);
- }
- return false;
- }
- }
-
- // Consolidate Ops
- var pendingOps = this._getPendingOps();
- var last = pendingOps.length - 1;
- var stateController = _CoreManager2.default.getObjectStateController();
- for (var attr in newOps) {
- var nextOp = newOps[attr].mergeWith(pendingOps[last][attr]);
- stateController.setPendingOp(this._getStateIdentifier(), attr, nextOp);
- }
-
- return this;
- }
-
- /**
- * Remove an attribute from the model. This is a noop if the attribute doesn't
- * exist.
- * @method unset
- * @param {String} attr The string name of an attribute.
- */
-
- }, {
- key: 'unset',
- value: function (attr, options) {
- options = options || {};
- options.unset = true;
- return this.set(attr, null, options);
- }
-
- /**
- * Atomically increments the value of the given attribute the next time the
- * object is saved. If no amount is specified, 1 is used by default.
- *
- * @method increment
- * @param attr {String} The key.
- * @param amount {Number} The amount to increment by (optional).
- */
-
- }, {
- key: 'increment',
- value: function (attr, amount) {
- if (typeof amount === 'undefined') {
- amount = 1;
- }
- if (typeof amount !== 'number') {
- throw new Error('Cannot increment by a non-numeric amount.');
- }
- return this.set(attr, new _ParseOp.IncrementOp(amount));
- }
-
- /**
- * Atomically add an object to the end of the array associated with a given
- * key.
- * @method add
- * @param attr {String} The key.
- * @param item {} The item to add.
- */
-
- }, {
- key: 'add',
- value: function (attr, item) {
- return this.set(attr, new _ParseOp.AddOp([item]));
- }
-
- /**
- * Atomically add an object to the array associated with a given key, only
- * if it is not already present in the array. The position of the insert is
- * not guaranteed.
- *
- * @method addUnique
- * @param attr {String} The key.
- * @param item {} The object to add.
- */
-
- }, {
- key: 'addUnique',
- value: function (attr, item) {
- return this.set(attr, new _ParseOp.AddUniqueOp([item]));
- }
-
- /**
- * Atomically remove all instances of an object from the array associated
- * with a given key.
- *
- * @method remove
- * @param attr {String} The key.
- * @param item {} The object to remove.
- */
-
- }, {
- key: 'remove',
- value: function (attr, item) {
- return this.set(attr, new _ParseOp.RemoveOp([item]));
- }
-
- /**
- * Returns an instance of a subclass of Parse.Op describing what kind of
- * modification has been performed on this field since the last time it was
- * saved. For example, after calling object.increment("x"), calling
- * object.op("x") would return an instance of Parse.Op.Increment.
- *
- * @method op
- * @param attr {String} The key.
- * @returns {Parse.Op} The operation, or undefined if none.
- */
-
- }, {
- key: 'op',
- value: function (attr) {
- var pending = this._getPendingOps();
- for (var i = pending.length; i--;) {
- if (pending[i][attr]) {
- return pending[i][attr];
- }
- }
- }
-
- /**
- * Creates a new model with identical attributes to this one, similar to Backbone.Model's clone()
- * @method clone
- * @return {Parse.Object}
- */
-
- }, {
- key: 'clone',
- value: function () {
- var clone = new this.constructor();
- if (!clone.className) {
- clone.className = this.className;
- }
- var attributes = this.attributes;
- if (typeof this.constructor.readOnlyAttributes === 'function') {
- var readonly = this.constructor.readOnlyAttributes() || [];
- // Attributes are frozen, so we have to rebuild an object,
- // rather than delete readonly keys
- var copy = {};
- for (var a in attributes) {
- if (readonly.indexOf(a) < 0) {
- copy[a] = attributes[a];
- }
- }
- attributes = copy;
- }
- if (clone.set) {
- clone.set(attributes);
- }
- return clone;
- }
-
- /**
- * Creates a new instance of this object. Not to be confused with clone()
- * @method newInstance
- * @return {Parse.Object}
- */
-
- }, {
- key: 'newInstance',
- value: function () {
- var clone = new this.constructor();
- if (!clone.className) {
- clone.className = this.className;
- }
- clone.id = this.id;
- if (singleInstance) {
- // Just return an object with the right id
- return clone;
- }
-
- var stateController = _CoreManager2.default.getObjectStateController();
- if (stateController) {
- stateController.duplicateState(this._getStateIdentifier(), clone._getStateIdentifier());
- }
- return clone;
- }
-
- /**
- * Returns true if this object has never been saved to Parse.
- * @method isNew
- * @return {Boolean}
- */
-
- }, {
- key: 'isNew',
- value: function () {
- return !this.id;
- }
-
- /**
- * Returns true if this object was created by the Parse server when the
- * object might have already been there (e.g. in the case of a Facebook
- * login)
- * @method existed
- * @return {Boolean}
- */
-
- }, {
- key: 'existed',
- value: function () {
- if (!this.id) {
- return false;
- }
- var stateController = _CoreManager2.default.getObjectStateController();
- var state = stateController.getState(this._getStateIdentifier());
- if (state) {
- return state.existed;
- }
- return false;
- }
-
- /**
- * Checks if the model is currently in a valid state.
- * @method isValid
- * @return {Boolean}
- */
-
- }, {
- key: 'isValid',
- value: function () {
- return !this.validate(this.attributes);
- }
-
- /**
- * You should not call this function directly unless you subclass
- * Parse.Object
, in which case you can override this method
- * to provide additional validation on set
and
- * save
. Your implementation should return
- *
- * @method validate
- * @param {Object} attrs The current data to validate.
- * @return {} False if the data is valid. An error object otherwise.
- * @see Parse.Object#set
- */
-
- }, {
- key: 'validate',
- value: function (attrs) {
- if (attrs.hasOwnProperty('ACL') && !(attrs.ACL instanceof _ParseACL2.default)) {
- return new _ParseError2.default(_ParseError2.default.OTHER_CAUSE, 'ACL must be a Parse ACL.');
- }
- for (var key in attrs) {
- if (!/^[A-Za-z][0-9A-Za-z_]*$/.test(key)) {
- return new _ParseError2.default(_ParseError2.default.INVALID_KEY_NAME);
- }
- }
- return false;
- }
-
- /**
- * Returns the ACL for this object.
- * @method getACL
- * @returns {Parse.ACL} An instance of Parse.ACL.
- * @see Parse.Object#get
- */
-
- }, {
- key: 'getACL',
- value: function () {
- var acl = this.get('ACL');
- if (acl instanceof _ParseACL2.default) {
- return acl;
- }
- return null;
- }
-
- /**
- * Sets the ACL to be used for this object.
- * @method setACL
- * @param {Parse.ACL} acl An instance of Parse.ACL.
- * @param {Object} options Optional Backbone-like options object to be
- * passed in to set.
- * @return {Boolean} Whether the set passed validation.
- * @see Parse.Object#set
- */
-
- }, {
- key: 'setACL',
- value: function (acl, options) {
- return this.set('ACL', acl, options);
- }
-
- /**
- * Clears any changes to this object made since the last call to save()
- * @method revert
- */
-
- }, {
- key: 'revert',
- value: function () {
- this._clearPendingOps();
- }
-
- /**
- * Clears all attributes on a model
- * @method clear
- */
-
- }, {
- key: 'clear',
- value: function () {
- var attributes = this.attributes;
- var erasable = {};
- var readonly = ['createdAt', 'updatedAt'];
- if (typeof this.constructor.readOnlyAttributes === 'function') {
- readonly = readonly.concat(this.constructor.readOnlyAttributes());
- }
- for (var attr in attributes) {
- if (readonly.indexOf(attr) < 0) {
- erasable[attr] = true;
- }
- }
- return this.set(erasable, { unset: true });
- }
-
- /**
- * Fetch the model from the server. If the server's representation of the
- * model differs from its current attributes, they will be overriden.
- *
- * @method fetch
- * @param {Object} options A Backbone-style callback object.
- * Valid options are:- * object.save();- * or
- * object.save(null, options);- * or
- * object.save(attrs, options);- * or
- * object.save(key, value, options);- * - * For example,
- * gameTurn.save({ - * player: "Jake Cutter", - * diceRoll: 2 - * }, { - * success: function(gameTurnAgain) { - * // The save was successful. - * }, - * error: function(gameTurnAgain, error) { - * // The save failed. Error is an instance of Parse.Error. - * } - * });- * or with promises:
- * gameTurn.save({ - * player: "Jake Cutter", - * diceRoll: 2 - * }).then(function(gameTurnAgain) { - * // The save was successful. - * }, function(error) { - * // The save failed. Error is an instance of Parse.Error. - * });- * - * @method save - * @param {Object} options A Backbone-style callback object. - * Valid options are:
- * Parse.Object.fetchAll([object1, object2, ...], { - * success: function(list) { - * // All the objects were fetched. - * }, - * error: function(error) { - * // An error occurred while fetching one of the objects. - * }, - * }); - *- * - * @method fetchAll - * @param {Array} list A list of
Parse.Object
.
- * @param {Object} options A Backbone-style callback object.
- * @static
- * Valid options are:- * Parse.Object.fetchAllIfNeeded([object1, ...], { - * success: function(list) { - * // Objects were fetched and updated. - * }, - * error: function(error) { - * // An error occurred while fetching one of the objects. - * }, - * }); - *- * - * @method fetchAllIfNeeded - * @param {Array} list A list of
Parse.Object
.
- * @param {Object} options A Backbone-style callback object.
- * @static
- * Valid options are:Unlike saveAll, if an error occurs while deleting an individual model, - * this method will continue trying to delete the rest of the models if - * possible, except in the case of a fatal error like a connection error. - * - *
In particular, the Parse.Error object returned in the case of error may - * be one of two types: - * - *
- * Parse.Object.destroyAll([object1, object2, ...], { - * success: function() { - * // All the objects were deleted. - * }, - * error: function(error) { - * // An error occurred while deleting one or more of the objects. - * // If this is an aggregate error, then we can inspect each error - * // object individually to determine the reason why a particular - * // object was not deleted. - * if (error.code === Parse.Error.AGGREGATE_ERROR) { - * for (var i = 0; i < error.errors.length; i++) { - * console.log("Couldn't delete " + error.errors[i].object.id + - * "due to " + error.errors[i].message); - * } - * } else { - * console.log("Delete aborted because of " + error.message); - * } - * }, - * }); - *- * - * @method destroyAll - * @param {Array} list A list of
Parse.Object
.
- * @param {Object} options A Backbone-style callback object.
- * @static
- * Valid options are:- * Parse.Object.saveAll([object1, object2, ...], { - * success: function(list) { - * // All the objects were saved. - * }, - * error: function(error) { - * // An error occurred while saving one of the objects. - * }, - * }); - *- * - * @method saveAll - * @param {Array} list A list of
Parse.Object
.
- * @param {Object} options A Backbone-style callback object.
- * @static
- * Valid options are:A shortcut for:
- * var Foo = Parse.Object.extend("Foo"); - * var pointerToFoo = new Foo(); - * pointerToFoo.id = "myObjectId"; - *- * - * @method createWithoutData - * @param {String} id The ID of the object to create a reference to. - * @static - * @return {Parse.Object} A Parse.Object reference. - */ - - }, { - key: 'createWithoutData', - value: function (id) { - var obj = new this(); - obj.id = id; - return obj; - } - - /** - * Creates a new instance of a Parse Object from a JSON representation. - * @method fromJSON - * @param {Object} json The JSON map of the Object's data - * @param {boolean} override In single instance mode, all old server data - * is overwritten if this is set to true - * @static - * @return {Parse.Object} A Parse.Object reference - */ - - }, { - key: 'fromJSON', - value: function (json, override) { - if (!json.className) { - throw new Error('Cannot create an object without a className'); - } - var constructor = classMap[json.className]; - var o = constructor ? new constructor() : new ParseObject(json.className); - var otherAttributes = {}; - for (var attr in json) { - if (attr !== 'className' && attr !== '__type') { - otherAttributes[attr] = json[attr]; - } - } - if (override) { - // id needs to be set before clearServerData can work - if (otherAttributes.objectId) { - o.id = otherAttributes.objectId; - } - var preserved = null; - if (typeof o._preserveFieldsOnFetch === 'function') { - preserved = o._preserveFieldsOnFetch(); - } - o._clearServerData(); - if (preserved) { - o._finishFetch(preserved); - } - } - o._finishFetch(otherAttributes); - if (json.objectId) { - o._setExisted(true); - } - return o; - } - - /** - * Registers a subclass of Parse.Object with a specific class name. - * When objects of that class are retrieved from a query, they will be - * instantiated with this subclass. - * This is only necessary when using ES6 subclassing. - * @method registerSubclass - * @param {String} className The class name of the subclass - * @param {Class} constructor The subclass - */ - - }, { - key: 'registerSubclass', - value: function (className, constructor) { - if (typeof className !== 'string') { - throw new TypeError('The first argument must be a valid class name.'); - } - if (typeof constructor === 'undefined') { - throw new TypeError('You must supply a subclass constructor.'); - } - if (typeof constructor !== 'function') { - throw new TypeError('You must register the subclass constructor. ' + 'Did you attempt to register an instance of the subclass?'); - } - classMap[className] = constructor; - if (!constructor.className) { - constructor.className = className; - } - } - - /** - * Creates a new subclass of Parse.Object for the given Parse class name. - * - *
Every extension of a Parse class will inherit from the most recent - * previous extension of that class. When a Parse.Object is automatically - * created by parsing JSON, it will use the most recent extension of that - * class.
- * - *You should call either:
- * var MyClass = Parse.Object.extend("MyClass", { - * Instance methods, - * initialize: function(attrs, options) { - * this.someInstanceProperty = [], - * Other instance properties - * } - * }, { - * Class properties - * });- * or, for Backbone compatibility:
- * var MyClass = Parse.Object.extend({ - * className: "MyClass", - * Instance methods, - * initialize: function(attrs, options) { - * this.someInstanceProperty = [], - * Other instance properties - * } - * }, { - * Class properties - * });- * - * @method extend - * @param {String} className The name of the Parse class backing this model. - * @param {Object} protoProps Instance properties to add to instances of the - * class returned from this method. - * @param {Object} classProps Class properties to add the class returned from - * this method. - * @return {Class} A new subclass of Parse.Object. - */ - - }, { - key: 'extend', - value: function (className, protoProps, classProps) { - if (typeof className !== 'string') { - if (className && typeof className.className === 'string') { - return ParseObject.extend(className.className, className, protoProps); - } else { - throw new Error('Parse.Object.extend\'s first argument should be the className.'); - } - } - var adjustedClassName = className; - - if (adjustedClassName === 'User' && _CoreManager2.default.get('PERFORM_USER_REWRITE')) { - adjustedClassName = '_User'; - } - - var parentProto = ParseObject.prototype; - if (this.hasOwnProperty('__super__') && this.__super__) { - parentProto = this.prototype; - } else if (classMap[adjustedClassName]) { - parentProto = classMap[adjustedClassName].prototype; - } - var ParseObjectSubclass = function (attributes, options) { - this.className = adjustedClassName; - this._objCount = objectCount++; - // Enable legacy initializers - if (typeof this.initialize === 'function') { - this.initialize.apply(this, arguments); - } - - if (attributes && (typeof attributes === 'undefined' ? 'undefined' : (0, _typeof3.default)(attributes)) === 'object') { - if (!this.set(attributes || {}, options)) { - throw new Error('Can\'t create an invalid Parse Object'); - } - } - }; - ParseObjectSubclass.className = adjustedClassName; - ParseObjectSubclass.__super__ = parentProto; - - ParseObjectSubclass.prototype = (0, _create2.default)(parentProto, { - constructor: { - value: ParseObjectSubclass, - enumerable: false, - writable: true, - configurable: true - } - }); - - if (protoProps) { - for (var prop in protoProps) { - if (prop !== 'className') { - (0, _defineProperty2.default)(ParseObjectSubclass.prototype, prop, { - value: protoProps[prop], - enumerable: false, - writable: true, - configurable: true - }); - } - } - } - - if (classProps) { - for (var prop in classProps) { - if (prop !== 'className') { - (0, _defineProperty2.default)(ParseObjectSubclass, prop, { - value: classProps[prop], - enumerable: false, - writable: true, - configurable: true - }); - } - } - } - - ParseObjectSubclass.extend = function (name, protoProps, classProps) { - if (typeof name === 'string') { - return ParseObject.extend.call(ParseObjectSubclass, name, protoProps, classProps); - } - return ParseObject.extend.call(ParseObjectSubclass, adjustedClassName, name, protoProps); - }; - ParseObjectSubclass.createWithoutData = ParseObject.createWithoutData; - - classMap[adjustedClassName] = ParseObjectSubclass; - return ParseObjectSubclass; - } - - /** - * Enable single instance objects, where any local objects with the same Id - * share the same attributes, and stay synchronized with each other. - * This is disabled by default in server environments, since it can lead to - * security issues. - * @method enableSingleInstance - */ - - }, { - key: 'enableSingleInstance', - value: function () { - singleInstance = true; - _CoreManager2.default.setObjectStateController(SingleInstanceStateController); - } - - /** - * Disable single instance objects, where any local objects with the same Id - * share the same attributes, and stay synchronized with each other. - * When disabled, you can have two instances of the same object in memory - * without them sharing attributes. - * @method disableSingleInstance - */ - - }, { - key: 'disableSingleInstance', - value: function () { - singleInstance = false; - _CoreManager2.default.setObjectStateController(UniqueInstanceStateController); - } - }]); - return ParseObject; -}(); - -exports.default = ParseObject; - -var DefaultController = { - fetch: function (target, forceFetch, options) { - if (Array.isArray(target)) { - if (target.length < 1) { - return _ParsePromise2.default.as([]); - } - var objs = []; - var ids = []; - var className = null; - var results = []; - var error = null; - target.forEach(function (el, i) { - if (error) { - return; - } - if (!className) { - className = el.className; - } - if (className !== el.className) { - error = new _ParseError2.default(_ParseError2.default.INVALID_CLASS_NAME, 'All objects should be of the same class'); - } - if (!el.id) { - error = new _ParseError2.default(_ParseError2.default.MISSING_OBJECT_ID, 'All objects must have an ID'); - } - if (forceFetch || (0, _keys2.default)(el._getServerData()).length === 0) { - ids.push(el.id); - objs.push(el); - } - results.push(el); - }); - if (error) { - return _ParsePromise2.default.error(error); - } - var query = new _ParseQuery2.default(className); - query.containedIn('objectId', ids); - query._limit = ids.length; - return query.find(options).then(function (objects) { - var idMap = {}; - objects.forEach(function (o) { - idMap[o.id] = o; - }); - for (var i = 0; i < objs.length; i++) { - var obj = objs[i]; - if (!obj || !obj.id || !idMap[obj.id]) { - if (forceFetch) { - return _ParsePromise2.default.error(new _ParseError2.default(_ParseError2.default.OBJECT_NOT_FOUND, 'All objects must exist on the server.')); - } - } - } - if (!singleInstance) { - // If single instance objects are disabled, we need to replace the - for (var i = 0; i < results.length; i++) { - var obj = results[i]; - if (obj && obj.id && idMap[obj.id]) { - var id = obj.id; - obj._finishFetch(idMap[id].toJSON()); - results[i] = idMap[id]; - } - } - } - return _ParsePromise2.default.as(results); - }); - } else { - var RESTController = _CoreManager2.default.getRESTController(); - return RESTController.request('GET', 'classes/' + target.className + '/' + target._getId(), {}, options).then(function (response, status, xhr) { - if (target instanceof ParseObject) { - target._clearPendingOps(); - target._clearServerData(); - target._finishFetch(response); - } - return target; - }); - } - }, - destroy: function (target, options) { - var RESTController = _CoreManager2.default.getRESTController(); - if (Array.isArray(target)) { - if (target.length < 1) { - return _ParsePromise2.default.as([]); - } - var batches = [[]]; - target.forEach(function (obj) { - if (!obj.id) { - return; - } - batches[batches.length - 1].push(obj); - if (batches[batches.length - 1].length >= 20) { - batches.push([]); - } - }); - if (batches[batches.length - 1].length === 0) { - // If the last batch is empty, remove it - batches.pop(); - } - var deleteCompleted = _ParsePromise2.default.as(); - var errors = []; - batches.forEach(function (batch) { - deleteCompleted = deleteCompleted.then(function () { - return RESTController.request('POST', 'batch', { - requests: batch.map(function (obj) { - return { - method: 'DELETE', - path: getServerUrlPath() + 'classes/' + obj.className + '/' + obj._getId(), - body: {} - }; - }) - }, options).then(function (results) { - for (var i = 0; i < results.length; i++) { - if (results[i] && results[i].hasOwnProperty('error')) { - var err = new _ParseError2.default(results[i].error.code, results[i].error.error); - err.object = batch[i]; - errors.push(err); - } - } - }); - }); - }); - return deleteCompleted.then(function () { - if (errors.length) { - var aggregate = new _ParseError2.default(_ParseError2.default.AGGREGATE_ERROR); - aggregate.errors = errors; - return _ParsePromise2.default.error(aggregate); - } - return _ParsePromise2.default.as(target); - }); - } else if (target instanceof ParseObject) { - return RESTController.request('DELETE', 'classes/' + target.className + '/' + target._getId(), {}, options).then(function () { - return _ParsePromise2.default.as(target); - }); - } - return _ParsePromise2.default.as(target); - }, - save: function (target, options) { - var RESTController = _CoreManager2.default.getRESTController(); - var stateController = _CoreManager2.default.getObjectStateController(); - if (Array.isArray(target)) { - if (target.length < 1) { - return _ParsePromise2.default.as([]); - } - - var unsaved = target.concat(); - for (var i = 0; i < target.length; i++) { - if (target[i] instanceof ParseObject) { - unsaved = unsaved.concat((0, _unsavedChildren2.default)(target[i], true)); - } - } - unsaved = (0, _unique2.default)(unsaved); - - var filesSaved = _ParsePromise2.default.as(); - var pending = []; - unsaved.forEach(function (el) { - if (el instanceof _ParseFile2.default) { - filesSaved = filesSaved.then(function () { - return el.save(); - }); - } else if (el instanceof ParseObject) { - pending.push(el); - } - }); - - return filesSaved.then(function () { - var objectError = null; - return _ParsePromise2.default._continueWhile(function () { - return pending.length > 0; - }, function () { - var batch = []; - var nextPending = []; - pending.forEach(function (el) { - if (batch.length < 20 && (0, _canBeSerialized2.default)(el)) { - batch.push(el); - } else { - nextPending.push(el); - } - }); - pending = nextPending; - if (batch.length < 1) { - return _ParsePromise2.default.error(new _ParseError2.default(_ParseError2.default.OTHER_CAUSE, 'Tried to save a batch with a cycle.')); - } - - // Queue up tasks for each object in the batch. - // When every task is ready, the API request will execute - var batchReturned = new _ParsePromise2.default(); - var batchReady = []; - var batchTasks = []; - batch.forEach(function (obj, index) { - var ready = new _ParsePromise2.default(); - batchReady.push(ready); - - stateController.pushPendingState(obj._getStateIdentifier()); - batchTasks.push(stateController.enqueueTask(obj._getStateIdentifier(), function () { - ready.resolve(); - return batchReturned.then(function (responses, status) { - if (responses[index].hasOwnProperty('success')) { - obj._handleSaveResponse(responses[index].success, status); - } else { - if (!objectError && responses[index].hasOwnProperty('error')) { - var serverError = responses[index].error; - objectError = new _ParseError2.default(serverError.code, serverError.error); - // Cancel the rest of the save - pending = []; - } - obj._handleSaveError(); - } - }); - })); - }); - - _ParsePromise2.default.when(batchReady).then(function () { - // Kick off the batch request - return RESTController.request('POST', 'batch', { - requests: batch.map(function (obj) { - var params = obj._getSaveParams(); - params.path = getServerUrlPath() + params.path; - return params; - }) - }, options); - }).then(function (response, status, xhr) { - batchReturned.resolve(response, status); - }); - - return _ParsePromise2.default.when(batchTasks); - }).then(function () { - if (objectError) { - return _ParsePromise2.default.error(objectError); - } - return _ParsePromise2.default.as(target); - }); - }); - } else if (target instanceof ParseObject) { - // copying target lets Flow guarantee the pointer isn't modified elsewhere - var targetCopy = target; - var task = function () { - var params = targetCopy._getSaveParams(); - return RESTController.request(params.method, params.path, params.body, options).then(function (response, status) { - targetCopy._handleSaveResponse(response, status); - }, function (error) { - targetCopy._handleSaveError(); - return _ParsePromise2.default.error(error); - }); - }; - - stateController.pushPendingState(target._getStateIdentifier()); - return stateController.enqueueTask(target._getStateIdentifier(), task).then(function () { - return target; - }, function (error) { - return _ParsePromise2.default.error(error); - }); - } - return _ParsePromise2.default.as(); - } -}; - -_CoreManager2.default.setObjectController(DefaultController); \ No newline at end of file diff --git a/lib/node/ParseOp.js b/lib/node/ParseOp.js deleted file mode 100644 index 1eba19303..000000000 --- a/lib/node/ParseOp.js +++ /dev/null @@ -1,579 +0,0 @@ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.RelationOp = exports.RemoveOp = exports.AddUniqueOp = exports.AddOp = exports.IncrementOp = exports.UnsetOp = exports.SetOp = exports.Op = undefined; - -var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); - -var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); - -var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); - -var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); - -var _inherits2 = require('babel-runtime/helpers/inherits'); - -var _inherits3 = _interopRequireDefault(_inherits2); - -var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); - -var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); - -var _createClass2 = require('babel-runtime/helpers/createClass'); - -var _createClass3 = _interopRequireDefault(_createClass2); - -exports.opFromJSON = opFromJSON; - -var _arrayContainsObject = require('./arrayContainsObject'); - -var _arrayContainsObject2 = _interopRequireDefault(_arrayContainsObject); - -var _decode = require('./decode'); - -var _decode2 = _interopRequireDefault(_decode); - -var _encode = require('./encode'); - -var _encode2 = _interopRequireDefault(_encode); - -var _ParseObject = require('./ParseObject'); - -var _ParseObject2 = _interopRequireDefault(_ParseObject); - -var _ParseRelation = require('./ParseRelation'); - -var _ParseRelation2 = _interopRequireDefault(_ParseRelation); - -var _unique = require('./unique'); - -var _unique2 = _interopRequireDefault(_unique); - -function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { default: obj }; -} - -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * - */ - -function opFromJSON(json) { - if (!json || !json.__op) { - return null; - } - switch (json.__op) { - case 'Delete': - return new UnsetOp(); - case 'Increment': - return new IncrementOp(json.amount); - case 'Add': - return new AddOp((0, _decode2.default)(json.objects)); - case 'AddUnique': - return new AddUniqueOp((0, _decode2.default)(json.objects)); - case 'Remove': - return new RemoveOp((0, _decode2.default)(json.objects)); - case 'AddRelation': - var toAdd = (0, _decode2.default)(json.objects); - if (!Array.isArray(toAdd)) { - return new RelationOp([], []); - } - return new RelationOp(toAdd, []); - case 'RemoveRelation': - var toRemove = (0, _decode2.default)(json.objects); - if (!Array.isArray(toRemove)) { - return new RelationOp([], []); - } - return new RelationOp([], toRemove); - case 'Batch': - var toAdd = []; - var toRemove = []; - for (var i = 0; i < json.ops.length; i++) { - if (json.ops[i].__op === 'AddRelation') { - toAdd = toAdd.concat((0, _decode2.default)(json.ops[i].objects)); - } else if (json.ops[i].__op === 'RemoveRelation') { - toRemove = toRemove.concat((0, _decode2.default)(json.ops[i].objects)); - } - } - return new RelationOp(toAdd, toRemove); - } - return null; -} - -var Op = exports.Op = function () { - function Op() { - (0, _classCallCheck3.default)(this, Op); - } - - (0, _createClass3.default)(Op, [{ - key: 'applyTo', - - // Empty parent class - value: function (value) {} - }, { - key: 'mergeWith', - value: function (previous) {} - }, { - key: 'toJSON', - value: function () {} - }]); - return Op; -}(); - -var SetOp = exports.SetOp = function (_Op) { - (0, _inherits3.default)(SetOp, _Op); - - function SetOp(value) { - (0, _classCallCheck3.default)(this, SetOp); - - var _this = (0, _possibleConstructorReturn3.default)(this, (SetOp.__proto__ || (0, _getPrototypeOf2.default)(SetOp)).call(this)); - - _this._value = value; - return _this; - } - - (0, _createClass3.default)(SetOp, [{ - key: 'applyTo', - value: function (value) { - return this._value; - } - }, { - key: 'mergeWith', - value: function (previous) { - return new SetOp(this._value); - } - }, { - key: 'toJSON', - value: function () { - return (0, _encode2.default)(this._value, false, true); - } - }]); - return SetOp; -}(Op); - -var UnsetOp = exports.UnsetOp = function (_Op2) { - (0, _inherits3.default)(UnsetOp, _Op2); - - function UnsetOp() { - (0, _classCallCheck3.default)(this, UnsetOp); - return (0, _possibleConstructorReturn3.default)(this, (UnsetOp.__proto__ || (0, _getPrototypeOf2.default)(UnsetOp)).apply(this, arguments)); - } - - (0, _createClass3.default)(UnsetOp, [{ - key: 'applyTo', - value: function (value) { - return undefined; - } - }, { - key: 'mergeWith', - value: function (previous) { - return new UnsetOp(); - } - }, { - key: 'toJSON', - value: function () { - return { __op: 'Delete' }; - } - }]); - return UnsetOp; -}(Op); - -var IncrementOp = exports.IncrementOp = function (_Op3) { - (0, _inherits3.default)(IncrementOp, _Op3); - - function IncrementOp(amount) { - (0, _classCallCheck3.default)(this, IncrementOp); - - var _this3 = (0, _possibleConstructorReturn3.default)(this, (IncrementOp.__proto__ || (0, _getPrototypeOf2.default)(IncrementOp)).call(this)); - - if (typeof amount !== 'number') { - throw new TypeError('Increment Op must be initialized with a numeric amount.'); - } - _this3._amount = amount; - return _this3; - } - - (0, _createClass3.default)(IncrementOp, [{ - key: 'applyTo', - value: function (value) { - if (typeof value === 'undefined') { - return this._amount; - } - if (typeof value !== 'number') { - throw new TypeError('Cannot increment a non-numeric value.'); - } - return this._amount + value; - } - }, { - key: 'mergeWith', - value: function (previous) { - if (!previous) { - return this; - } - if (previous instanceof SetOp) { - return new SetOp(this.applyTo(previous._value)); - } - if (previous instanceof UnsetOp) { - return new SetOp(this._amount); - } - if (previous instanceof IncrementOp) { - return new IncrementOp(this.applyTo(previous._amount)); - } - throw new Error('Cannot merge Increment Op with the previous Op'); - } - }, { - key: 'toJSON', - value: function () { - return { __op: 'Increment', amount: this._amount }; - } - }]); - return IncrementOp; -}(Op); - -var AddOp = exports.AddOp = function (_Op4) { - (0, _inherits3.default)(AddOp, _Op4); - - function AddOp(value) { - (0, _classCallCheck3.default)(this, AddOp); - - var _this4 = (0, _possibleConstructorReturn3.default)(this, (AddOp.__proto__ || (0, _getPrototypeOf2.default)(AddOp)).call(this)); - - _this4._value = Array.isArray(value) ? value : [value]; - return _this4; - } - - (0, _createClass3.default)(AddOp, [{ - key: 'applyTo', - value: function (value) { - if (value == null) { - return this._value; - } - if (Array.isArray(value)) { - return value.concat(this._value); - } - throw new Error('Cannot add elements to a non-array value'); - } - }, { - key: 'mergeWith', - value: function (previous) { - if (!previous) { - return this; - } - if (previous instanceof SetOp) { - return new SetOp(this.applyTo(previous._value)); - } - if (previous instanceof UnsetOp) { - return new SetOp(this._value); - } - if (previous instanceof AddOp) { - return new AddOp(this.applyTo(previous._value)); - } - throw new Error('Cannot merge Add Op with the previous Op'); - } - }, { - key: 'toJSON', - value: function () { - return { __op: 'Add', objects: (0, _encode2.default)(this._value, false, true) }; - } - }]); - return AddOp; -}(Op); - -var AddUniqueOp = exports.AddUniqueOp = function (_Op5) { - (0, _inherits3.default)(AddUniqueOp, _Op5); - - function AddUniqueOp(value) { - (0, _classCallCheck3.default)(this, AddUniqueOp); - - var _this5 = (0, _possibleConstructorReturn3.default)(this, (AddUniqueOp.__proto__ || (0, _getPrototypeOf2.default)(AddUniqueOp)).call(this)); - - _this5._value = (0, _unique2.default)(Array.isArray(value) ? value : [value]); - return _this5; - } - - (0, _createClass3.default)(AddUniqueOp, [{ - key: 'applyTo', - value: function (value) { - if (value == null) { - return this._value || []; - } - if (Array.isArray(value)) { - // copying value lets Flow guarantee the pointer isn't modified elsewhere - var valueCopy = value; - var toAdd = []; - this._value.forEach(function (v) { - if (v instanceof _ParseObject2.default) { - if (!(0, _arrayContainsObject2.default)(valueCopy, v)) { - toAdd.push(v); - } - } else { - if (valueCopy.indexOf(v) < 0) { - toAdd.push(v); - } - } - }); - return value.concat(toAdd); - } - throw new Error('Cannot add elements to a non-array value'); - } - }, { - key: 'mergeWith', - value: function (previous) { - if (!previous) { - return this; - } - if (previous instanceof SetOp) { - return new SetOp(this.applyTo(previous._value)); - } - if (previous instanceof UnsetOp) { - return new SetOp(this._value); - } - if (previous instanceof AddUniqueOp) { - return new AddUniqueOp(this.applyTo(previous._value)); - } - throw new Error('Cannot merge AddUnique Op with the previous Op'); - } - }, { - key: 'toJSON', - value: function () { - return { __op: 'AddUnique', objects: (0, _encode2.default)(this._value, false, true) }; - } - }]); - return AddUniqueOp; -}(Op); - -var RemoveOp = exports.RemoveOp = function (_Op6) { - (0, _inherits3.default)(RemoveOp, _Op6); - - function RemoveOp(value) { - (0, _classCallCheck3.default)(this, RemoveOp); - - var _this6 = (0, _possibleConstructorReturn3.default)(this, (RemoveOp.__proto__ || (0, _getPrototypeOf2.default)(RemoveOp)).call(this)); - - _this6._value = (0, _unique2.default)(Array.isArray(value) ? value : [value]); - return _this6; - } - - (0, _createClass3.default)(RemoveOp, [{ - key: 'applyTo', - value: function (value) { - if (value == null) { - return []; - } - if (Array.isArray(value)) { - var i = value.indexOf(this._value); - var removed = value.concat([]); - for (var i = 0; i < this._value.length; i++) { - var index = removed.indexOf(this._value[i]); - while (index > -1) { - removed.splice(index, 1); - index = removed.indexOf(this._value[i]); - } - if (this._value[i] instanceof _ParseObject2.default && this._value[i].id) { - for (var j = 0; j < removed.length; j++) { - if (removed[j] instanceof _ParseObject2.default && this._value[i].id === removed[j].id) { - removed.splice(j, 1); - j--; - } - } - } - } - return removed; - } - throw new Error('Cannot remove elements from a non-array value'); - } - }, { - key: 'mergeWith', - value: function (previous) { - if (!previous) { - return this; - } - if (previous instanceof SetOp) { - return new SetOp(this.applyTo(previous._value)); - } - if (previous instanceof UnsetOp) { - return new UnsetOp(); - } - if (previous instanceof RemoveOp) { - var uniques = previous._value.concat([]); - for (var i = 0; i < this._value.length; i++) { - if (this._value[i] instanceof _ParseObject2.default) { - if (!(0, _arrayContainsObject2.default)(uniques, this._value[i])) { - uniques.push(this._value[i]); - } - } else { - if (uniques.indexOf(this._value[i]) < 0) { - uniques.push(this._value[i]); - } - } - } - return new RemoveOp(uniques); - } - throw new Error('Cannot merge Remove Op with the previous Op'); - } - }, { - key: 'toJSON', - value: function () { - return { __op: 'Remove', objects: (0, _encode2.default)(this._value, false, true) }; - } - }]); - return RemoveOp; -}(Op); - -var RelationOp = exports.RelationOp = function (_Op7) { - (0, _inherits3.default)(RelationOp, _Op7); - - function RelationOp(adds, removes) { - (0, _classCallCheck3.default)(this, RelationOp); - - var _this7 = (0, _possibleConstructorReturn3.default)(this, (RelationOp.__proto__ || (0, _getPrototypeOf2.default)(RelationOp)).call(this)); - - _this7._targetClassName = null; - - if (Array.isArray(adds)) { - _this7.relationsToAdd = (0, _unique2.default)(adds.map(_this7._extractId, _this7)); - } - - if (Array.isArray(removes)) { - _this7.relationsToRemove = (0, _unique2.default)(removes.map(_this7._extractId, _this7)); - } - return _this7; - } - - (0, _createClass3.default)(RelationOp, [{ - key: '_extractId', - value: function (obj) { - if (typeof obj === 'string') { - return obj; - } - if (!obj.id) { - throw new Error('You cannot add or remove an unsaved Parse Object from a relation'); - } - if (!this._targetClassName) { - this._targetClassName = obj.className; - } - if (this._targetClassName !== obj.className) { - throw new Error('Tried to create a Relation with 2 different object types: ' + this._targetClassName + ' and ' + obj.className + '.'); - } - return obj.id; - } - }, { - key: 'applyTo', - value: function (value, object, key) { - if (!value) { - if (!object || !key) { - throw new Error('Cannot apply a RelationOp without either a previous value, or an object and a key'); - } - var parent = new _ParseObject2.default(object.className); - if (object.id && object.id.indexOf('local') === 0) { - parent._localId = object.id; - } else if (object.id) { - parent.id = object.id; - } - var relation = new _ParseRelation2.default(parent, key); - relation.targetClassName = this._targetClassName; - return relation; - } - if (value instanceof _ParseRelation2.default) { - if (this._targetClassName) { - if (value.targetClassName) { - if (this._targetClassName !== value.targetClassName) { - throw new Error('Related object must be a ' + value.targetClassName + ', but a ' + this._targetClassName + ' was passed in.'); - } - } else { - value.targetClassName = this._targetClassName; - } - } - return value; - } else { - throw new Error('Relation cannot be applied to a non-relation field'); - } - } - }, { - key: 'mergeWith', - value: function (previous) { - if (!previous) { - return this; - } else if (previous instanceof UnsetOp) { - throw new Error('You cannot modify a relation after deleting it.'); - } else if (previous instanceof RelationOp) { - if (previous._targetClassName && previous._targetClassName !== this._targetClassName) { - throw new Error('Related object must be of class ' + previous._targetClassName + ', but ' + (this._targetClassName || 'null') + ' was passed in.'); - } - var newAdd = previous.relationsToAdd.concat([]); - this.relationsToRemove.forEach(function (r) { - var index = newAdd.indexOf(r); - if (index > -1) { - newAdd.splice(index, 1); - } - }); - this.relationsToAdd.forEach(function (r) { - var index = newAdd.indexOf(r); - if (index < 0) { - newAdd.push(r); - } - }); - - var newRemove = previous.relationsToRemove.concat([]); - this.relationsToAdd.forEach(function (r) { - var index = newRemove.indexOf(r); - if (index > -1) { - newRemove.splice(index, 1); - } - }); - this.relationsToRemove.forEach(function (r) { - var index = newRemove.indexOf(r); - if (index < 0) { - newRemove.push(r); - } - }); - - var newRelation = new RelationOp(newAdd, newRemove); - newRelation._targetClassName = this._targetClassName; - return newRelation; - } - throw new Error('Cannot merge Relation Op with the previous Op'); - } - }, { - key: 'toJSON', - value: function () { - var _this8 = this; - - var idToPointer = function (id) { - return { - __type: 'Pointer', - className: _this8._targetClassName, - objectId: id - }; - }; - - var adds = null; - var removes = null; - var pointers = null; - - if (this.relationsToAdd.length > 0) { - pointers = this.relationsToAdd.map(idToPointer); - adds = { __op: 'AddRelation', objects: pointers }; - } - if (this.relationsToRemove.length > 0) { - pointers = this.relationsToRemove.map(idToPointer); - removes = { __op: 'RemoveRelation', objects: pointers }; - } - - if (adds && removes) { - return { __op: 'Batch', ops: [adds, removes] }; - } - - return adds || removes || {}; - } - }]); - return RelationOp; -}(Op); \ No newline at end of file diff --git a/lib/node/ParsePromise.js b/lib/node/ParsePromise.js deleted file mode 100644 index a79b4afee..000000000 --- a/lib/node/ParsePromise.js +++ /dev/null @@ -1,740 +0,0 @@ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _getIterator2 = require('babel-runtime/core-js/get-iterator'); - -var _getIterator3 = _interopRequireDefault(_getIterator2); - -var _typeof2 = require('babel-runtime/helpers/typeof'); - -var _typeof3 = _interopRequireDefault(_typeof2); - -var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); - -var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); - -var _createClass2 = require('babel-runtime/helpers/createClass'); - -var _createClass3 = _interopRequireDefault(_createClass2); - -function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { default: obj }; -} - -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -var _isPromisesAPlusCompliant = true; - -/** - * A Promise is returned by async methods as a hook to provide callbacks to be - * called when the async task is fulfilled. - * - *
Typical usage would be like:
- * query.find().then(function(results) { - * results[0].set("foo", "bar"); - * return results[0].saveAsync(); - * }).then(function(result) { - * console.log("Updated " + result.id); - * }); - *- * - * @class Parse.Promise - * @constructor - */ - -var ParsePromise = function () { - function ParsePromise(executor) { - (0, _classCallCheck3.default)(this, ParsePromise); - - this._resolved = false; - this._rejected = false; - this._resolvedCallbacks = []; - this._rejectedCallbacks = []; - - if (typeof executor === 'function') { - executor(this.resolve.bind(this), this.reject.bind(this)); - } - } - - /** - * Marks this promise as fulfilled, firing any callbacks waiting on it. - * @method resolve - * @param {Object} result the result to pass to the callbacks. - */ - - (0, _createClass3.default)(ParsePromise, [{ - key: 'resolve', - value: function () { - if (this._resolved || this._rejected) { - throw new Error('A promise was resolved even though it had already been ' + (this._resolved ? 'resolved' : 'rejected') + '.'); - } - this._resolved = true; - - for (var _len = arguments.length, results = Array(_len), _key = 0; _key < _len; _key++) { - results[_key] = arguments[_key]; - } - - this._result = results; - for (var i = 0; i < this._resolvedCallbacks.length; i++) { - this._resolvedCallbacks[i].apply(this, results); - } - - this._resolvedCallbacks = []; - this._rejectedCallbacks = []; - } - - /** - * Marks this promise as fulfilled, firing any callbacks waiting on it. - * @method reject - * @param {Object} error the error to pass to the callbacks. - */ - - }, { - key: 'reject', - value: function (error) { - if (this._resolved || this._rejected) { - throw new Error('A promise was rejected even though it had already been ' + (this._resolved ? 'resolved' : 'rejected') + '.'); - } - this._rejected = true; - this._error = error; - for (var i = 0; i < this._rejectedCallbacks.length; i++) { - this._rejectedCallbacks[i](error); - } - this._resolvedCallbacks = []; - this._rejectedCallbacks = []; - } - - /** - * Adds callbacks to be called when this promise is fulfilled. Returns a new - * Promise that will be fulfilled when the callback is complete. It allows - * chaining. If the callback itself returns a Promise, then the one returned - * by "then" will not be fulfilled until that one returned by the callback - * is fulfilled. - * @method then - * @param {Function} resolvedCallback Function that is called when this - * Promise is resolved. Once the callback is complete, then the Promise - * returned by "then" will also be fulfilled. - * @param {Function} rejectedCallback Function that is called when this - * Promise is rejected with an error. Once the callback is complete, then - * the promise returned by "then" with be resolved successfully. If - * rejectedCallback is null, or it returns a rejected Promise, then the - * Promise returned by "then" will be rejected with that error. - * @return {Parse.Promise} A new Promise that will be fulfilled after this - * Promise is fulfilled and either callback has completed. If the callback - * returned a Promise, then this Promise will not be fulfilled until that - * one is. - */ - - }, { - key: 'then', - value: function (resolvedCallback, rejectedCallback) { - var _this = this; - - var promise = new ParsePromise(); - - var wrappedResolvedCallback = function () { - for (var _len2 = arguments.length, results = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { - results[_key2] = arguments[_key2]; - } - - if (typeof resolvedCallback === 'function') { - if (_isPromisesAPlusCompliant) { - try { - results = [resolvedCallback.apply(this, results)]; - } catch (e) { - results = [ParsePromise.error(e)]; - } - } else { - results = [resolvedCallback.apply(this, results)]; - } - } - if (results.length === 1 && ParsePromise.is(results[0])) { - results[0].then(function () { - promise.resolve.apply(promise, arguments); - }, function (error) { - promise.reject(error); - }); - } else { - promise.resolve.apply(promise, results); - } - }; - - var wrappedRejectedCallback = function (error) { - var result = []; - if (typeof rejectedCallback === 'function') { - if (_isPromisesAPlusCompliant) { - try { - result = [rejectedCallback(error)]; - } catch (e) { - result = [ParsePromise.error(e)]; - } - } else { - result = [rejectedCallback(error)]; - } - if (result.length === 1 && ParsePromise.is(result[0])) { - result[0].then(function () { - promise.resolve.apply(promise, arguments); - }, function (error) { - promise.reject(error); - }); - } else { - if (_isPromisesAPlusCompliant) { - promise.resolve.apply(promise, result); - } else { - promise.reject(result[0]); - } - } - } else { - promise.reject(error); - } - }; - - var runLater = function (fn) { - fn.call(); - }; - if (_isPromisesAPlusCompliant) { - if (typeof process !== 'undefined' && typeof process.nextTick === 'function') { - runLater = function (fn) { - process.nextTick(fn); - }; - } else if (typeof setTimeout === 'function') { - runLater = function (fn) { - setTimeout(fn, 0); - }; - } - } - - if (this._resolved) { - runLater(function () { - wrappedResolvedCallback.apply(_this, _this._result); - }); - } else if (this._rejected) { - runLater(function () { - wrappedRejectedCallback(_this._error); - }); - } else { - this._resolvedCallbacks.push(wrappedResolvedCallback); - this._rejectedCallbacks.push(wrappedRejectedCallback); - } - - return promise; - } - - /** - * Add handlers to be called when the promise - * is either resolved or rejected - * @method always - */ - - }, { - key: 'always', - value: function (callback) { - return this.then(callback, callback); - } - - /** - * Add handlers to be called when the Promise object is resolved - * @method done - */ - - }, { - key: 'done', - value: function (callback) { - return this.then(callback); - } - - /** - * Add handlers to be called when the Promise object is rejected - * Alias for catch(). - * @method fail - */ - - }, { - key: 'fail', - value: function (callback) { - return this.then(null, callback); - } - - /** - * Add handlers to be called when the Promise object is rejected - * @method catch - */ - - }, { - key: 'catch', - value: function (callback) { - return this.then(null, callback); - } - - /** - * Run the given callbacks after this promise is fulfilled. - * @method _thenRunCallbacks - * @param optionsOrCallback {} A Backbone-style options callback, or a - * callback function. If this is an options object and contains a "model" - * attributes, that will be passed to error callbacks as the first argument. - * @param model {} If truthy, this will be passed as the first result of - * error callbacks. This is for Backbone-compatability. - * @return {Parse.Promise} A promise that will be resolved after the - * callbacks are run, with the same result as this. - */ - - }, { - key: '_thenRunCallbacks', - value: function (optionsOrCallback, model) { - var options = {}; - if (typeof optionsOrCallback === 'function') { - options.success = function (result) { - optionsOrCallback(result, null); - }; - options.error = function (error) { - optionsOrCallback(null, error); - }; - } else if ((typeof optionsOrCallback === 'undefined' ? 'undefined' : (0, _typeof3.default)(optionsOrCallback)) === 'object') { - if (typeof optionsOrCallback.success === 'function') { - options.success = optionsOrCallback.success; - } - if (typeof optionsOrCallback.error === 'function') { - options.error = optionsOrCallback.error; - } - } - - return this.then(function () { - for (var _len3 = arguments.length, results = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) { - results[_key3] = arguments[_key3]; - } - - if (options.success) { - options.success.apply(this, results); - } - return ParsePromise.as.apply(ParsePromise, arguments); - }, function (error) { - if (options.error) { - if (typeof model !== 'undefined') { - options.error(model, error); - } else { - options.error(error); - } - } - // By explicitly returning a rejected Promise, this will work with - // either jQuery or Promises/A+ semantics. - return ParsePromise.error(error); - }); - } - - /** - * Adds a callback function that should be called regardless of whether - * this promise failed or succeeded. The callback will be given either the - * array of results for its first argument, or the error as its second, - * depending on whether this Promise was rejected or resolved. Returns a - * new Promise, like "then" would. - * @method _continueWith - * @param {Function} continuation the callback. - */ - - }, { - key: '_continueWith', - value: function (continuation) { - return this.then(function () { - for (var _len4 = arguments.length, args = Array(_len4), _key4 = 0; _key4 < _len4; _key4++) { - args[_key4] = arguments[_key4]; - } - - return continuation(args, null); - }, function (error) { - return continuation(null, error); - }); - } - - /** - * Returns true iff the given object fulfils the Promise interface. - * @method is - * @param {Object} promise The object to test - * @static - * @return {Boolean} - */ - - }], [{ - key: 'is', - value: function (promise) { - return promise != null && typeof promise.then === 'function'; - } - - /** - * Returns a new promise that is resolved with a given value. - * @method as - * @param value The value to resolve the promise with - * @static - * @return {Parse.Promise} the new promise. - */ - - }, { - key: 'as', - value: function () { - var promise = new ParsePromise(); - - for (var _len5 = arguments.length, values = Array(_len5), _key5 = 0; _key5 < _len5; _key5++) { - values[_key5] = arguments[_key5]; - } - - promise.resolve.apply(promise, values); - return promise; - } - - /** - * Returns a new promise that is resolved with a given value. - * If that value is a thenable Promise (has a .then() prototype - * method), the new promise will be chained to the end of the - * value. - * @method resolve - * @param value The value to resolve the promise with - * @static - * @return {Parse.Promise} the new promise. - */ - - }, { - key: 'resolve', - value: function (value) { - return new ParsePromise(function (resolve, reject) { - if (ParsePromise.is(value)) { - value.then(resolve, reject); - } else { - resolve(value); - } - }); - } - - /** - * Returns a new promise that is rejected with a given error. - * @method error - * @param error The error to reject the promise with - * @static - * @return {Parse.Promise} the new promise. - */ - - }, { - key: 'error', - value: function () { - var promise = new ParsePromise(); - - for (var _len6 = arguments.length, errors = Array(_len6), _key6 = 0; _key6 < _len6; _key6++) { - errors[_key6] = arguments[_key6]; - } - - promise.reject.apply(promise, errors); - return promise; - } - - /** - * Returns a new promise that is rejected with a given error. - * This is an alias for Parse.Promise.error, for compliance with - * the ES6 implementation. - * @method reject - * @param error The error to reject the promise with - * @static - * @return {Parse.Promise} the new promise. - */ - - }, { - key: 'reject', - value: function () { - for (var _len7 = arguments.length, errors = Array(_len7), _key7 = 0; _key7 < _len7; _key7++) { - errors[_key7] = arguments[_key7]; - } - - return ParsePromise.error.apply(null, errors); - } - - /** - * Returns a new promise that is fulfilled when all of the input promises - * are resolved. If any promise in the list fails, then the returned promise - * will be rejected with an array containing the error from each promise. - * If they all succeed, then the returned promise will succeed, with the - * results being the results of all the input - * promises. For example:
- * var p1 = Parse.Promise.as(1); - * var p2 = Parse.Promise.as(2); - * var p3 = Parse.Promise.as(3); - * - * Parse.Promise.when(p1, p2, p3).then(function(r1, r2, r3) { - * console.log(r1); // prints 1 - * console.log(r2); // prints 2 - * console.log(r3); // prints 3 - * });- * - * The input promises can also be specified as an array:
- * var promises = [p1, p2, p3]; - * Parse.Promise.when(promises).then(function(results) { - * console.log(results); // prints [1,2,3] - * }); - *- * @method when - * @param {Array} promises a list of promises to wait for. - * @static - * @return {Parse.Promise} the new promise. - */ - - }, { - key: 'when', - value: function (promises) { - var objects; - var arrayArgument = Array.isArray(promises); - if (arrayArgument) { - objects = promises; - } else { - objects = arguments; - } - - var total = objects.length; - var hadError = false; - var results = []; - var returnValue = arrayArgument ? [results] : results; - var errors = []; - results.length = objects.length; - errors.length = objects.length; - - if (total === 0) { - return ParsePromise.as.apply(this, returnValue); - } - - var promise = new ParsePromise(); - - var resolveOne = function () { - total--; - if (total <= 0) { - if (hadError) { - promise.reject(errors); - } else { - promise.resolve.apply(promise, returnValue); - } - } - }; - - var chain = function (object, index) { - if (ParsePromise.is(object)) { - object.then(function (result) { - results[index] = result; - resolveOne(); - }, function (error) { - errors[index] = error; - hadError = true; - resolveOne(); - }); - } else { - results[i] = object; - resolveOne(); - } - }; - for (var i = 0; i < objects.length; i++) { - chain(objects[i], i); - } - - return promise; - } - - /** - * Returns a new promise that is fulfilled when all of the promises in the - * iterable argument are resolved. If any promise in the list fails, then - * the returned promise will be immediately rejected with the reason that - * single promise rejected. If they all succeed, then the returned promise - * will succeed, with the results being the results of all the input - * promises. If the iterable provided is empty, the returned promise will - * be immediately resolved. - * - * For example:
- * var p1 = Parse.Promise.as(1); - * var p2 = Parse.Promise.as(2); - * var p3 = Parse.Promise.as(3); - * - * Parse.Promise.all([p1, p2, p3]).then(function([r1, r2, r3]) { - * console.log(r1); // prints 1 - * console.log(r2); // prints 2 - * console.log(r3); // prints 3 - * });- * - * @method all - * @param {Iterable} promises an iterable of promises to wait for. - * @static - * @return {Parse.Promise} the new promise. - */ - - }, { - key: 'all', - value: function (promises) { - var total = 0; - var objects = []; - - var _iteratorNormalCompletion = true; - var _didIteratorError = false; - var _iteratorError = undefined; - - try { - for (var _iterator = (0, _getIterator3.default)(promises), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { - var p = _step.value; - - objects[total++] = p; - } - } catch (err) { - _didIteratorError = true; - _iteratorError = err; - } finally { - try { - if (!_iteratorNormalCompletion && _iterator.return) { - _iterator.return(); - } - } finally { - if (_didIteratorError) { - throw _iteratorError; - } - } - } - - if (total === 0) { - return ParsePromise.as([]); - } - - var hadError = false; - var promise = new ParsePromise(); - var resolved = 0; - var results = []; - objects.forEach(function (object, i) { - if (ParsePromise.is(object)) { - object.then(function (result) { - if (hadError) { - return false; - } - results[i] = result; - resolved++; - if (resolved >= total) { - promise.resolve(results); - } - }, function (error) { - // Reject immediately - promise.reject(error); - hadError = true; - }); - } else { - results[i] = object; - resolved++; - if (!hadError && resolved >= total) { - promise.resolve(results); - } - } - }); - - return promise; - } - - /** - * Returns a new promise that is immediately fulfilled when any of the - * promises in the iterable argument are resolved or rejected. If the - * first promise to complete is resolved, the returned promise will be - * resolved with the same value. Likewise, if the first promise to - * complete is rejected, the returned promise will be rejected with the - * same reason. - * - * @method race - * @param {Iterable} promises an iterable of promises to wait for. - * @static - * @return {Parse.Promise} the new promise. - */ - - }, { - key: 'race', - value: function (promises) { - var completed = false; - var promise = new ParsePromise(); - var _iteratorNormalCompletion2 = true; - var _didIteratorError2 = false; - var _iteratorError2 = undefined; - - try { - for (var _iterator2 = (0, _getIterator3.default)(promises), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { - var p = _step2.value; - - if (ParsePromise.is(p)) { - p.then(function (result) { - if (completed) { - return; - } - completed = true; - promise.resolve(result); - }, function (error) { - if (completed) { - return; - } - completed = true; - promise.reject(error); - }); - } else if (!completed) { - completed = true; - promise.resolve(p); - } - } - } catch (err) { - _didIteratorError2 = true; - _iteratorError2 = err; - } finally { - try { - if (!_iteratorNormalCompletion2 && _iterator2.return) { - _iterator2.return(); - } - } finally { - if (_didIteratorError2) { - throw _iteratorError2; - } - } - } - - return promise; - } - - /** - * Runs the given asyncFunction repeatedly, as long as the predicate - * function returns a truthy value. Stops repeating if asyncFunction returns - * a rejected promise. - * @method _continueWhile - * @param {Function} predicate should return false when ready to stop. - * @param {Function} asyncFunction should return a Promise. - * @static - */ - - }, { - key: '_continueWhile', - value: function (predicate, asyncFunction) { - if (predicate()) { - return asyncFunction().then(function () { - return ParsePromise._continueWhile(predicate, asyncFunction); - }); - } - return ParsePromise.as(); - } - }, { - key: 'isPromisesAPlusCompliant', - value: function () { - return _isPromisesAPlusCompliant; - } - }, { - key: 'enableAPlusCompliant', - value: function () { - _isPromisesAPlusCompliant = true; - } - }, { - key: 'disableAPlusCompliant', - value: function () { - _isPromisesAPlusCompliant = false; - } - }]); - return ParsePromise; -}(); - -exports.default = ParsePromise; \ No newline at end of file diff --git a/lib/node/ParseQuery.js b/lib/node/ParseQuery.js deleted file mode 100644 index 664f5a737..000000000 --- a/lib/node/ParseQuery.js +++ /dev/null @@ -1,1340 +0,0 @@ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _typeof2 = require('babel-runtime/helpers/typeof'); - -var _typeof3 = _interopRequireDefault(_typeof2); - -var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); - -var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); - -var _createClass2 = require('babel-runtime/helpers/createClass'); - -var _createClass3 = _interopRequireDefault(_createClass2); - -var _keys = require('babel-runtime/core-js/object/keys'); - -var _keys2 = _interopRequireDefault(_keys); - -var _CoreManager = require('./CoreManager'); - -var _CoreManager2 = _interopRequireDefault(_CoreManager); - -var _encode = require('./encode'); - -var _encode2 = _interopRequireDefault(_encode); - -var _ParseError = require('./ParseError'); - -var _ParseError2 = _interopRequireDefault(_ParseError); - -var _ParseGeoPoint = require('./ParseGeoPoint'); - -var _ParseGeoPoint2 = _interopRequireDefault(_ParseGeoPoint); - -var _ParseObject = require('./ParseObject'); - -var _ParseObject2 = _interopRequireDefault(_ParseObject); - -var _ParsePromise = require('./ParsePromise'); - -var _ParsePromise2 = _interopRequireDefault(_ParsePromise); - -function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { default: obj }; -} - -/** - * Converts a string into a regex that matches it. - * Surrounding with \Q .. \E does this, we just need to escape any \E's in - * the text separately. - */ -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * - */ - -function quote(s) { - return '\\Q' + s.replace('\\E', '\\E\\\\E\\Q') + '\\E'; -} - -/** - * Handles pre-populating the result data of a query with select fields, - * making sure that the data object contains keys for all objects that have - * been requested with a select, so that our cached state updates correctly. - */ -function handleSelectResult(data, select) { - var serverDataMask = {}; - - select.forEach(function (field) { - var hasSubObjectSelect = field.indexOf(".") !== -1; - if (!hasSubObjectSelect && !data.hasOwnProperty(field)) { - // this field was selected, but is missing from the retrieved data - data[field] = undefined; - } else if (hasSubObjectSelect) { - // this field references a sub-object, - // so we need to walk down the path components - var pathComponents = field.split("."); - var obj = data; - var serverMask = serverDataMask; - - pathComponents.forEach(function (component, index, arr) { - // add keys if the expected data is missing - if (!obj[component]) { - obj[component] = index == arr.length - 1 ? undefined : {}; - } - obj = obj[component]; - - //add this path component to the server mask so we can fill it in later if needed - if (index < arr.length - 1) { - if (!serverMask[component]) { - serverMask[component] = {}; - } - } - }); - } - }); - - if ((0, _keys2.default)(serverDataMask).length > 0) { - var copyMissingDataWithMask = function copyMissingDataWithMask(src, dest, mask, copyThisLevel) { - //copy missing elements at this level - if (copyThisLevel) { - for (var key in src) { - if (src.hasOwnProperty(key) && !dest.hasOwnProperty(key)) { - dest[key] = src[key]; - } - } - } - for (var key in mask) { - //traverse into objects as needed - copyMissingDataWithMask(src[key], dest[key], mask[key], true); - } - }; - - // When selecting from sub-objects, we don't want to blow away the missing - // information that we may have retrieved before. We've already added any - // missing selected keys to sub-objects, but we still need to add in the - // data for any previously retrieved sub-objects that were not selected. - - var serverData = _CoreManager2.default.getObjectStateController().getServerData({ id: data.objectId, className: data.className }); - - copyMissingDataWithMask(serverData, data, serverDataMask, false); - } -} - -/** - * Creates a new parse Parse.Query for the given Parse.Object subclass. - * @class Parse.Query - * @constructor - * @param {} objectClass An instance of a subclass of Parse.Object, or a Parse className string. - * - *
Parse.Query defines a query that is used to fetch Parse.Objects. The
- * most common use case is finding all objects that match a query through the
- * find
method. For example, this sample code fetches all objects
- * of class MyClass
. It calls a different function depending on
- * whether the fetch succeeded or not.
- *
- *
- * var query = new Parse.Query(MyClass); - * query.find({ - * success: function(results) { - * // results is an array of Parse.Object. - * }, - * - * error: function(error) { - * // error is an instance of Parse.Error. - * } - * });- * - *
A Parse.Query can also be used to retrieve a single object whose id is
- * known, through the get method. For example, this sample code fetches an
- * object of class MyClass
and id myId
. It calls a
- * different function depending on whether the fetch succeeded or not.
- *
- *
- * var query = new Parse.Query(MyClass); - * query.get(myId, { - * success: function(object) { - * // object is an instance of Parse.Object. - * }, - * - * error: function(object, error) { - * // error is an instance of Parse.Error. - * } - * });- * - *
A Parse.Query can also be used to count the number of objects that match
- * the query without retrieving all of those objects. For example, this
- * sample code counts the number of objects of the class MyClass
- *
- * var query = new Parse.Query(MyClass); - * query.count({ - * success: function(number) { - * // There are number instances of MyClass. - * }, - * - * error: function(error) { - * // error is an instance of Parse.Error. - * } - * });- */ - -var ParseQuery = function () { - function ParseQuery(objectClass) { - (0, _classCallCheck3.default)(this, ParseQuery); - - if (typeof objectClass === 'string') { - if (objectClass === 'User' && _CoreManager2.default.get('PERFORM_USER_REWRITE')) { - this.className = '_User'; - } else { - this.className = objectClass; - } - } else if (objectClass instanceof _ParseObject2.default) { - this.className = objectClass.className; - } else if (typeof objectClass === 'function') { - if (typeof objectClass.className === 'string') { - this.className = objectClass.className; - } else { - var obj = new objectClass(); - this.className = obj.className; - } - } else { - throw new TypeError('A ParseQuery must be constructed with a ParseObject or class name.'); - } - - this._where = {}; - this._include = []; - this._limit = -1; // negative limit is not sent in the server request - this._skip = 0; - this._extraOptions = {}; - } - - /** - * Adds constraint that at least one of the passed in queries matches. - * @method _orQuery - * @param {Array} queries - * @return {Parse.Query} Returns the query, so you can chain this call. - */ - - (0, _createClass3.default)(ParseQuery, [{ - key: '_orQuery', - value: function (queries) { - var queryJSON = queries.map(function (q) { - return q.toJSON().where; - }); - - this._where.$or = queryJSON; - return this; - } - - /** - * Helper for condition queries - */ - - }, { - key: '_addCondition', - value: function (key, condition, value) { - if (!this._where[key] || typeof this._where[key] === 'string') { - this._where[key] = {}; - } - this._where[key][condition] = (0, _encode2.default)(value, false, true); - return this; - } - - /** - * Converts string for regular expression at the beginning - */ - - }, { - key: '_regexStartWith', - value: function (string) { - return '^' + quote(string); - } - - /** - * Returns a JSON representation of this query. - * @method toJSON - * @return {Object} The JSON representation of the query. - */ - - }, { - key: 'toJSON', - value: function () { - var params = { - where: this._where - }; - - if (this._include.length) { - params.include = this._include.join(','); - } - if (this._select) { - params.keys = this._select.join(','); - } - if (this._limit >= 0) { - params.limit = this._limit; - } - if (this._skip > 0) { - params.skip = this._skip; - } - if (this._order) { - params.order = this._order.join(','); - } - for (var key in this._extraOptions) { - params[key] = this._extraOptions[key]; - } - - return params; - } - - /** - * Constructs a Parse.Object whose id is already known by fetching data from - * the server. Either options.success or options.error is called when the - * find completes. - * - * @method get - * @param {String} objectId The id of the object to be fetched. - * @param {Object} options A Backbone-style options object. - * Valid options are:
var compoundQuery = Parse.Query.or(query1, query2, query3);- * - * will create a compoundQuery that is an or of the query1, query2, and - * query3. - * @method or - * @param {...Parse.Query} var_args The list of queries to OR. - * @static - * @return {Parse.Query} The query that is the OR of the passed in queries. - */ - - }], [{ - key: 'or', - value: function () { - var className = null; - - for (var _len7 = arguments.length, queries = Array(_len7), _key7 = 0; _key7 < _len7; _key7++) { - queries[_key7] = arguments[_key7]; - } - - queries.forEach(function (q) { - if (!className) { - className = q.className; - } - - if (className !== q.className) { - throw new Error('All queries must be for the same class.'); - } - }); - - var query = new ParseQuery(className); - query._orQuery(queries); - return query; - } - }]); - return ParseQuery; -}(); - -exports.default = ParseQuery; - -var DefaultController = { - find: function (className, params, options) { - var RESTController = _CoreManager2.default.getRESTController(); - - return RESTController.request('GET', 'classes/' + className, params, options); - } -}; - -_CoreManager2.default.setQueryController(DefaultController); \ No newline at end of file diff --git a/lib/node/ParseRelation.js b/lib/node/ParseRelation.js deleted file mode 100644 index fe9ea8769..000000000 --- a/lib/node/ParseRelation.js +++ /dev/null @@ -1,182 +0,0 @@ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); - -var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); - -var _createClass2 = require('babel-runtime/helpers/createClass'); - -var _createClass3 = _interopRequireDefault(_createClass2); - -var _ParseOp = require('./ParseOp'); - -var _ParseObject = require('./ParseObject'); - -var _ParseObject2 = _interopRequireDefault(_ParseObject); - -var _ParseQuery = require('./ParseQuery'); - -var _ParseQuery2 = _interopRequireDefault(_ParseQuery); - -function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { default: obj }; -} - -/** - * Creates a new Relation for the given parent object and key. This - * constructor should rarely be used directly, but rather created by - * Parse.Object.relation. - * @class Parse.Relation - * @constructor - * @param {Parse.Object} parent The parent of this relation. - * @param {String} key The key for this relation on the parent. - * - *
- * A class that is used to access all of the children of a many-to-many - * relationship. Each instance of Parse.Relation is associated with a - * particular parent object and key. - *
- */ -var ParseRelation = function () { - function ParseRelation(parent, key) { - (0, _classCallCheck3.default)(this, ParseRelation); - - this.parent = parent; - this.key = key; - this.targetClassName = null; - } - - /** - * Makes sure that this relation has the right parent and key. - */ - - (0, _createClass3.default)(ParseRelation, [{ - key: '_ensureParentAndKey', - value: function (parent, key) { - this.key = this.key || key; - if (this.key !== key) { - throw new Error('Internal Error. Relation retrieved from two different keys.'); - } - if (this.parent) { - if (this.parent.className !== parent.className) { - throw new Error('Internal Error. Relation retrieved from two different Objects.'); - } - if (this.parent.id) { - if (this.parent.id !== parent.id) { - throw new Error('Internal Error. Relation retrieved from two different Objects.'); - } - } else if (parent.id) { - this.parent = parent; - } - } else { - this.parent = parent; - } - } - - /** - * Adds a Parse.Object or an array of Parse.Objects to the relation. - * @method add - * @param {} objects The item or items to add. - */ - - }, { - key: 'add', - value: function (objects) { - if (!Array.isArray(objects)) { - objects = [objects]; - } - - var change = new _ParseOp.RelationOp(objects, []); - var parent = this.parent; - if (!parent) { - throw new Error('Cannot add to a Relation without a parent'); - } - parent.set(this.key, change); - this.targetClassName = change._targetClassName; - return parent; - } - - /** - * Removes a Parse.Object or an array of Parse.Objects from this relation. - * @method remove - * @param {} objects The item or items to remove. - */ - - }, { - key: 'remove', - value: function (objects) { - if (!Array.isArray(objects)) { - objects = [objects]; - } - - var change = new _ParseOp.RelationOp([], objects); - if (!this.parent) { - throw new Error('Cannot remove from a Relation without a parent'); - } - this.parent.set(this.key, change); - this.targetClassName = change._targetClassName; - } - - /** - * Returns a JSON version of the object suitable for saving to disk. - * @method toJSON - * @return {Object} - */ - - }, { - key: 'toJSON', - value: function () { - return { - __type: 'Relation', - className: this.targetClassName - }; - } - - /** - * Returns a Parse.Query that is limited to objects in this - * relation. - * @method query - * @return {Parse.Query} - */ - - }, { - key: 'query', - value: function () { - var query; - var parent = this.parent; - if (!parent) { - throw new Error('Cannot construct a query for a Relation without a parent'); - } - if (!this.targetClassName) { - query = new _ParseQuery2.default(parent.className); - query._extraOptions.redirectClassNameForKey = this.key; - } else { - query = new _ParseQuery2.default(this.targetClassName); - } - query._addCondition('$relatedTo', 'object', { - __type: 'Pointer', - className: parent.className, - objectId: parent.id - }); - query._addCondition('$relatedTo', 'key', this.key); - - return query; - } - }]); - return ParseRelation; -}(); /** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * - */ - -exports.default = ParseRelation; \ No newline at end of file diff --git a/lib/node/ParseRole.js b/lib/node/ParseRole.js deleted file mode 100644 index 567af7a54..000000000 --- a/lib/node/ParseRole.js +++ /dev/null @@ -1,196 +0,0 @@ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); - -var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); - -var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); - -var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); - -var _createClass2 = require('babel-runtime/helpers/createClass'); - -var _createClass3 = _interopRequireDefault(_createClass2); - -var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); - -var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); - -var _get2 = require('babel-runtime/helpers/get'); - -var _get3 = _interopRequireDefault(_get2); - -var _inherits2 = require('babel-runtime/helpers/inherits'); - -var _inherits3 = _interopRequireDefault(_inherits2); - -var _ParseACL = require('./ParseACL'); - -var _ParseACL2 = _interopRequireDefault(_ParseACL); - -var _ParseError = require('./ParseError'); - -var _ParseError2 = _interopRequireDefault(_ParseError); - -var _ParseObject2 = require('./ParseObject'); - -var _ParseObject3 = _interopRequireDefault(_ParseObject2); - -function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { default: obj }; -} - -/** - * Represents a Role on the Parse server. Roles represent groupings of - * Users for the purposes of granting permissions (e.g. specifying an ACL - * for an Object). Roles are specified by their sets of child users and - * child roles, all of which are granted any permissions that the parent - * role has. - * - *Roles must have a name (which cannot be changed after creation of the - * role), and must specify an ACL.
- * @class Parse.Role - * @constructor - * @param {String} name The name of the Role to create. - * @param {Parse.ACL} acl The ACL for this role. Roles must have an ACL. - * A Parse.Role is a local representation of a role persisted to the Parse - * cloud. - */ -var ParseRole = function (_ParseObject) { - (0, _inherits3.default)(ParseRole, _ParseObject); - - function ParseRole(name, acl) { - (0, _classCallCheck3.default)(this, ParseRole); - - var _this = (0, _possibleConstructorReturn3.default)(this, (ParseRole.__proto__ || (0, _getPrototypeOf2.default)(ParseRole)).call(this, '_Role')); - - if (typeof name === 'string' && acl instanceof _ParseACL2.default) { - _this.setName(name); - _this.setACL(acl); - } - return _this; - } - - /** - * Gets the name of the role. You can alternatively call role.get("name") - * - * @method getName - * @return {String} the name of the role. - */ - - (0, _createClass3.default)(ParseRole, [{ - key: 'getName', - value: function () { - var name = this.get('name'); - if (name == null || typeof name === 'string') { - return name; - } - return ''; - } - - /** - * Sets the name for a role. This value must be set before the role has - * been saved to the server, and cannot be set once the role has been - * saved. - * - *- * A role's name can only contain alphanumeric characters, _, -, and - * spaces. - *
- * - *This is equivalent to calling role.set("name", name)
- * - * @method setName - * @param {String} name The name of the role. - * @param {Object} options Standard options object with success and error - * callbacks. - */ - - }, { - key: 'setName', - value: function (name, options) { - return this.set('name', name, options); - } - - /** - * Gets the Parse.Relation for the Parse.Users that are direct - * children of this role. These users are granted any privileges that this - * role has been granted (e.g. read or write access through ACLs). You can - * add or remove users from the role through this relation. - * - *This is equivalent to calling role.relation("users")
- * - * @method getUsers - * @return {Parse.Relation} the relation for the users belonging to this - * role. - */ - - }, { - key: 'getUsers', - value: function () { - return this.relation('users'); - } - - /** - * Gets the Parse.Relation for the Parse.Roles that are direct - * children of this role. These roles' users are granted any privileges that - * this role has been granted (e.g. read or write access through ACLs). You - * can add or remove child roles from this role through this relation. - * - *This is equivalent to calling role.relation("roles")
- * - * @method getRoles - * @return {Parse.Relation} the relation for the roles belonging to this - * role. - */ - - }, { - key: 'getRoles', - value: function () { - return this.relation('roles'); - } - }, { - key: 'validate', - value: function (attrs, options) { - var isInvalid = (0, _get3.default)(ParseRole.prototype.__proto__ || (0, _getPrototypeOf2.default)(ParseRole.prototype), 'validate', this).call(this, attrs, options); - if (isInvalid) { - return isInvalid; - } - - if ('name' in attrs && attrs.name !== this.getName()) { - var newName = attrs.name; - if (this.id && this.id !== attrs.objectId) { - // Check to see if the objectId being set matches this.id - // This happens during a fetch -- the id is set before calling fetch - // Let the name be set in this case - return new _ParseError2.default(_ParseError2.default.OTHER_CAUSE, 'A role\'s name can only be set before it has been saved.'); - } - if (typeof newName !== 'string') { - return new _ParseError2.default(_ParseError2.default.OTHER_CAUSE, 'A role\'s name must be a String.'); - } - if (!/^[0-9a-zA-Z\-_ ]+$/.test(newName)) { - return new _ParseError2.default(_ParseError2.default.OTHER_CAUSE, 'A role\'s name can be only contain alphanumeric characters, _, ' + '-, and spaces.'); - } - } - return false; - } - }]); - return ParseRole; -}(_ParseObject3.default); /** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * - */ - -exports.default = ParseRole; - -_ParseObject3.default.registerSubclass('_Role', ParseRole); \ No newline at end of file diff --git a/lib/node/ParseSession.js b/lib/node/ParseSession.js deleted file mode 100644 index 7fb75c80d..000000000 --- a/lib/node/ParseSession.js +++ /dev/null @@ -1,180 +0,0 @@ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _typeof2 = require('babel-runtime/helpers/typeof'); - -var _typeof3 = _interopRequireDefault(_typeof2); - -var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); - -var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); - -var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); - -var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); - -var _createClass2 = require('babel-runtime/helpers/createClass'); - -var _createClass3 = _interopRequireDefault(_createClass2); - -var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); - -var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); - -var _inherits2 = require('babel-runtime/helpers/inherits'); - -var _inherits3 = _interopRequireDefault(_inherits2); - -var _CoreManager = require('./CoreManager'); - -var _CoreManager2 = _interopRequireDefault(_CoreManager); - -var _isRevocableSession = require('./isRevocableSession'); - -var _isRevocableSession2 = _interopRequireDefault(_isRevocableSession); - -var _ParseObject2 = require('./ParseObject'); - -var _ParseObject3 = _interopRequireDefault(_ParseObject2); - -var _ParsePromise = require('./ParsePromise'); - -var _ParsePromise2 = _interopRequireDefault(_ParsePromise); - -var _ParseUser = require('./ParseUser'); - -var _ParseUser2 = _interopRequireDefault(_ParseUser); - -function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { default: obj }; -} - -/** - * @class Parse.Session - * @constructor - * - *A Parse.Session object is a local representation of a revocable session. - * This class is a subclass of a Parse.Object, and retains the same - * functionality of a Parse.Object.
- */ -var ParseSession = function (_ParseObject) { - (0, _inherits3.default)(ParseSession, _ParseObject); - - function ParseSession(attributes) { - (0, _classCallCheck3.default)(this, ParseSession); - - var _this = (0, _possibleConstructorReturn3.default)(this, (ParseSession.__proto__ || (0, _getPrototypeOf2.default)(ParseSession)).call(this, '_Session')); - - if (attributes && (typeof attributes === 'undefined' ? 'undefined' : (0, _typeof3.default)(attributes)) === 'object') { - if (!_this.set(attributes || {})) { - throw new Error('Can\'t create an invalid Session'); - } - } - return _this; - } - - /** - * Returns the session token string. - * @method getSessionToken - * @return {String} - */ - - (0, _createClass3.default)(ParseSession, [{ - key: 'getSessionToken', - value: function () { - var token = this.get('sessionToken'); - if (typeof token === 'string') { - return token; - } - return ''; - } - }], [{ - key: 'readOnlyAttributes', - value: function () { - return ['createdWith', 'expiresAt', 'installationId', 'restricted', 'sessionToken', 'user']; - } - - /** - * Retrieves the Session object for the currently logged in session. - * @method current - * @static - * @return {Parse.Promise} A promise that is resolved with the Parse.Session - * object after it has been fetched. If there is no current user, the - * promise will be rejected. - */ - - }, { - key: 'current', - value: function (options) { - options = options || {}; - var controller = _CoreManager2.default.getSessionController(); - - var sessionOptions = {}; - if (options.hasOwnProperty('useMasterKey')) { - sessionOptions.useMasterKey = options.useMasterKey; - } - return _ParseUser2.default.currentAsync().then(function (user) { - if (!user) { - return _ParsePromise2.default.error('There is no current user.'); - } - user.getSessionToken(); - - sessionOptions.sessionToken = user.getSessionToken(); - return controller.getSession(sessionOptions); - }); - } - - /** - * Determines whether the current session token is revocable. - * This method is useful for migrating Express.js or Node.js web apps to - * use revocable sessions. If you are migrating an app that uses the Parse - * SDK in the browser only, please use Parse.User.enableRevocableSession() - * instead, so that sessions can be automatically upgraded. - * @method isCurrentSessionRevocable - * @static - * @return {Boolean} - */ - - }, { - key: 'isCurrentSessionRevocable', - value: function () { - var currentUser = _ParseUser2.default.current(); - if (currentUser) { - return (0, _isRevocableSession2.default)(currentUser.getSessionToken() || ''); - } - return false; - } - }]); - return ParseSession; -}(_ParseObject3.default); /** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * - */ - -exports.default = ParseSession; - -_ParseObject3.default.registerSubclass('_Session', ParseSession); - -var DefaultController = { - getSession: function (options) { - var RESTController = _CoreManager2.default.getRESTController(); - var session = new ParseSession(); - - return RESTController.request('GET', 'sessions/me', {}, options).then(function (sessionData) { - session._finishFetch(sessionData); - session._setExisted(true); - return session; - }); - } -}; - -_CoreManager2.default.setSessionController(DefaultController); \ No newline at end of file diff --git a/lib/node/ParseUser.js b/lib/node/ParseUser.js deleted file mode 100644 index f18f42820..000000000 --- a/lib/node/ParseUser.js +++ /dev/null @@ -1,1150 +0,0 @@ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _stringify = require('babel-runtime/core-js/json/stringify'); - -var _stringify2 = _interopRequireDefault(_stringify); - -var _defineProperty = require('babel-runtime/core-js/object/define-property'); - -var _defineProperty2 = _interopRequireDefault(_defineProperty); - -var _typeof2 = require('babel-runtime/helpers/typeof'); - -var _typeof3 = _interopRequireDefault(_typeof2); - -var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); - -var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); - -var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); - -var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); - -var _createClass2 = require('babel-runtime/helpers/createClass'); - -var _createClass3 = _interopRequireDefault(_createClass2); - -var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); - -var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); - -var _get2 = require('babel-runtime/helpers/get'); - -var _get3 = _interopRequireDefault(_get2); - -var _inherits2 = require('babel-runtime/helpers/inherits'); - -var _inherits3 = _interopRequireDefault(_inherits2); - -var _CoreManager = require('./CoreManager'); - -var _CoreManager2 = _interopRequireDefault(_CoreManager); - -var _isRevocableSession = require('./isRevocableSession'); - -var _isRevocableSession2 = _interopRequireDefault(_isRevocableSession); - -var _ParseError = require('./ParseError'); - -var _ParseError2 = _interopRequireDefault(_ParseError); - -var _ParseObject2 = require('./ParseObject'); - -var _ParseObject3 = _interopRequireDefault(_ParseObject2); - -var _ParsePromise = require('./ParsePromise'); - -var _ParsePromise2 = _interopRequireDefault(_ParsePromise); - -var _ParseSession = require('./ParseSession'); - -var _ParseSession2 = _interopRequireDefault(_ParseSession); - -var _Storage = require('./Storage'); - -var _Storage2 = _interopRequireDefault(_Storage); - -function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { default: obj }; -} - -var CURRENT_USER_KEY = 'currentUser'; /** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * - */ - -var canUseCurrentUser = !_CoreManager2.default.get('IS_NODE'); -var currentUserCacheMatchesDisk = false; -var currentUserCache = null; - -var authProviders = {}; - -/** - * @class Parse.User - * @constructor - * - *A Parse.User object is a local representation of a user persisted to the - * Parse cloud. This class is a subclass of a Parse.Object, and retains the - * same functionality of a Parse.Object, but also extends it with various - * user specific methods, like authentication, signing up, and validation of - * uniqueness.
- */ - -var ParseUser = function (_ParseObject) { - (0, _inherits3.default)(ParseUser, _ParseObject); - - function ParseUser(attributes) { - (0, _classCallCheck3.default)(this, ParseUser); - - var _this = (0, _possibleConstructorReturn3.default)(this, (ParseUser.__proto__ || (0, _getPrototypeOf2.default)(ParseUser)).call(this, '_User')); - - if (attributes && (typeof attributes === 'undefined' ? 'undefined' : (0, _typeof3.default)(attributes)) === 'object') { - if (!_this.set(attributes || {})) { - throw new Error('Can\'t create an invalid Parse User'); - } - } - return _this; - } - - /** - * Request a revocable session token to replace the older style of token. - * @method _upgradeToRevocableSession - * @param {Object} options A Backbone-style options object. - * @return {Parse.Promise} A promise that is resolved when the replacement - * token has been fetched. - */ - - (0, _createClass3.default)(ParseUser, [{ - key: '_upgradeToRevocableSession', - value: function (options) { - options = options || {}; - - var upgradeOptions = {}; - if (options.hasOwnProperty('useMasterKey')) { - upgradeOptions.useMasterKey = options.useMasterKey; - } - - var controller = _CoreManager2.default.getUserController(); - return controller.upgradeToRevocableSession(this, upgradeOptions)._thenRunCallbacks(options); - } - - /** - * Unlike in the Android/iOS SDKs, logInWith is unnecessary, since you can - * call linkWith on the user (even if it doesn't exist yet on the server). - * @method _linkWith - */ - - }, { - key: '_linkWith', - value: function (provider, options) { - var _this2 = this; - - var authType; - if (typeof provider === 'string') { - authType = provider; - provider = authProviders[provider]; - } else { - authType = provider.getAuthType(); - } - if (options && options.hasOwnProperty('authData')) { - var authData = this.get('authData') || {}; - if ((typeof authData === 'undefined' ? 'undefined' : (0, _typeof3.default)(authData)) !== 'object') { - throw new Error('Invalid type: authData field should be an object'); - } - authData[authType] = options.authData; - - var controller = _CoreManager2.default.getUserController(); - return controller.linkWith(this, authData)._thenRunCallbacks(options, this); - } else { - var promise = new _ParsePromise2.default(); - provider.authenticate({ - success: function (provider, result) { - var opts = {}; - opts.authData = result; - if (options.success) { - opts.success = options.success; - } - if (options.error) { - opts.error = options.error; - } - _this2._linkWith(provider, opts).then(function () { - promise.resolve(_this2); - }, function (error) { - promise.reject(error); - }); - }, - error: function (provider, _error) { - if (typeof options.error === 'function') { - options.error(_this2, _error); - } - promise.reject(_error); - } - }); - return promise; - } - } - - /** - * Synchronizes auth data for a provider (e.g. puts the access token in the - * right place to be used by the Facebook SDK). - * @method _synchronizeAuthData - */ - - }, { - key: '_synchronizeAuthData', - value: function (provider) { - if (!this.isCurrent() || !provider) { - return; - } - var authType; - if (typeof provider === 'string') { - authType = provider; - provider = authProviders[authType]; - } else { - authType = provider.getAuthType(); - } - var authData = this.get('authData'); - if (!provider || !authData || (typeof authData === 'undefined' ? 'undefined' : (0, _typeof3.default)(authData)) !== 'object') { - return; - } - var success = provider.restoreAuthentication(authData[authType]); - if (!success) { - this._unlinkFrom(provider); - } - } - - /** - * Synchronizes authData for all providers. - * @method _synchronizeAllAuthData - */ - - }, { - key: '_synchronizeAllAuthData', - value: function () { - var authData = this.get('authData'); - if ((typeof authData === 'undefined' ? 'undefined' : (0, _typeof3.default)(authData)) !== 'object') { - return; - } - - for (var key in authData) { - this._synchronizeAuthData(key); - } - } - - /** - * Removes null values from authData (which exist temporarily for - * unlinking) - * @method _cleanupAuthData - */ - - }, { - key: '_cleanupAuthData', - value: function () { - if (!this.isCurrent()) { - return; - } - var authData = this.get('authData'); - if ((typeof authData === 'undefined' ? 'undefined' : (0, _typeof3.default)(authData)) !== 'object') { - return; - } - - for (var key in authData) { - if (!authData[key]) { - delete authData[key]; - } - } - } - - /** - * Unlinks a user from a service. - * @method _unlinkFrom - */ - - }, { - key: '_unlinkFrom', - value: function (provider, options) { - var _this3 = this; - - if (typeof provider === 'string') { - provider = authProviders[provider]; - } else { - provider.getAuthType(); - } - return this._linkWith(provider, { authData: null }).then(function () { - _this3._synchronizeAuthData(provider); - return _ParsePromise2.default.as(_this3); - })._thenRunCallbacks(options); - } - - /** - * Checks whether a user is linked to a service. - * @method _isLinked - */ - - }, { - key: '_isLinked', - value: function (provider) { - var authType; - if (typeof provider === 'string') { - authType = provider; - } else { - authType = provider.getAuthType(); - } - var authData = this.get('authData') || {}; - if ((typeof authData === 'undefined' ? 'undefined' : (0, _typeof3.default)(authData)) !== 'object') { - return false; - } - return !!authData[authType]; - } - - /** - * Deauthenticates all providers. - * @method _logOutWithAll - */ - - }, { - key: '_logOutWithAll', - value: function () { - var authData = this.get('authData'); - if ((typeof authData === 'undefined' ? 'undefined' : (0, _typeof3.default)(authData)) !== 'object') { - return; - } - - for (var key in authData) { - this._logOutWith(key); - } - } - - /** - * Deauthenticates a single provider (e.g. removing access tokens from the - * Facebook SDK). - * @method _logOutWith - */ - - }, { - key: '_logOutWith', - value: function (provider) { - if (!this.isCurrent()) { - return; - } - if (typeof provider === 'string') { - provider = authProviders[provider]; - } - if (provider && provider.deauthenticate) { - provider.deauthenticate(); - } - } - - /** - * Class instance method used to maintain specific keys when a fetch occurs. - * Used to ensure that the session token is not lost. - */ - - }, { - key: '_preserveFieldsOnFetch', - value: function () { - return { - sessionToken: this.get('sessionToken') - }; - } - - /** - * Returns true ifcurrent
would return this user.
- * @method isCurrent
- * @return {Boolean}
- */
-
- }, {
- key: 'isCurrent',
- value: function () {
- var current = ParseUser.current();
- return !!current && current.id === this.id;
- }
-
- /**
- * Returns get("username").
- * @method getUsername
- * @return {String}
- */
-
- }, {
- key: 'getUsername',
- value: function () {
- var username = this.get('username');
- if (username == null || typeof username === 'string') {
- return username;
- }
- return '';
- }
-
- /**
- * Calls set("username", username, options) and returns the result.
- * @method setUsername
- * @param {String} username
- * @param {Object} options A Backbone-style options object.
- * @return {Boolean}
- */
-
- }, {
- key: 'setUsername',
- value: function (username) {
- // Strip anonymity, even we do not support anonymous user in js SDK, we may
- // encounter anonymous user created by android/iOS in cloud code.
- var authData = this.get('authData');
- if (authData && (typeof authData === 'undefined' ? 'undefined' : (0, _typeof3.default)(authData)) === 'object' && authData.hasOwnProperty('anonymous')) {
- // We need to set anonymous to null instead of deleting it in order to remove it from Parse.
- authData.anonymous = null;
- }
- this.set('username', username);
- }
-
- /**
- * Calls set("password", password, options) and returns the result.
- * @method setPassword
- * @param {String} password
- * @param {Object} options A Backbone-style options object.
- * @return {Boolean}
- */
-
- }, {
- key: 'setPassword',
- value: function (password) {
- this.set('password', password);
- }
-
- /**
- * Returns get("email").
- * @method getEmail
- * @return {String}
- */
-
- }, {
- key: 'getEmail',
- value: function () {
- var email = this.get('email');
- if (email == null || typeof email === 'string') {
- return email;
- }
- return '';
- }
-
- /**
- * Calls set("email", email, options) and returns the result.
- * @method setEmail
- * @param {String} email
- * @param {Object} options A Backbone-style options object.
- * @return {Boolean}
- */
-
- }, {
- key: 'setEmail',
- value: function (email) {
- this.set('email', email);
- }
-
- /**
- * Returns the session token for this user, if the user has been logged in,
- * or if it is the result of a query with the master key. Otherwise, returns
- * undefined.
- * @method getSessionToken
- * @return {String} the session token, or undefined
- */
-
- }, {
- key: 'getSessionToken',
- value: function () {
- var token = this.get('sessionToken');
- if (token == null || typeof token === 'string') {
- return token;
- }
- return '';
- }
-
- /**
- * Checks whether this user is the current user and has been authenticated.
- * @method authenticated
- * @return (Boolean) whether this user is the current user and is logged in.
- */
-
- }, {
- key: 'authenticated',
- value: function () {
- var current = ParseUser.current();
- return !!this.get('sessionToken') && !!current && current.id === this.id;
- }
-
- /**
- * Signs up a new user. You should call this instead of save for
- * new Parse.Users. This will create a new Parse.User on the server, and
- * also persist the session on disk so that you can access the user using
- * current
.
- *
- * A username and password must be set before calling signUp.
- * - *Calls options.success or options.error on completion.
- * - * @method signUp - * @param {Object} attrs Extra fields to set on the new user, or null. - * @param {Object} options A Backbone-style options object. - * @return {Parse.Promise} A promise that is fulfilled when the signup - * finishes. - */ - - }, { - key: 'signUp', - value: function (attrs, options) { - options = options || {}; - - var signupOptions = {}; - if (options.hasOwnProperty('useMasterKey')) { - signupOptions.useMasterKey = options.useMasterKey; - } - if (options.hasOwnProperty('installationId')) { - signupOptions.installationId = options.installationId; - } - - var controller = _CoreManager2.default.getUserController(); - return controller.signUp(this, attrs, signupOptions)._thenRunCallbacks(options, this); - } - - /** - * Logs in a Parse.User. On success, this saves the session to disk, - * so you can retrieve the currently logged in user using - *current
.
- *
- * A username and password must be set before calling logIn.
- * - *Calls options.success or options.error on completion.
- * - * @method logIn - * @param {Object} options A Backbone-style options object. - * @return {Parse.Promise} A promise that is fulfilled with the user when - * the login is complete. - */ - - }, { - key: 'logIn', - value: function (options) { - options = options || {}; - - var loginOptions = {}; - if (options.hasOwnProperty('useMasterKey')) { - loginOptions.useMasterKey = options.useMasterKey; - } - if (options.hasOwnProperty('installationId')) { - loginOptions.installationId = options.installationId; - } - - var controller = _CoreManager2.default.getUserController(); - return controller.logIn(this, loginOptions)._thenRunCallbacks(options, this); - } - - /** - * Wrap the default save behavior with functionality to save to local - * storage if this is current user. - */ - - }, { - key: 'save', - value: function () { - var _this4 = this; - - for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { - args[_key] = arguments[_key]; - } - - return (0, _get3.default)(ParseUser.prototype.__proto__ || (0, _getPrototypeOf2.default)(ParseUser.prototype), 'save', this).apply(this, args).then(function () { - if (_this4.isCurrent()) { - return _CoreManager2.default.getUserController().updateUserOnDisk(_this4); - } - return _this4; - }); - } - - /** - * Wrap the default destroy behavior with functionality that logs out - * the current user when it is destroyed - */ - - }, { - key: 'destroy', - value: function () { - var _this5 = this; - - for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { - args[_key2] = arguments[_key2]; - } - - return (0, _get3.default)(ParseUser.prototype.__proto__ || (0, _getPrototypeOf2.default)(ParseUser.prototype), 'destroy', this).apply(this, args).then(function () { - if (_this5.isCurrent()) { - return _CoreManager2.default.getUserController().removeUserFromDisk(); - } - return _this5; - }); - } - - /** - * Wrap the default fetch behavior with functionality to save to local - * storage if this is current user. - */ - - }, { - key: 'fetch', - value: function () { - var _this6 = this; - - for (var _len3 = arguments.length, args = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) { - args[_key3] = arguments[_key3]; - } - - return (0, _get3.default)(ParseUser.prototype.__proto__ || (0, _getPrototypeOf2.default)(ParseUser.prototype), 'fetch', this).apply(this, args).then(function () { - if (_this6.isCurrent()) { - return _CoreManager2.default.getUserController().updateUserOnDisk(_this6); - } - return _this6; - }); - } - }], [{ - key: 'readOnlyAttributes', - value: function () { - return ['sessionToken']; - } - - /** - * Adds functionality to the existing Parse.User class - * @method extend - * @param {Object} protoProps A set of properties to add to the prototype - * @param {Object} classProps A set of static properties to add to the class - * @static - * @return {Class} The newly extended Parse.User class - */ - - }, { - key: 'extend', - value: function (protoProps, classProps) { - if (protoProps) { - for (var prop in protoProps) { - if (prop !== 'className') { - (0, _defineProperty2.default)(ParseUser.prototype, prop, { - value: protoProps[prop], - enumerable: false, - writable: true, - configurable: true - }); - } - } - } - - if (classProps) { - for (var prop in classProps) { - if (prop !== 'className') { - (0, _defineProperty2.default)(ParseUser, prop, { - value: classProps[prop], - enumerable: false, - writable: true, - configurable: true - }); - } - } - } - - return ParseUser; - } - - /** - * Retrieves the currently logged in ParseUser with a valid session, - * either from memory or localStorage, if necessary. - * @method current - * @static - * @return {Parse.Object} The currently logged in Parse.User. - */ - - }, { - key: 'current', - value: function () { - if (!canUseCurrentUser) { - return null; - } - var controller = _CoreManager2.default.getUserController(); - return controller.currentUser(); - } - - /** - * Retrieves the currently logged in ParseUser from asynchronous Storage. - * @method currentAsync - * @static - * @return {Parse.Promise} A Promise that is resolved with the currently - * logged in Parse User - */ - - }, { - key: 'currentAsync', - value: function () { - if (!canUseCurrentUser) { - return _ParsePromise2.default.as(null); - } - var controller = _CoreManager2.default.getUserController(); - return controller.currentUserAsync(); - } - - /** - * Signs up a new user with a username (or email) and password. - * This will create a new Parse.User on the server, and also persist the - * session in localStorage so that you can access the user using - * {@link #current}. - * - *Calls options.success or options.error on completion.
- * - * @method signUp - * @param {String} username The username (or email) to sign up with. - * @param {String} password The password to sign up with. - * @param {Object} attrs Extra fields to set on the new user. - * @param {Object} options A Backbone-style options object. - * @static - * @return {Parse.Promise} A promise that is fulfilled with the user when - * the signup completes. - */ - - }, { - key: 'signUp', - value: function (username, password, attrs, options) { - attrs = attrs || {}; - attrs.username = username; - attrs.password = password; - var user = new ParseUser(attrs); - return user.signUp({}, options); - } - - /** - * Logs in a user with a username (or email) and password. On success, this - * saves the session to disk, so you can retrieve the currently logged in - * user usingcurrent
.
- *
- * Calls options.success or options.error on completion.
- * - * @method logIn - * @param {String} username The username (or email) to log in with. - * @param {String} password The password to log in with. - * @param {Object} options A Backbone-style options object. - * @static - * @return {Parse.Promise} A promise that is fulfilled with the user when - * the login completes. - */ - - }, { - key: 'logIn', - value: function (username, password, options) { - if (typeof username !== 'string') { - return _ParsePromise2.default.error(new _ParseError2.default(_ParseError2.default.OTHER_CAUSE, 'Username must be a string.')); - } else if (typeof password !== 'string') { - return _ParsePromise2.default.error(new _ParseError2.default(_ParseError2.default.OTHER_CAUSE, 'Password must be a string.')); - } - var user = new ParseUser(); - user._finishFetch({ username: username, password: password }); - return user.logIn(options); - } - - /** - * Logs in a user with a session token. On success, this saves the session - * to disk, so you can retrieve the currently logged in user using - *current
.
- *
- * Calls options.success or options.error on completion.
- * - * @method become - * @param {String} sessionToken The sessionToken to log in with. - * @param {Object} options A Backbone-style options object. - * @static - * @return {Parse.Promise} A promise that is fulfilled with the user when - * the login completes. - */ - - }, { - key: 'become', - value: function (sessionToken, options) { - if (!canUseCurrentUser) { - throw new Error('It is not memory-safe to become a user in a server environment'); - } - options = options || {}; - - var becomeOptions = { - sessionToken: sessionToken - }; - if (options.hasOwnProperty('useMasterKey')) { - becomeOptions.useMasterKey = options.useMasterKey; - } - - var controller = _CoreManager2.default.getUserController(); - return controller.become(becomeOptions)._thenRunCallbacks(options); - } - }, { - key: 'logInWith', - value: function (provider, options) { - return ParseUser._logInWith(provider, options); - } - - /** - * Logs out the currently logged in user session. This will remove the - * session from disk, log out of linked services, and future calls to - *current
will return null
.
- * @method logOut
- * @static
- * @return {Parse.Promise} A promise that is resolved when the session is
- * destroyed on the server.
- */
-
- }, {
- key: 'logOut',
- value: function () {
- if (!canUseCurrentUser) {
- throw new Error('There is no current user user on a node.js server environment.');
- }
-
- var controller = _CoreManager2.default.getUserController();
- return controller.logOut();
- }
-
- /**
- * Requests a password reset email to be sent to the specified email address
- * associated with the user account. This email allows the user to securely
- * reset their password on the Parse site.
- *
- * Calls options.success or options.error on completion.
- * - * @method requestPasswordReset - * @param {String} email The email address associated with the user that - * forgot their password. - * @param {Object} options A Backbone-style options object. - * @static - */ - - }, { - key: 'requestPasswordReset', - value: function (email, options) { - options = options || {}; - - var requestOptions = {}; - if (options.hasOwnProperty('useMasterKey')) { - requestOptions.useMasterKey = options.useMasterKey; - } - - var controller = _CoreManager2.default.getUserController(); - return controller.requestPasswordReset(email, requestOptions)._thenRunCallbacks(options); - } - - /** - * Allow someone to define a custom User class without className - * being rewritten to _User. The default behavior is to rewrite - * User to _User for legacy reasons. This allows developers to - * override that behavior. - * - * @method allowCustomUserClass - * @param {Boolean} isAllowed Whether or not to allow custom User class - * @static - */ - - }, { - key: 'allowCustomUserClass', - value: function (isAllowed) { - _CoreManager2.default.set('PERFORM_USER_REWRITE', !isAllowed); - } - - /** - * Allows a legacy application to start using revocable sessions. If the - * current session token is not revocable, a request will be made for a new, - * revocable session. - * It is not necessary to call this method from cloud code unless you are - * handling user signup or login from the server side. In a cloud code call, - * this function will not attempt to upgrade the current token. - * @method enableRevocableSession - * @param {Object} options A Backbone-style options object. - * @static - * @return {Parse.Promise} A promise that is resolved when the process has - * completed. If a replacement session token is requested, the promise - * will be resolved after a new token has been fetched. - */ - - }, { - key: 'enableRevocableSession', - value: function (options) { - options = options || {}; - _CoreManager2.default.set('FORCE_REVOCABLE_SESSION', true); - if (canUseCurrentUser) { - var current = ParseUser.current(); - if (current) { - return current._upgradeToRevocableSession(options); - } - } - return _ParsePromise2.default.as()._thenRunCallbacks(options); - } - - /** - * Enables the use of become or the current user in a server - * environment. These features are disabled by default, since they depend on - * global objects that are not memory-safe for most servers. - * @method enableUnsafeCurrentUser - * @static - */ - - }, { - key: 'enableUnsafeCurrentUser', - value: function () { - canUseCurrentUser = true; - } - - /** - * Disables the use of become or the current user in any environment. - * These features are disabled on servers by default, since they depend on - * global objects that are not memory-safe for most servers. - * @method disableUnsafeCurrentUser - * @static - */ - - }, { - key: 'disableUnsafeCurrentUser', - value: function () { - canUseCurrentUser = false; - } - }, { - key: '_registerAuthenticationProvider', - value: function (provider) { - authProviders[provider.getAuthType()] = provider; - // Synchronize the current user with the auth provider. - ParseUser.currentAsync().then(function (current) { - if (current) { - current._synchronizeAuthData(provider.getAuthType()); - } - }); - } - }, { - key: '_logInWith', - value: function (provider, options) { - var user = new ParseUser(); - return user._linkWith(provider, options); - } - }, { - key: '_clearCache', - value: function () { - currentUserCache = null; - currentUserCacheMatchesDisk = false; - } - }, { - key: '_setCurrentUserCache', - value: function (user) { - currentUserCache = user; - } - }]); - return ParseUser; -}(_ParseObject3.default); - -exports.default = ParseUser; - -_ParseObject3.default.registerSubclass('_User', ParseUser); - -var DefaultController = { - updateUserOnDisk: function (user) { - var path = _Storage2.default.generatePath(CURRENT_USER_KEY); - var json = user.toJSON(); - json.className = '_User'; - return _Storage2.default.setItemAsync(path, (0, _stringify2.default)(json)).then(function () { - return user; - }); - }, - removeUserFromDisk: function () { - var path = _Storage2.default.generatePath(CURRENT_USER_KEY); - currentUserCacheMatchesDisk = true; - currentUserCache = null; - return _Storage2.default.removeItemAsync(path); - }, - setCurrentUser: function (user) { - currentUserCache = user; - user._cleanupAuthData(); - user._synchronizeAllAuthData(); - return DefaultController.updateUserOnDisk(user); - }, - currentUser: function () { - if (currentUserCache) { - return currentUserCache; - } - if (currentUserCacheMatchesDisk) { - return null; - } - if (_Storage2.default.async()) { - throw new Error('Cannot call currentUser() when using a platform with an async ' + 'storage system. Call currentUserAsync() instead.'); - } - var path = _Storage2.default.generatePath(CURRENT_USER_KEY); - var userData = _Storage2.default.getItem(path); - currentUserCacheMatchesDisk = true; - if (!userData) { - currentUserCache = null; - return null; - } - userData = JSON.parse(userData); - if (!userData.className) { - userData.className = '_User'; - } - if (userData._id) { - if (userData.objectId !== userData._id) { - userData.objectId = userData._id; - } - delete userData._id; - } - if (userData._sessionToken) { - userData.sessionToken = userData._sessionToken; - delete userData._sessionToken; - } - var current = _ParseObject3.default.fromJSON(userData); - currentUserCache = current; - current._synchronizeAllAuthData(); - return current; - }, - currentUserAsync: function () { - if (currentUserCache) { - return _ParsePromise2.default.as(currentUserCache); - } - if (currentUserCacheMatchesDisk) { - return _ParsePromise2.default.as(null); - } - var path = _Storage2.default.generatePath(CURRENT_USER_KEY); - return _Storage2.default.getItemAsync(path).then(function (userData) { - currentUserCacheMatchesDisk = true; - if (!userData) { - currentUserCache = null; - return _ParsePromise2.default.as(null); - } - userData = JSON.parse(userData); - if (!userData.className) { - userData.className = '_User'; - } - if (userData._id) { - if (userData.objectId !== userData._id) { - userData.objectId = userData._id; - } - delete userData._id; - } - if (userData._sessionToken) { - userData.sessionToken = userData._sessionToken; - delete userData._sessionToken; - } - var current = _ParseObject3.default.fromJSON(userData); - currentUserCache = current; - current._synchronizeAllAuthData(); - return _ParsePromise2.default.as(current); - }); - }, - signUp: function (user, attrs, options) { - var username = attrs && attrs.username || user.get('username'); - var password = attrs && attrs.password || user.get('password'); - - if (!username || !username.length) { - return _ParsePromise2.default.error(new _ParseError2.default(_ParseError2.default.OTHER_CAUSE, 'Cannot sign up user with an empty name.')); - } - if (!password || !password.length) { - return _ParsePromise2.default.error(new _ParseError2.default(_ParseError2.default.OTHER_CAUSE, 'Cannot sign up user with an empty password.')); - } - - return user.save(attrs, options).then(function () { - // Clear the password field - user._finishFetch({ password: undefined }); - - if (canUseCurrentUser) { - return DefaultController.setCurrentUser(user); - } - return user; - }); - }, - logIn: function (user, options) { - var RESTController = _CoreManager2.default.getRESTController(); - var stateController = _CoreManager2.default.getObjectStateController(); - var auth = { - username: user.get('username'), - password: user.get('password') - }; - return RESTController.request('GET', 'login', auth, options).then(function (response, status) { - user._migrateId(response.objectId); - user._setExisted(true); - stateController.setPendingOp(user._getStateIdentifier(), 'username', undefined); - stateController.setPendingOp(user._getStateIdentifier(), 'password', undefined); - response.password = undefined; - user._finishFetch(response); - if (!canUseCurrentUser) { - // We can't set the current user, so just return the one we logged in - return _ParsePromise2.default.as(user); - } - return DefaultController.setCurrentUser(user); - }); - }, - become: function (options) { - var user = new ParseUser(); - var RESTController = _CoreManager2.default.getRESTController(); - return RESTController.request('GET', 'users/me', {}, options).then(function (response, status) { - user._finishFetch(response); - user._setExisted(true); - return DefaultController.setCurrentUser(user); - }); - }, - logOut: function () { - return DefaultController.currentUserAsync().then(function (currentUser) { - var path = _Storage2.default.generatePath(CURRENT_USER_KEY); - var promise = _Storage2.default.removeItemAsync(path); - var RESTController = _CoreManager2.default.getRESTController(); - if (currentUser !== null) { - var currentSession = currentUser.getSessionToken(); - if (currentSession && (0, _isRevocableSession2.default)(currentSession)) { - promise = promise.then(function () { - return RESTController.request('POST', 'logout', {}, { sessionToken: currentSession }); - }); - } - currentUser._logOutWithAll(); - currentUser._finishFetch({ sessionToken: undefined }); - } - currentUserCacheMatchesDisk = true; - currentUserCache = null; - - return promise; - }); - }, - requestPasswordReset: function (email, options) { - var RESTController = _CoreManager2.default.getRESTController(); - return RESTController.request('POST', 'requestPasswordReset', { email: email }, options); - }, - upgradeToRevocableSession: function (user, options) { - var token = user.getSessionToken(); - if (!token) { - return _ParsePromise2.default.error(new _ParseError2.default(_ParseError2.default.SESSION_MISSING, 'Cannot upgrade a user with no session token')); - } - - options.sessionToken = token; - - var RESTController = _CoreManager2.default.getRESTController(); - return RESTController.request('POST', 'upgradeToRevocableSession', {}, options).then(function (result) { - var session = new _ParseSession2.default(); - session._finishFetch(result); - user._finishFetch({ sessionToken: session.getSessionToken() }); - if (user.isCurrent()) { - return DefaultController.setCurrentUser(user); - } - return _ParsePromise2.default.as(user); - }); - }, - linkWith: function (user, authData) { - return user.save({ authData: authData }).then(function () { - if (canUseCurrentUser) { - return DefaultController.setCurrentUser(user); - } - return user; - }); - } -}; - -_CoreManager2.default.setUserController(DefaultController); \ No newline at end of file diff --git a/lib/node/Push.js b/lib/node/Push.js deleted file mode 100644 index cfb740767..000000000 --- a/lib/node/Push.js +++ /dev/null @@ -1,98 +0,0 @@ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _typeof2 = require('babel-runtime/helpers/typeof'); - -var _typeof3 = _interopRequireDefault(_typeof2); - -exports.send = send; - -var _CoreManager = require('./CoreManager'); - -var _CoreManager2 = _interopRequireDefault(_CoreManager); - -var _ParseQuery = require('./ParseQuery'); - -var _ParseQuery2 = _interopRequireDefault(_ParseQuery); - -function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { default: obj }; -} - -/** - * Contains functions to deal with Push in Parse. - * @class Parse.Push - * @static - */ - -/** - * Sends a push notification. - * @method send - * @param {Object} data - The data of the push notification. Valid fields - * are: - *- * var dimensions = { - * gender: 'm', - * source: 'web', - * dayType: 'weekend' - * }; - * Parse.Analytics.track('signup', dimensions); - *- * - * There is a default limit of 8 dimensions per event tracked. - * - * @method track - * @param {String} name The name of the custom event to report to Parse as - * having happened. - * @param {Object} dimensions The dictionary of information by which to - * segment this event. - * @param {Object} options A Backbone-style callback object. - * @return {Parse.Promise} A promise that is resolved when the round-trip - * to the server completes. - */ -export function track(name, dimensions, options) { - name = name || ''; - name = name.replace(/^\s*/, ''); - name = name.replace(/\s*$/, ''); - if (name.length === 0) { - throw new TypeError('A name for the custom event must be provided'); - } - - for (var key in dimensions) { - if (typeof key !== 'string' || typeof dimensions[key] !== 'string') { - throw new TypeError('track() dimensions expects keys and values of type "string".'); - } - } - - options = options || {}; - return CoreManager.getAnalyticsController().track(name, dimensions)._thenRunCallbacks(options); -} - -var DefaultController = { - track(name, dimensions) { - var RESTController = CoreManager.getRESTController(); - return RESTController.request('POST', 'events/' + name, { dimensions: dimensions }); - } -}; - -CoreManager.setAnalyticsController(DefaultController); \ No newline at end of file diff --git a/lib/react-native/Cloud.js b/lib/react-native/Cloud.js deleted file mode 100644 index 604707acd..000000000 --- a/lib/react-native/Cloud.js +++ /dev/null @@ -1,86 +0,0 @@ -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * - */ - -import CoreManager from './CoreManager'; -import decode from './decode'; -import encode from './encode'; -import ParseError from './ParseError'; -import ParsePromise from './ParsePromise'; - -/** - * Contains functions for calling and declaring - * cloud functions. - *
- * Some functions are only available from Cloud Code. - *
- * - * @class Parse.Cloud - * @static - */ - -/** - * Makes a call to a cloud function. - * @method run - * @param {String} name The function name. - * @param {Object} data The parameters to send to the cloud function. - * @param {Object} options A Backbone-style options object - * options.success, if set, should be a function to handle a successful - * call to a cloud function. options.error should be a function that - * handles an error running the cloud function. Both functions are - * optional. Both functions take a single argument. - * @return {Parse.Promise} A promise that will be resolved with the result - * of the function. - */ -export function run(name, data, options) { - options = options || {}; - - if (typeof name !== 'string' || name.length === 0) { - throw new TypeError('Cloud function name must be a string.'); - } - - var requestOptions = {}; - if (options.useMasterKey) { - requestOptions.useMasterKey = options.useMasterKey; - } - if (options.sessionToken) { - requestOptions.sessionToken = options.sessionToken; - } - - return CoreManager.getCloudController().run(name, data, requestOptions)._thenRunCallbacks(options); -} - -var DefaultController = { - run(name, data, options) { - var RESTController = CoreManager.getRESTController(); - - var payload = encode(data, true); - - var requestOptions = {}; - if (options.hasOwnProperty('useMasterKey')) { - requestOptions.useMasterKey = options.useMasterKey; - } - if (options.hasOwnProperty('sessionToken')) { - requestOptions.sessionToken = options.sessionToken; - } - - var request = RESTController.request('POST', 'functions/' + name, payload, requestOptions); - - return request.then(function (res) { - var decoded = decode(res); - if (decoded && decoded.hasOwnProperty('result')) { - return ParsePromise.as(decoded.result); - } - return ParsePromise.error(new ParseError(ParseError.INVALID_JSON, 'The server returned an invalid response.')); - })._thenRunCallbacks(options); - } -}; - -CoreManager.setCloudController(DefaultController); \ No newline at end of file diff --git a/lib/react-native/CoreManager.js b/lib/react-native/CoreManager.js deleted file mode 100644 index 97062e559..000000000 --- a/lib/react-native/CoreManager.js +++ /dev/null @@ -1,188 +0,0 @@ -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * - */ - -var config = { - // Defaults - IS_NODE: typeof process !== 'undefined' && !!process.versions && !!process.versions.node && !process.versions.electron, - REQUEST_ATTEMPT_LIMIT: 5, - SERVER_URL: 'https://api.parse.com/1', - LIVEQUERY_SERVER_URL: null, - VERSION: 'js' + '1.9.2', - APPLICATION_ID: null, - JAVASCRIPT_KEY: null, - MASTER_KEY: null, - USE_MASTER_KEY: false, - PERFORM_USER_REWRITE: true, - FORCE_REVOCABLE_SESSION: false -}; - -function requireMethods(name, methods, controller) { - methods.forEach(func => { - if (typeof controller[func] !== 'function') { - throw new Error(`${name} must implement ${func}()`); - } - }); -} - -module.exports = { - get: function (key) { - if (config.hasOwnProperty(key)) { - return config[key]; - } - throw new Error('Configuration key not found: ' + key); - }, - - set: function (key, value) { - config[key] = value; - }, - - /* Specialized Controller Setters/Getters */ - - setAnalyticsController(controller) { - requireMethods('AnalyticsController', ['track'], controller); - config['AnalyticsController'] = controller; - }, - - getAnalyticsController() { - return config['AnalyticsController']; - }, - - setCloudController(controller) { - requireMethods('CloudController', ['run'], controller); - config['CloudController'] = controller; - }, - - getCloudController() { - return config['CloudController']; - }, - - setConfigController(controller) { - requireMethods('ConfigController', ['current', 'get'], controller); - config['ConfigController'] = controller; - }, - - getConfigController() { - return config['ConfigController']; - }, - - setFileController(controller) { - requireMethods('FileController', ['saveFile', 'saveBase64'], controller); - config['FileController'] = controller; - }, - - getFileController() { - return config['FileController']; - }, - - setInstallationController(controller) { - requireMethods('InstallationController', ['currentInstallationId'], controller); - config['InstallationController'] = controller; - }, - - getInstallationController() { - return config['InstallationController']; - }, - - setObjectController(controller) { - requireMethods('ObjectController', ['save', 'fetch', 'destroy'], controller); - config['ObjectController'] = controller; - }, - - getObjectController() { - return config['ObjectController']; - }, - - setObjectStateController(controller) { - requireMethods('ObjectStateController', ['getState', 'initializeState', 'removeState', 'getServerData', 'setServerData', 'getPendingOps', 'setPendingOp', 'pushPendingState', 'popPendingState', 'mergeFirstPendingState', 'getObjectCache', 'estimateAttribute', 'estimateAttributes', 'commitServerChanges', 'enqueueTask', 'clearAllState'], controller); - - config['ObjectStateController'] = controller; - }, - - getObjectStateController() { - return config['ObjectStateController']; - }, - - setPushController(controller) { - requireMethods('PushController', ['send'], controller); - config['PushController'] = controller; - }, - - getPushController() { - return config['PushController']; - }, - - setQueryController(controller) { - requireMethods('QueryController', ['find'], controller); - config['QueryController'] = controller; - }, - - getQueryController() { - return config['QueryController']; - }, - - setRESTController(controller) { - requireMethods('RESTController', ['request', 'ajax'], controller); - config['RESTController'] = controller; - }, - - getRESTController() { - return config['RESTController']; - }, - - setSessionController(controller) { - requireMethods('SessionController', ['getSession'], controller); - config['SessionController'] = controller; - }, - - getSessionController() { - return config['SessionController']; - }, - - setStorageController(controller) { - if (controller.async) { - requireMethods('An async StorageController', ['getItemAsync', 'setItemAsync', 'removeItemAsync'], controller); - } else { - requireMethods('A synchronous StorageController', ['getItem', 'setItem', 'removeItem'], controller); - } - config['StorageController'] = controller; - }, - - getStorageController() { - return config['StorageController']; - }, - - setUserController(controller) { - requireMethods('UserController', ['setCurrentUser', 'currentUser', 'currentUserAsync', 'signUp', 'logIn', 'become', 'logOut', 'requestPasswordReset', 'upgradeToRevocableSession', 'linkWith'], controller); - config['UserController'] = controller; - }, - - getUserController() { - return config['UserController']; - }, - - setLiveQueryController(controller) { - requireMethods('LiveQueryController', ['subscribe', 'unsubscribe', 'open', 'close'], controller); - config['LiveQueryController'] = controller; - }, - - getLiveQueryController() { - return config['LiveQueryController']; - }, - - setHooksController(controller) { - requireMethods('HooksController', ['create', 'get', 'update', 'remove'], controller); - config['HooksController'] = controller; - }, - - getHooksController() { - return config['HooksController']; - } -}; \ No newline at end of file diff --git a/lib/react-native/EventEmitter.js b/lib/react-native/EventEmitter.js deleted file mode 100644 index 3450b4ac6..000000000 --- a/lib/react-native/EventEmitter.js +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * This is a simple wrapper to unify EventEmitter implementations across platforms. - */ - -{ - const EventEmitter = require('EventEmitter'); - EventEmitter.prototype.on = EventEmitter.prototype.addListener; - module.exports = EventEmitter; -} \ No newline at end of file diff --git a/lib/react-native/FacebookUtils.js b/lib/react-native/FacebookUtils.js deleted file mode 100644 index 24ce10c01..000000000 --- a/lib/react-native/FacebookUtils.js +++ /dev/null @@ -1,229 +0,0 @@ -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * -weak - */ - -import parseDate from './parseDate'; -import ParseUser from './ParseUser'; - -var PUBLIC_KEY = "*"; - -var initialized = false; -var requestedPermissions; -var initOptions; -var provider = { - authenticate(options) { - if (typeof FB === 'undefined') { - options.error(this, 'Facebook SDK not found.'); - } - FB.login(response => { - if (response.authResponse) { - if (options.success) { - options.success(this, { - id: response.authResponse.userID, - access_token: response.authResponse.accessToken, - expiration_date: new Date(response.authResponse.expiresIn * 1000 + new Date().getTime()).toJSON() - }); - } - } else { - if (options.error) { - options.error(this, response); - } - } - }, { - scope: requestedPermissions - }); - }, - - restoreAuthentication(authData) { - if (authData) { - var expiration = parseDate(authData.expiration_date); - var expiresIn = expiration ? (expiration.getTime() - new Date().getTime()) / 1000 : 0; - - var authResponse = { - userID: authData.id, - accessToken: authData.access_token, - expiresIn: expiresIn - }; - var newOptions = {}; - if (initOptions) { - for (var key in initOptions) { - newOptions[key] = initOptions[key]; - } - } - newOptions.authResponse = authResponse; - - // Suppress checks for login status from the browser. - newOptions.status = false; - - // If the user doesn't match the one known by the FB SDK, log out. - // Most of the time, the users will match -- it's only in cases where - // the FB SDK knows of a different user than the one being restored - // from a Parse User that logged in with username/password. - var existingResponse = FB.getAuthResponse(); - if (existingResponse && existingResponse.userID !== authResponse.userID) { - FB.logout(); - } - - FB.init(newOptions); - } - return true; - }, - - getAuthType() { - return 'facebook'; - }, - - deauthenticate() { - this.restoreAuthentication(null); - } -}; - -/** - * Provides a set of utilities for using Parse with Facebook. - * @class Parse.FacebookUtils - * @static - */ -var FacebookUtils = { - /** - * Initializes Parse Facebook integration. Call this function after you - * have loaded the Facebook Javascript SDK with the same parameters - * as you would pass to
- *
- * FB.init()
. Parse.FacebookUtils will invoke FB.init() for you
- * with these arguments.
- *
- * @method init
- * @param {Object} options Facebook options argument as described here:
- *
- * FB.init(). The status flag will be coerced to 'false' because it
- * interferes with Parse Facebook integration. Call FB.getLoginStatus()
- * explicitly if this behavior is required by your application.
- */
- init(options) {
- if (typeof FB === 'undefined') {
- throw new Error('The Facebook JavaScript SDK must be loaded before calling init.');
- }
- initOptions = {};
- if (options) {
- for (var key in options) {
- initOptions[key] = options[key];
- }
- }
- if (initOptions.status && typeof console !== 'undefined') {
- var warn = console.warn || console.log || function () {};
- warn.call(console, 'The "status" flag passed into' + ' FB.init, when set to true, can interfere with Parse Facebook' + ' integration, so it has been suppressed. Please call' + ' FB.getLoginStatus() explicitly if you require this behavior.');
- }
- initOptions.status = false;
- FB.init(initOptions);
- ParseUser._registerAuthenticationProvider(provider);
- initialized = true;
- },
-
- /**
- * Gets whether the user has their account linked to Facebook.
- *
- * @method isLinked
- * @param {Parse.User} user User to check for a facebook link.
- * The user must be logged in on this device.
- * @return {Boolean} true
if the user has their account
- * linked to Facebook.
- */
- isLinked(user) {
- return user._isLinked('facebook');
- },
-
- /**
- * Logs in a user using Facebook. This method delegates to the Facebook
- * SDK to authenticate the user, and then automatically logs in (or
- * creates, in the case where it is a new user) a Parse.User.
- *
- * @method logIn
- * @param {String, Object} permissions The permissions required for Facebook
- * log in. This is a comma-separated string of permissions.
- * Alternatively, supply a Facebook authData object as described in our
- * REST API docs if you want to handle getting facebook auth tokens
- * yourself.
- * @param {Object} options Standard options object with success and error
- * callbacks.
- */
- logIn(permissions, options) {
- if (!permissions || typeof permissions === 'string') {
- if (!initialized) {
- throw new Error('You must initialize FacebookUtils before calling logIn.');
- }
- requestedPermissions = permissions;
- return ParseUser._logInWith('facebook', options);
- } else {
- var newOptions = {};
- if (options) {
- for (var key in options) {
- newOptions[key] = options[key];
- }
- }
- newOptions.authData = permissions;
- return ParseUser._logInWith('facebook', newOptions);
- }
- },
-
- /**
- * Links Facebook to an existing PFUser. This method delegates to the
- * Facebook SDK to authenticate the user, and then automatically links
- * the account to the Parse.User.
- *
- * @method link
- * @param {Parse.User} user User to link to Facebook. This must be the
- * current user.
- * @param {String, Object} permissions The permissions required for Facebook
- * log in. This is a comma-separated string of permissions.
- * Alternatively, supply a Facebook authData object as described in our
- * REST API docs if you want to handle getting facebook auth tokens
- * yourself.
- * @param {Object} options Standard options object with success and error
- * callbacks.
- */
- link(user, permissions, options) {
- if (!permissions || typeof permissions === 'string') {
- if (!initialized) {
- throw new Error('You must initialize FacebookUtils before calling link.');
- }
- requestedPermissions = permissions;
- return user._linkWith('facebook', options);
- } else {
- var newOptions = {};
- if (options) {
- for (var key in options) {
- newOptions[key] = options[key];
- }
- }
- newOptions.authData = permissions;
- return user._linkWith('facebook', newOptions);
- }
- },
-
- /**
- * Unlinks the Parse.User from a Facebook account.
- *
- * @method unlink
- * @param {Parse.User} user User to unlink from Facebook. This must be the
- * current user.
- * @param {Object} options Standard options object with success and error
- * callbacks.
- */
- unlink: function (user, options) {
- if (!initialized) {
- throw new Error('You must initialize FacebookUtils before calling unlink.');
- }
- return user._unlinkFrom('facebook', options);
- }
-};
-
-export default FacebookUtils;
\ No newline at end of file
diff --git a/lib/react-native/InstallationController.js b/lib/react-native/InstallationController.js
deleted file mode 100644
index 75aad6d0b..000000000
--- a/lib/react-native/InstallationController.js
+++ /dev/null
@@ -1,54 +0,0 @@
-/**
- * Copyright (c) 2015-present, Parse, LLC.
- * All rights reserved.
- *
- * This source code is licensed under the BSD-style license found in the
- * LICENSE file in the root directory of this source tree. An additional grant
- * of patent rights can be found in the PATENTS file in the same directory.
- *
- *
- */
-
-import CoreManager from './CoreManager';
-import ParsePromise from './ParsePromise';
-import Storage from './Storage';
-
-var iidCache = null;
-
-function hexOctet() {
- return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
-}
-
-function generateId() {
- return hexOctet() + hexOctet() + '-' + hexOctet() + '-' + hexOctet() + '-' + hexOctet() + '-' + hexOctet() + hexOctet() + hexOctet();
-}
-
-var InstallationController = {
- currentInstallationId() {
- if (typeof iidCache === 'string') {
- return ParsePromise.as(iidCache);
- }
- var path = Storage.generatePath('installationId');
- return Storage.getItemAsync(path).then(iid => {
- if (!iid) {
- iid = generateId();
- return Storage.setItemAsync(path, iid).then(() => {
- iidCache = iid;
- return iid;
- });
- }
- iidCache = iid;
- return iid;
- });
- },
-
- _clearCache() {
- iidCache = null;
- },
-
- _setInstallationIdCache(iid) {
- iidCache = iid;
- }
-};
-
-module.exports = InstallationController;
\ No newline at end of file
diff --git a/lib/react-native/LiveQueryClient.js b/lib/react-native/LiveQueryClient.js
deleted file mode 100644
index 0af495cb4..000000000
--- a/lib/react-native/LiveQueryClient.js
+++ /dev/null
@@ -1,430 +0,0 @@
-/**
- * Copyright (c) 2015-present, Parse, LLC.
- * All rights reserved.
- *
- * This source code is licensed under the BSD-style license found in the
- * LICENSE file in the root directory of this source tree. An additional grant
- * of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-import EventEmitter from './EventEmitter';
-import ParsePromise from './ParsePromise';
-import ParseObject from './ParseObject';
-import LiveQuerySubscription from './LiveQuerySubscription';
-
-// The LiveQuery client inner state
-const CLIENT_STATE = {
- INITIALIZED: 'initialized',
- CONNECTING: 'connecting',
- CONNECTED: 'connected',
- CLOSED: 'closed',
- RECONNECTING: 'reconnecting',
- DISCONNECTED: 'disconnected'
-};
-
-// The event type the LiveQuery client should sent to server
-const OP_TYPES = {
- CONNECT: 'connect',
- SUBSCRIBE: 'subscribe',
- UNSUBSCRIBE: 'unsubscribe',
- ERROR: 'error'
-};
-
-// The event we get back from LiveQuery server
-const OP_EVENTS = {
- CONNECTED: 'connected',
- SUBSCRIBED: 'subscribed',
- UNSUBSCRIBED: 'unsubscribed',
- ERROR: 'error',
- CREATE: 'create',
- UPDATE: 'update',
- ENTER: 'enter',
- LEAVE: 'leave',
- DELETE: 'delete'
-};
-
-// The event the LiveQuery client should emit
-const CLIENT_EMMITER_TYPES = {
- CLOSE: 'close',
- ERROR: 'error',
- OPEN: 'open'
-};
-
-// The event the LiveQuery subscription should emit
-const SUBSCRIPTION_EMMITER_TYPES = {
- OPEN: 'open',
- CLOSE: 'close',
- ERROR: 'error',
- CREATE: 'create',
- UPDATE: 'update',
- ENTER: 'enter',
- LEAVE: 'leave',
- DELETE: 'delete'
-};
-
-let generateInterval = k => {
- return Math.random() * Math.min(30, Math.pow(2, k) - 1) * 1000;
-};
-
-/**
- * Creates a new LiveQueryClient.
- * Extends events.EventEmitter
- * cloud functions.
- *
- * A wrapper of a standard WebSocket client. We add several useful methods to
- * help you connect/disconnect to LiveQueryServer, subscribe/unsubscribe a ParseQuery easily.
- *
- * javascriptKey and masterKey are used for verifying the LiveQueryClient when it tries
- * to connect to the LiveQuery server
- *
- * @class Parse.LiveQueryClient
- * @constructor
- * @param {Object} options
- * @param {string} options.applicationId - applicationId of your Parse app
- * @param {string} options.serverURL - the URL of your LiveQuery server
- * @param {string} options.javascriptKey (optional)
- * @param {string} options.masterKey (optional) Your Parse Master Key. (Node.js only!)
- * @param {string} options.sessionToken (optional)
- *
- *
- * We expose three events to help you monitor the status of the LiveQueryClient.
- *
- * - * let Parse = require('parse/node'); - * let LiveQueryClient = Parse.LiveQueryClient; - * let client = new LiveQueryClient({ - * applicationId: '', - * serverURL: '', - * javascriptKey: '', - * masterKey: '' - * }); - *- * - * Open - When we establish the WebSocket connection to the LiveQuery server, you'll get this event. - *
- * client.on('open', () => { - * - * });- * - * Close - When we lose the WebSocket connection to the LiveQuery server, you'll get this event. - *
- * client.on('close', () => { - * - * });- * - * Error - When some network error or LiveQuery server error happens, you'll get this event. - *
- * client.on('error', (error) => { - * - * });- * - * - */ -export default class LiveQueryClient extends EventEmitter { - - constructor({ - applicationId, - serverURL, - javascriptKey, - masterKey, - sessionToken - }) { - super(); - - if (!serverURL || serverURL.indexOf('ws') !== 0) { - throw new Error('You need to set a proper Parse LiveQuery server url before using LiveQueryClient'); - } - - this.reconnectHandle = null; - this.attempts = 1;; - this.id = 0; - this.requestId = 1; - this.serverURL = serverURL; - this.applicationId = applicationId; - this.javascriptKey = javascriptKey; - this.masterKey = masterKey; - this.sessionToken = sessionToken; - this.connectPromise = new ParsePromise(); - this.subscriptions = new Map(); - this.state = CLIENT_STATE.INITIALIZED; - } - - shouldOpen() { - return this.state === CLIENT_STATE.INITIALIZED || this.state === CLIENT_STATE.DISCONNECTED; - } - - /** - * Subscribes to a ParseQuery - * - * If you provide the sessionToken, when the LiveQuery server gets ParseObject's - * updates from parse server, it'll try to check whether the sessionToken fulfills - * the ParseObject's ACL. The LiveQuery server will only send updates to clients whose - * sessionToken is fit for the ParseObject's ACL. You can check the LiveQuery protocol - * here for more details. The subscription you get is the same subscription you get - * from our Standard API. - * - * @method subscribe - * @param {Object} query - the ParseQuery you want to subscribe to - * @param {string} sessionToken (optional) - * @return {Object} subscription - */ - subscribe(query, sessionToken) { - if (!query) { - return; - } - let where = query.toJSON().where; - let className = query.className; - let subscribeRequest = { - op: OP_TYPES.SUBSCRIBE, - requestId: this.requestId, - query: { - className, - where - } - }; - - if (sessionToken) { - subscribeRequest.sessionToken = sessionToken; - } - - let subscription = new LiveQuerySubscription(this.requestId, query, sessionToken); - this.subscriptions.set(this.requestId, subscription); - this.requestId += 1; - this.connectPromise.then(() => { - this.socket.send(JSON.stringify(subscribeRequest)); - }); - - // adding listener so process does not crash - // best practice is for developer to register their own listener - subscription.on('error', () => {}); - - return subscription; - } - - /** - * After calling unsubscribe you'll stop receiving events from the subscription object. - * - * @method unsubscribe - * @param {Object} subscription - subscription you would like to unsubscribe from. - */ - unsubscribe(subscription) { - if (!subscription) { - return; - } - - this.subscriptions.delete(subscription.id); - let unsubscribeRequest = { - op: OP_TYPES.UNSUBSCRIBE, - requestId: subscription.id - }; - this.connectPromise.then(() => { - this.socket.send(JSON.stringify(unsubscribeRequest)); - }); - } - - /** - * After open is called, the LiveQueryClient will try to send a connect request - * to the LiveQuery server. - * - * @method open - */ - open() { - let WebSocketImplementation = this._getWebSocketImplementation(); - if (!WebSocketImplementation) { - this.emit(CLIENT_EMMITER_TYPES.ERROR, 'Can not find WebSocket implementation'); - return; - } - - if (this.state !== CLIENT_STATE.RECONNECTING) { - this.state = CLIENT_STATE.CONNECTING; - } - - // Get WebSocket implementation - this.socket = new WebSocketImplementation(this.serverURL); - - // Bind WebSocket callbacks - this.socket.onopen = () => { - this._handleWebSocketOpen(); - }; - - this.socket.onmessage = event => { - this._handleWebSocketMessage(event); - }; - - this.socket.onclose = () => { - this._handleWebSocketClose(); - }; - - this.socket.onerror = error => { - this._handleWebSocketError(error); - }; - } - - resubscribe() { - this.subscriptions.forEach((subscription, requestId) => { - let query = subscription.query; - let where = query.toJSON().where; - let className = query.className; - let sessionToken = subscription.sessionToken; - let subscribeRequest = { - op: OP_TYPES.SUBSCRIBE, - requestId, - query: { - className, - where - } - }; - - if (sessionToken) { - subscribeRequest.sessionToken = sessionToken; - } - - this.connectPromise.then(() => { - this.socket.send(JSON.stringify(subscribeRequest)); - }); - }); - } - - /** - * This method will close the WebSocket connection to this LiveQueryClient, - * cancel the auto reconnect and unsubscribe all subscriptions based on it. - * - * @method close - */ - close() { - if (this.state === CLIENT_STATE.INITIALIZED || this.state === CLIENT_STATE.DISCONNECTED) { - return; - } - this.state = CLIENT_STATE.DISCONNECTED; - this.socket.close(); - // Notify each subscription about the close - for (let subscription of this.subscriptions.values()) { - subscription.emit(SUBSCRIPTION_EMMITER_TYPES.CLOSE); - } - this._handleReset(); - this.emit(CLIENT_EMMITER_TYPES.CLOSE); - } - - _getWebSocketImplementation() { - return WebSocket; - } - - // ensure we start with valid state if connect is called again after close - _handleReset() { - this.attempts = 1;; - this.id = 0; - this.requestId = 1; - this.connectPromise = new ParsePromise(); - this.subscriptions = new Map(); - } - - _handleWebSocketOpen() { - this.attempts = 1; - let connectRequest = { - op: OP_TYPES.CONNECT, - applicationId: this.applicationId, - javascriptKey: this.javascriptKey, - masterKey: this.masterKey, - sessionToken: this.sessionToken - }; - this.socket.send(JSON.stringify(connectRequest)); - } - - _handleWebSocketMessage(event) { - let data = event.data; - if (typeof data === 'string') { - data = JSON.parse(data); - } - let subscription = null; - if (data.requestId) { - subscription = this.subscriptions.get(data.requestId); - } - switch (data.op) { - case OP_EVENTS.CONNECTED: - if (this.state === CLIENT_STATE.RECONNECTING) { - this.resubscribe(); - } - this.emit(CLIENT_EMMITER_TYPES.OPEN); - this.id = data.clientId; - this.connectPromise.resolve(); - this.state = CLIENT_STATE.CONNECTED; - break; - case OP_EVENTS.SUBSCRIBED: - if (subscription) { - subscription.emit(SUBSCRIPTION_EMMITER_TYPES.OPEN); - } - break; - case OP_EVENTS.ERROR: - if (data.requestId) { - if (subscription) { - subscription.emit(SUBSCRIPTION_EMMITER_TYPES.ERROR, data.error); - } - } else { - this.emit(CLIENT_EMMITER_TYPES.ERROR, data.error); - } - break; - case OP_EVENTS.UNSUBSCRIBED: - // We have already deleted subscription in unsubscribe(), do nothing here - break; - default: - // create, update, enter, leave, delete cases - let className = data.object.className; - // Delete the extrea __type and className fields during transfer to full JSON - delete data.object.__type; - delete data.object.className; - let parseObject = new ParseObject(className); - parseObject._finishFetch(data.object); - if (!subscription) { - break; - } - subscription.emit(data.op, parseObject); - } - } - - _handleWebSocketClose() { - if (this.state === CLIENT_STATE.DISCONNECTED) { - return; - } - this.state = CLIENT_STATE.CLOSED; - this.emit(CLIENT_EMMITER_TYPES.CLOSE); - // Notify each subscription about the close - for (let subscription of this.subscriptions.values()) { - subscription.emit(SUBSCRIPTION_EMMITER_TYPES.CLOSE); - } - this._handleReconnect(); - } - - _handleWebSocketError(error) { - this.emit(CLIENT_EMMITER_TYPES.ERROR, error); - for (let subscription of this.subscriptions.values()) { - subscription.emit(SUBSCRIPTION_EMMITER_TYPES.ERROR); - } - this._handleReconnect(); - } - - _handleReconnect() { - // if closed or currently reconnecting we stop attempting to reconnect - if (this.state === CLIENT_STATE.DISCONNECTED) { - return; - } - - this.state = CLIENT_STATE.RECONNECTING; - let time = generateInterval(this.attempts); - - // handle case when both close/error occur at frequent rates we ensure we do not reconnect unnecessarily. - // we're unable to distinguish different between close/error when we're unable to reconnect therefore - // we try to reonnect in both cases - // server side ws and browser WebSocket behave differently in when close/error get triggered - - if (this.reconnectHandle) { - clearTimeout(this.reconnectHandle); - } - - this.reconnectHandle = setTimeout((() => { - this.attempts++; - this.connectPromise = new ParsePromise(); - this.open(); - }).bind(this), time); - } -} \ No newline at end of file diff --git a/lib/react-native/LiveQuerySubscription.js b/lib/react-native/LiveQuerySubscription.js deleted file mode 100644 index 4f19b2b6e..000000000 --- a/lib/react-native/LiveQuerySubscription.js +++ /dev/null @@ -1,112 +0,0 @@ -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -import EventEmitter from './EventEmitter'; -import CoreManager from './CoreManager'; - -/** - * Creates a new LiveQuery Subscription. - * Extends events.EventEmitter - * cloud functions. - * - * @constructor - * @param {string} id - subscription id - * @param {string} query - query to subscribe to - * @param {string} sessionToken - optional session token - * - *
Open Event - When you call query.subscribe(), we send a subscribe request to - * the LiveQuery server, when we get the confirmation from the LiveQuery server, - * this event will be emitted. When the client loses WebSocket connection to the - * LiveQuery server, we will try to auto reconnect the LiveQuery server. If we - * reconnect the LiveQuery server and successfully resubscribe the ParseQuery, - * you'll also get this event. - * - *
- * subscription.on('open', () => { - * - * });- * - *
Create Event - When a new ParseObject is created and it fulfills the ParseQuery you subscribe, - * you'll get this event. The object is the ParseObject which is created. - * - *
- * subscription.on('create', (object) => { - * - * });- * - *
Update Event - When an existing ParseObject which fulfills the ParseQuery you subscribe - * is updated (The ParseObject fulfills the ParseQuery before and after changes), - * you'll get this event. The object is the ParseObject which is updated. - * Its content is the latest value of the ParseObject. - * - *
- * subscription.on('update', (object) => { - * - * });- * - *
Enter Event - When an existing ParseObject's old value doesn't fulfill the ParseQuery - * but its new value fulfills the ParseQuery, you'll get this event. The object is the - * ParseObject which enters the ParseQuery. Its content is the latest value of the ParseObject. - * - *
- * subscription.on('enter', (object) => { - * - * });- * - * - *
Update Event - When an existing ParseObject's old value fulfills the ParseQuery but its new value - * doesn't fulfill the ParseQuery, you'll get this event. The object is the ParseObject - * which leaves the ParseQuery. Its content is the latest value of the ParseObject. - * - *
- * subscription.on('leave', (object) => { - * - * });- * - * - *
Delete Event - When an existing ParseObject which fulfills the ParseQuery is deleted, you'll - * get this event. The object is the ParseObject which is deleted. - * - *
- * subscription.on('delete', (object) => { - * - * });- * - * - *
Close Event - When the client loses the WebSocket connection to the LiveQuery - * server and we stop receiving events, you'll get this event. - * - *
- * subscription.on('close', () => { - * - * });- * - * - */ -export default class Subscription extends EventEmitter { - constructor(id, query, sessionToken) { - super(); - this.id = id; - this.query = query; - this.sessionToken = sessionToken; - } - - /** - * @method unsubscribe - */ - unsubscribe() { - let _this = this; - CoreManager.getLiveQueryController().getDefaultLiveQueryClient().then(liveQueryClient => { - liveQueryClient.unsubscribe(_this); - _this.emit('close'); - this.resolve(); - }); - } -} \ No newline at end of file diff --git a/lib/react-native/ObjectStateMutations.js b/lib/react-native/ObjectStateMutations.js deleted file mode 100644 index 349dd150b..000000000 --- a/lib/react-native/ObjectStateMutations.js +++ /dev/null @@ -1,121 +0,0 @@ -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * - */ - -import encode from './encode'; -import ParseFile from './ParseFile'; -import ParseObject from './ParseObject'; -import ParsePromise from './ParsePromise'; -import ParseRelation from './ParseRelation'; -import TaskQueue from './TaskQueue'; -import { RelationOp } from './ParseOp'; - -export function defaultState() { - return { - serverData: {}, - pendingOps: [{}], - objectCache: {}, - tasks: new TaskQueue(), - existed: false - }; -} - -export function setServerData(serverData, attributes) { - for (let attr in attributes) { - if (typeof attributes[attr] !== 'undefined') { - serverData[attr] = attributes[attr]; - } else { - delete serverData[attr]; - } - } -} - -export function setPendingOp(pendingOps, attr, op) { - let last = pendingOps.length - 1; - if (op) { - pendingOps[last][attr] = op; - } else { - delete pendingOps[last][attr]; - } -} - -export function pushPendingState(pendingOps) { - pendingOps.push({}); -} - -export function popPendingState(pendingOps) { - let first = pendingOps.shift(); - if (!pendingOps.length) { - pendingOps[0] = {}; - } - return first; -} - -export function mergeFirstPendingState(pendingOps) { - let first = popPendingState(pendingOps); - let next = pendingOps[0]; - for (let attr in first) { - if (next[attr] && first[attr]) { - let merged = next[attr].mergeWith(first[attr]); - if (merged) { - next[attr] = merged; - } - } else { - next[attr] = first[attr]; - } - } -} - -export function estimateAttribute(serverData, pendingOps, className, id, attr) { - let value = serverData[attr]; - for (let i = 0; i < pendingOps.length; i++) { - if (pendingOps[i][attr]) { - if (pendingOps[i][attr] instanceof RelationOp) { - if (id) { - value = pendingOps[i][attr].applyTo(value, { className: className, id: id }, attr); - } - } else { - value = pendingOps[i][attr].applyTo(value); - } - } - } - return value; -} - -export function estimateAttributes(serverData, pendingOps, className, id) { - let data = {}; - - for (var attr in serverData) { - data[attr] = serverData[attr]; - } - for (let i = 0; i < pendingOps.length; i++) { - for (attr in pendingOps[i]) { - if (pendingOps[i][attr] instanceof RelationOp) { - if (id) { - data[attr] = pendingOps[i][attr].applyTo(data[attr], { className: className, id: id }, attr); - } - } else { - data[attr] = pendingOps[i][attr].applyTo(data[attr]); - } - } - } - return data; -} - -export function commitServerChanges(serverData, objectCache, changes) { - for (let attr in changes) { - let val = changes[attr]; - serverData[attr] = val; - if (val && typeof val === 'object' && !(val instanceof ParseObject) && !(val instanceof ParseFile) && !(val instanceof ParseRelation)) { - let json = encode(val, false, true); - objectCache[attr] = JSON.stringify(json); - } - } -} \ No newline at end of file diff --git a/lib/react-native/Parse.js b/lib/react-native/Parse.js deleted file mode 100644 index b95835026..000000000 --- a/lib/react-native/Parse.js +++ /dev/null @@ -1,141 +0,0 @@ -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -import decode from './decode'; -import encode from './encode'; -import CoreManager from './CoreManager'; -import InstallationController from './InstallationController'; -import * as ParseOp from './ParseOp'; -import RESTController from './RESTController'; - -/** - * Contains all Parse API classes and functions. - * @class Parse - * @static - */ -var Parse = { - /** - * Call this method first to set up your authentication tokens for Parse. - * You can get your keys from the Data Browser on parse.com. - * @method initialize - * @param {String} applicationId Your Parse Application ID. - * @param {String} javaScriptKey (optional) Your Parse JavaScript Key (Not needed for parse-server) - * @param {String} masterKey (optional) Your Parse Master Key. (Node.js only!) - * @static - */ - initialize(applicationId, javaScriptKey) { - Parse._initialize(applicationId, javaScriptKey); - }, - - _initialize(applicationId, javaScriptKey, masterKey) { - CoreManager.set('APPLICATION_ID', applicationId); - CoreManager.set('JAVASCRIPT_KEY', javaScriptKey); - CoreManager.set('MASTER_KEY', masterKey); - CoreManager.set('USE_MASTER_KEY', false); - } -}; - -/** These legacy setters may eventually be deprecated **/ -Object.defineProperty(Parse, 'applicationId', { - get() { - return CoreManager.get('APPLICATION_ID'); - }, - set(value) { - CoreManager.set('APPLICATION_ID', value); - } -}); -Object.defineProperty(Parse, 'javaScriptKey', { - get() { - return CoreManager.get('JAVASCRIPT_KEY'); - }, - set(value) { - CoreManager.set('JAVASCRIPT_KEY', value); - } -}); -Object.defineProperty(Parse, 'masterKey', { - get() { - return CoreManager.get('MASTER_KEY'); - }, - set(value) { - CoreManager.set('MASTER_KEY', value); - } -}); -Object.defineProperty(Parse, 'serverURL', { - get() { - return CoreManager.get('SERVER_URL'); - }, - set(value) { - CoreManager.set('SERVER_URL', value); - } -}); -Object.defineProperty(Parse, 'liveQueryServerURL', { - get() { - return CoreManager.get('LIVEQUERY_SERVER_URL'); - }, - set(value) { - CoreManager.set('LIVEQUERY_SERVER_URL', value); - } -}); -/** End setters **/ - -Parse.ACL = require('./ParseACL').default; -Parse.Analytics = require('./Analytics'); -Parse.Cloud = require('./Cloud'); -Parse.CoreManager = require('./CoreManager'); -Parse.Config = require('./ParseConfig').default; -Parse.Error = require('./ParseError').default; -Parse.FacebookUtils = require('./FacebookUtils').default; -Parse.File = require('./ParseFile').default; -Parse.GeoPoint = require('./ParseGeoPoint').default; -Parse.Installation = require('./ParseInstallation').default; -Parse.Object = require('./ParseObject').default; -Parse.Op = { - Set: ParseOp.SetOp, - Unset: ParseOp.UnsetOp, - Increment: ParseOp.IncrementOp, - Add: ParseOp.AddOp, - Remove: ParseOp.RemoveOp, - AddUnique: ParseOp.AddUniqueOp, - Relation: ParseOp.RelationOp -}; -Parse.Promise = require('./ParsePromise').default; -Parse.Push = require('./Push'); -Parse.Query = require('./ParseQuery').default; -Parse.Relation = require('./ParseRelation').default; -Parse.Role = require('./ParseRole').default; -Parse.Session = require('./ParseSession').default; -Parse.Storage = require('./Storage'); -Parse.User = require('./ParseUser').default; -Parse.LiveQuery = require('./ParseLiveQuery').default; -Parse.LiveQueryClient = require('./LiveQueryClient').default; - -Parse._request = function (...args) { - return CoreManager.getRESTController().request.apply(null, args); -}; -Parse._ajax = function (...args) { - return CoreManager.getRESTController().ajax.apply(null, args); -}; -// We attempt to match the signatures of the legacy versions of these methods -Parse._decode = function (_, value) { - return decode(value); -}; -Parse._encode = function (value, _, disallowObjects) { - return encode(value, disallowObjects); -}; -Parse._getInstallationId = function () { - return CoreManager.getInstallationController().currentInstallationId(); -}; - -CoreManager.setInstallationController(InstallationController); -CoreManager.setRESTController(RESTController); - -// For legacy requires, of the form `var Parse = require('parse').Parse` -Parse.Parse = Parse; - -module.exports = Parse; \ No newline at end of file diff --git a/lib/react-native/ParseACL.js b/lib/react-native/ParseACL.js deleted file mode 100644 index 2c28c6fa6..000000000 --- a/lib/react-native/ParseACL.js +++ /dev/null @@ -1,325 +0,0 @@ -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * - */ - -import ParseRole from './ParseRole'; -import ParseUser from './ParseUser'; - -var PUBLIC_KEY = '*'; - -/** - * Creates a new ACL. - * If no argument is given, the ACL has no permissions for anyone. - * If the argument is a Parse.User, the ACL will have read and write - * permission for only that user. - * If the argument is any other JSON object, that object will be interpretted - * as a serialized ACL created with toJSON(). - * @class Parse.ACL - * @constructor - * - *
An ACL, or Access Control List can be added to any
- * Parse.Object
to restrict access to only a subset of users
- * of your application.
Parse.Error
.
- * @param {String} message A detailed description of the error.
- */
-export default class ParseError {
- constructor(code, message) {
- this.code = code;
- this.message = message;
- }
-}
-
-/**
- * Error code indicating some error other than those enumerated here.
- * @property OTHER_CAUSE
- * @static
- * @final
- */
-ParseError.OTHER_CAUSE = -1;
-
-/**
- * Error code indicating that something has gone wrong with the server.
- * If you get this error code, it is Parse's fault. Contact us at
- * https://parse.com/help
- * @property INTERNAL_SERVER_ERROR
- * @static
- * @final
- */
-ParseError.INTERNAL_SERVER_ERROR = 1;
-
-/**
- * Error code indicating the connection to the Parse servers failed.
- * @property CONNECTION_FAILED
- * @static
- * @final
- */
-ParseError.CONNECTION_FAILED = 100;
-
-/**
- * Error code indicating the specified object doesn't exist.
- * @property OBJECT_NOT_FOUND
- * @static
- * @final
- */
-ParseError.OBJECT_NOT_FOUND = 101;
-
-/**
- * Error code indicating you tried to query with a datatype that doesn't
- * support it, like exact matching an array or object.
- * @property INVALID_QUERY
- * @static
- * @final
- */
-ParseError.INVALID_QUERY = 102;
-
-/**
- * Error code indicating a missing or invalid classname. Classnames are
- * case-sensitive. They must start with a letter, and a-zA-Z0-9_ are the
- * only valid characters.
- * @property INVALID_CLASS_NAME
- * @static
- * @final
- */
-ParseError.INVALID_CLASS_NAME = 103;
-
-/**
- * Error code indicating an unspecified object id.
- * @property MISSING_OBJECT_ID
- * @static
- * @final
- */
-ParseError.MISSING_OBJECT_ID = 104;
-
-/**
- * Error code indicating an invalid key name. Keys are case-sensitive. They
- * must start with a letter, and a-zA-Z0-9_ are the only valid characters.
- * @property INVALID_KEY_NAME
- * @static
- * @final
- */
-ParseError.INVALID_KEY_NAME = 105;
-
-/**
- * Error code indicating a malformed pointer. You should not see this unless
- * you have been mucking about changing internal Parse code.
- * @property INVALID_POINTER
- * @static
- * @final
- */
-ParseError.INVALID_POINTER = 106;
-
-/**
- * Error code indicating that badly formed JSON was received upstream. This
- * either indicates you have done something unusual with modifying how
- * things encode to JSON, or the network is failing badly.
- * @property INVALID_JSON
- * @static
- * @final
- */
-ParseError.INVALID_JSON = 107;
-
-/**
- * Error code indicating that the feature you tried to access is only
- * available internally for testing purposes.
- * @property COMMAND_UNAVAILABLE
- * @static
- * @final
- */
-ParseError.COMMAND_UNAVAILABLE = 108;
-
-/**
- * You must call Parse.initialize before using the Parse library.
- * @property NOT_INITIALIZED
- * @static
- * @final
- */
-ParseError.NOT_INITIALIZED = 109;
-
-/**
- * Error code indicating that a field was set to an inconsistent type.
- * @property INCORRECT_TYPE
- * @static
- * @final
- */
-ParseError.INCORRECT_TYPE = 111;
-
-/**
- * Error code indicating an invalid channel name. A channel name is either
- * an empty string (the broadcast channel) or contains only a-zA-Z0-9_
- * characters and starts with a letter.
- * @property INVALID_CHANNEL_NAME
- * @static
- * @final
- */
-ParseError.INVALID_CHANNEL_NAME = 112;
-
-/**
- * Error code indicating that push is misconfigured.
- * @property PUSH_MISCONFIGURED
- * @static
- * @final
- */
-ParseError.PUSH_MISCONFIGURED = 115;
-
-/**
- * Error code indicating that the object is too large.
- * @property OBJECT_TOO_LARGE
- * @static
- * @final
- */
-ParseError.OBJECT_TOO_LARGE = 116;
-
-/**
- * Error code indicating that the operation isn't allowed for clients.
- * @property OPERATION_FORBIDDEN
- * @static
- * @final
- */
-ParseError.OPERATION_FORBIDDEN = 119;
-
-/**
- * Error code indicating the result was not found in the cache.
- * @property CACHE_MISS
- * @static
- * @final
- */
-ParseError.CACHE_MISS = 120;
-
-/**
- * Error code indicating that an invalid key was used in a nested
- * JSONObject.
- * @property INVALID_NESTED_KEY
- * @static
- * @final
- */
-ParseError.INVALID_NESTED_KEY = 121;
-
-/**
- * Error code indicating that an invalid filename was used for ParseFile.
- * A valid file name contains only a-zA-Z0-9_. characters and is between 1
- * and 128 characters.
- * @property INVALID_FILE_NAME
- * @static
- * @final
- */
-ParseError.INVALID_FILE_NAME = 122;
-
-/**
- * Error code indicating an invalid ACL was provided.
- * @property INVALID_ACL
- * @static
- * @final
- */
-ParseError.INVALID_ACL = 123;
-
-/**
- * Error code indicating that the request timed out on the server. Typically
- * this indicates that the request is too expensive to run.
- * @property TIMEOUT
- * @static
- * @final
- */
-ParseError.TIMEOUT = 124;
-
-/**
- * Error code indicating that the email address was invalid.
- * @property INVALID_EMAIL_ADDRESS
- * @static
- * @final
- */
-ParseError.INVALID_EMAIL_ADDRESS = 125;
-
-/**
- * Error code indicating a missing content type.
- * @property MISSING_CONTENT_TYPE
- * @static
- * @final
- */
-ParseError.MISSING_CONTENT_TYPE = 126;
-
-/**
- * Error code indicating a missing content length.
- * @property MISSING_CONTENT_LENGTH
- * @static
- * @final
- */
-ParseError.MISSING_CONTENT_LENGTH = 127;
-
-/**
- * Error code indicating an invalid content length.
- * @property INVALID_CONTENT_LENGTH
- * @static
- * @final
- */
-ParseError.INVALID_CONTENT_LENGTH = 128;
-
-/**
- * Error code indicating a file that was too large.
- * @property FILE_TOO_LARGE
- * @static
- * @final
- */
-ParseError.FILE_TOO_LARGE = 129;
-
-/**
- * Error code indicating an error saving a file.
- * @property FILE_SAVE_ERROR
- * @static
- * @final
- */
-ParseError.FILE_SAVE_ERROR = 130;
-
-/**
- * Error code indicating that a unique field was given a value that is
- * already taken.
- * @property DUPLICATE_VALUE
- * @static
- * @final
- */
-ParseError.DUPLICATE_VALUE = 137;
-
-/**
- * Error code indicating that a role's name is invalid.
- * @property INVALID_ROLE_NAME
- * @static
- * @final
- */
-ParseError.INVALID_ROLE_NAME = 139;
-
-/**
- * Error code indicating that an application quota was exceeded. Upgrade to
- * resolve.
- * @property EXCEEDED_QUOTA
- * @static
- * @final
- */
-ParseError.EXCEEDED_QUOTA = 140;
-
-/**
- * Error code indicating that a Cloud Code script failed.
- * @property SCRIPT_FAILED
- * @static
- * @final
- */
-ParseError.SCRIPT_FAILED = 141;
-
-/**
- * Error code indicating that a Cloud Code validation failed.
- * @property VALIDATION_ERROR
- * @static
- * @final
- */
-ParseError.VALIDATION_ERROR = 142;
-
-/**
- * Error code indicating that invalid image data was provided.
- * @property INVALID_IMAGE_DATA
- * @static
- * @final
- */
-ParseError.INVALID_IMAGE_DATA = 143;
-
-/**
- * Error code indicating an unsaved file.
- * @property UNSAVED_FILE_ERROR
- * @static
- * @final
- */
-ParseError.UNSAVED_FILE_ERROR = 151;
-
-/**
- * Error code indicating an invalid push time.
- * @property INVALID_PUSH_TIME_ERROR
- * @static
- * @final
- */
-ParseError.INVALID_PUSH_TIME_ERROR = 152;
-
-/**
- * Error code indicating an error deleting a file.
- * @property FILE_DELETE_ERROR
- * @static
- * @final
- */
-ParseError.FILE_DELETE_ERROR = 153;
-
-/**
- * Error code indicating that the application has exceeded its request
- * limit.
- * @property REQUEST_LIMIT_EXCEEDED
- * @static
- * @final
- */
-ParseError.REQUEST_LIMIT_EXCEEDED = 155;
-
-/**
- * Error code indicating an invalid event name.
- * @property INVALID_EVENT_NAME
- * @static
- * @final
- */
-ParseError.INVALID_EVENT_NAME = 160;
-
-/**
- * Error code indicating that the username is missing or empty.
- * @property USERNAME_MISSING
- * @static
- * @final
- */
-ParseError.USERNAME_MISSING = 200;
-
-/**
- * Error code indicating that the password is missing or empty.
- * @property PASSWORD_MISSING
- * @static
- * @final
- */
-ParseError.PASSWORD_MISSING = 201;
-
-/**
- * Error code indicating that the username has already been taken.
- * @property USERNAME_TAKEN
- * @static
- * @final
- */
-ParseError.USERNAME_TAKEN = 202;
-
-/**
- * Error code indicating that the email has already been taken.
- * @property EMAIL_TAKEN
- * @static
- * @final
- */
-ParseError.EMAIL_TAKEN = 203;
-
-/**
- * Error code indicating that the email is missing, but must be specified.
- * @property EMAIL_MISSING
- * @static
- * @final
- */
-ParseError.EMAIL_MISSING = 204;
-
-/**
- * Error code indicating that a user with the specified email was not found.
- * @property EMAIL_NOT_FOUND
- * @static
- * @final
- */
-ParseError.EMAIL_NOT_FOUND = 205;
-
-/**
- * Error code indicating that a user object without a valid session could
- * not be altered.
- * @property SESSION_MISSING
- * @static
- * @final
- */
-ParseError.SESSION_MISSING = 206;
-
-/**
- * Error code indicating that a user can only be created through signup.
- * @property MUST_CREATE_USER_THROUGH_SIGNUP
- * @static
- * @final
- */
-ParseError.MUST_CREATE_USER_THROUGH_SIGNUP = 207;
-
-/**
- * Error code indicating that an an account being linked is already linked
- * to another user.
- * @property ACCOUNT_ALREADY_LINKED
- * @static
- * @final
- */
-ParseError.ACCOUNT_ALREADY_LINKED = 208;
-
-/**
- * Error code indicating that the current session token is invalid.
- * @property INVALID_SESSION_TOKEN
- * @static
- * @final
- */
-ParseError.INVALID_SESSION_TOKEN = 209;
-
-/**
- * Error code indicating that a user cannot be linked to an account because
- * that account's id could not be found.
- * @property LINKED_ID_MISSING
- * @static
- * @final
- */
-ParseError.LINKED_ID_MISSING = 250;
-
-/**
- * Error code indicating that a user with a linked (e.g. Facebook) account
- * has an invalid session.
- * @property INVALID_LINKED_SESSION
- * @static
- * @final
- */
-ParseError.INVALID_LINKED_SESSION = 251;
-
-/**
- * Error code indicating that a service being linked (e.g. Facebook or
- * Twitter) is unsupported.
- * @property UNSUPPORTED_SERVICE
- * @static
- * @final
- */
-ParseError.UNSUPPORTED_SERVICE = 252;
-
-/**
- * Error code indicating that there were multiple errors. Aggregate errors
- * have an "errors" property, which is an array of error objects with more
- * detail about each error that occurred.
- * @property AGGREGATE_ERROR
- * @static
- * @final
- */
-ParseError.AGGREGATE_ERROR = 600;
-
-/**
- * Error code indicating the client was unable to read an input file.
- * @property FILE_READ_ERROR
- * @static
- * @final
- */
-ParseError.FILE_READ_ERROR = 601;
-
-/**
- * Error code indicating a real error code is unavailable because
- * we had to use an XDomainRequest object to allow CORS requests in
- * Internet Explorer, which strips the body from HTTP responses that have
- * a non-2XX status code.
- * @property X_DOMAIN_REQUEST
- * @static
- * @final
- */
-ParseError.X_DOMAIN_REQUEST = 602;
\ No newline at end of file
diff --git a/lib/react-native/ParseFile.js b/lib/react-native/ParseFile.js
deleted file mode 100644
index 0b1b448a8..000000000
--- a/lib/react-native/ParseFile.js
+++ /dev/null
@@ -1,247 +0,0 @@
-/**
- * Copyright (c) 2015-present, Parse, LLC.
- * All rights reserved.
- *
- * This source code is licensed under the BSD-style license found in the
- * LICENSE file in the root directory of this source tree. An additional grant
- * of patent rights can be found in the PATENTS file in the same directory.
- *
- *
- */
-
-import CoreManager from './CoreManager';
-import ParsePromise from './ParsePromise';
-
-var dataUriRegexp = /^data:([a-zA-Z]*\/[a-zA-Z+.-]*);(charset=[a-zA-Z0-9\-\/\s]*,)?base64,/;
-
-function b64Digit(number) {
- if (number < 26) {
- return String.fromCharCode(65 + number);
- }
- if (number < 52) {
- return String.fromCharCode(97 + (number - 26));
- }
- if (number < 62) {
- return String.fromCharCode(48 + (number - 52));
- }
- if (number === 62) {
- return '+';
- }
- if (number === 63) {
- return '/';
- }
- throw new TypeError('Tried to encode large digit ' + number + ' in base64.');
-}
-
-/**
- * A Parse.File is a local representation of a file that is saved to the Parse
- * cloud.
- * @class Parse.File
- * @constructor
- * @param name {String} The file's name. This will be prefixed by a unique
- * value once the file has finished saving. The file name must begin with
- * an alphanumeric character, and consist of alphanumeric characters,
- * periods, spaces, underscores, or dashes.
- * @param data {Array} The data for the file, as either:
- * 1. an Array of byte value Numbers, or
- * 2. an Object like { base64: "..." } with a base64-encoded String.
- * 3. a File object selected with a file upload control. (3) only works
- * in Firefox 3.6+, Safari 6.0.2+, Chrome 7+, and IE 10+.
- * For example:- * var fileUploadControl = $("#profilePhotoFileUpload")[0]; - * if (fileUploadControl.files.length > 0) { - * var file = fileUploadControl.files[0]; - * var name = "photo.jpg"; - * var parseFile = new Parse.File(name, file); - * parseFile.save().then(function() { - * // The file has been saved to Parse. - * }, function(error) { - * // The file either could not be read, or could not be saved to Parse. - * }); - * }- * @param type {String} Optional Content-Type header to use for the file. If - * this is omitted, the content type will be inferred from the name's - * extension. - */ -export default class ParseFile { - - constructor(name, data, type) { - var specifiedType = type || ''; - - this._name = name; - - if (data !== undefined) { - if (Array.isArray(data)) { - this._source = { - format: 'base64', - base64: ParseFile.encodeBase64(data), - type: specifiedType - }; - } else if (typeof File !== 'undefined' && data instanceof File) { - this._source = { - format: 'file', - file: data, - type: specifiedType - }; - } else if (data && typeof data.base64 === 'string') { - const base64 = data.base64; - var commaIndex = base64.indexOf(','); - - if (commaIndex !== -1) { - var matches = dataUriRegexp.exec(base64.slice(0, commaIndex + 1)); - // if data URI with type and charset, there will be 4 matches. - this._source = { - format: 'base64', - base64: base64.slice(commaIndex + 1), - type: matches[1] - }; - } else { - this._source = { - format: 'base64', - base64: base64, - type: specifiedType - }; - } - } else { - throw new TypeError('Cannot create a Parse.File with that data.'); - } - } - } - - /** - * Gets the name of the file. Before save is called, this is the filename - * given by the user. After save is called, that name gets prefixed with a - * unique identifier. - * @method name - * @return {String} - */ - name() { - return this._name; - } - - /** - * Gets the url of the file. It is only available after you save the file or - * after you get the file from a Parse.Object. - * @method url - * @param {Object} options An object to specify url options - * @return {String} - */ - url(options) { - options = options || {}; - if (!this._url) { - return; - } - if (options.forceSecure) { - return this._url.replace(/^http:\/\//i, 'https://'); - } else { - return this._url; - } - } - - /** - * Saves the file to the Parse cloud. - * @method save - * @param {Object} options A Backbone-style options object. - * @return {Parse.Promise} Promise that is resolved when the save finishes. - */ - save(options) { - options = options || {}; - var controller = CoreManager.getFileController(); - if (!this._previousSave) { - if (this._source.format === 'file') { - this._previousSave = controller.saveFile(this._name, this._source).then(res => { - this._name = res.name; - this._url = res.url; - return this; - }); - } else { - this._previousSave = controller.saveBase64(this._name, this._source).then(res => { - this._name = res.name; - this._url = res.url; - return this; - }); - } - } - if (this._previousSave) { - return this._previousSave._thenRunCallbacks(options); - } - } - - toJSON() { - return { - __type: 'File', - name: this._name, - url: this._url - }; - } - - equals(other) { - if (this === other) { - return true; - } - // Unsaved Files are never equal, since they will be saved to different URLs - return other instanceof ParseFile && this.name() === other.name() && this.url() === other.url() && typeof this.url() !== 'undefined'; - } - - static fromJSON(obj) { - if (obj.__type !== 'File') { - throw new TypeError('JSON object does not represent a ParseFile'); - } - var file = new ParseFile(obj.name); - file._url = obj.url; - return file; - } - - static encodeBase64(bytes) { - var chunks = []; - chunks.length = Math.ceil(bytes.length / 3); - for (var i = 0; i < chunks.length; i++) { - var b1 = bytes[i * 3]; - var b2 = bytes[i * 3 + 1] || 0; - var b3 = bytes[i * 3 + 2] || 0; - - var has2 = i * 3 + 1 < bytes.length; - var has3 = i * 3 + 2 < bytes.length; - - chunks[i] = [b64Digit(b1 >> 2 & 0x3F), b64Digit(b1 << 4 & 0x30 | b2 >> 4 & 0x0F), has2 ? b64Digit(b2 << 2 & 0x3C | b3 >> 6 & 0x03) : '=', has3 ? b64Digit(b3 & 0x3F) : '='].join(''); - } - - return chunks.join(''); - } -} - -var DefaultController = { - saveFile: function (name, source) { - if (source.format !== 'file') { - throw new Error('saveFile can only be used with File-type sources.'); - } - // To directly upload a File, we use a REST-style AJAX request - var headers = { - 'X-Parse-Application-ID': CoreManager.get('APPLICATION_ID'), - 'X-Parse-JavaScript-Key': CoreManager.get('JAVASCRIPT_KEY'), - 'Content-Type': source.type || (source.file ? source.file.type : null) - }; - var url = CoreManager.get('SERVER_URL'); - if (url[url.length - 1] !== '/') { - url += '/'; - } - url += 'files/' + name; - return CoreManager.getRESTController().ajax('POST', url, source.file, headers); - }, - - saveBase64: function (name, source) { - if (source.format !== 'base64') { - throw new Error('saveBase64 can only be used with Base64-type sources.'); - } - var data = { - base64: source.base64 - }; - if (source.type) { - data._ContentType = source.type; - } - - return CoreManager.getRESTController().request('POST', 'files/' + name, data); - } -}; - -CoreManager.setFileController(DefaultController); \ No newline at end of file diff --git a/lib/react-native/ParseGeoPoint.js b/lib/react-native/ParseGeoPoint.js deleted file mode 100644 index e6dcddf43..000000000 --- a/lib/react-native/ParseGeoPoint.js +++ /dev/null @@ -1,186 +0,0 @@ -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * - */ - -import ParsePromise from './ParsePromise'; - -/** - * Creates a new GeoPoint with any of the following forms:
- * new GeoPoint(otherGeoPoint) - * new GeoPoint(30, 30) - * new GeoPoint([30, 30]) - * new GeoPoint({latitude: 30, longitude: 30}) - * new GeoPoint() // defaults to (0, 0) - *- * @class Parse.GeoPoint - * @constructor - * - *
Represents a latitude / longitude point that may be associated - * with a key in a ParseObject or used as a reference point for geo queries. - * This allows proximity-based queries on the key.
- * - *Only one key in a class may contain a GeoPoint.
- * - *Example:
- * var point = new Parse.GeoPoint(30.0, -20.0); - * var object = new Parse.Object("PlaceObject"); - * object.set("location", point); - * object.save();- */ -export default class ParseGeoPoint { - - constructor(arg1, arg2) { - if (Array.isArray(arg1)) { - ParseGeoPoint._validate(arg1[0], arg1[1]); - this._latitude = arg1[0]; - this._longitude = arg1[1]; - } else if (typeof arg1 === 'object') { - ParseGeoPoint._validate(arg1.latitude, arg1.longitude); - this._latitude = arg1.latitude; - this._longitude = arg1.longitude; - } else if (typeof arg1 === 'number' && typeof arg2 === 'number') { - ParseGeoPoint._validate(arg1, arg2); - this._latitude = arg1; - this._longitude = arg2; - } else { - this._latitude = 0; - this._longitude = 0; - } - } - - /** - * North-south portion of the coordinate, in range [-90, 90]. - * Throws an exception if set out of range in a modern browser. - * @property latitude - * @type Number - */ - get latitude() { - return this._latitude; - } - - set latitude(val) { - ParseGeoPoint._validate(val, this.longitude); - this._latitude = val; - } - - /** - * East-west portion of the coordinate, in range [-180, 180]. - * Throws if set out of range in a modern browser. - * @property longitude - * @type Number - */ - get longitude() { - return this._longitude; - } - - set longitude(val) { - ParseGeoPoint._validate(this.latitude, val); - this._longitude = val; - } - - /** - * Returns a JSON representation of the GeoPoint, suitable for Parse. - * @method toJSON - * @return {Object} - */ - toJSON() { - ParseGeoPoint._validate(this._latitude, this._longitude); - return { - __type: 'GeoPoint', - latitude: this._latitude, - longitude: this._longitude - }; - } - - equals(other) { - return other instanceof ParseGeoPoint && this.latitude === other.latitude && this.longitude === other.longitude; - } - - /** - * Returns the distance from this GeoPoint to another in radians. - * @method radiansTo - * @param {Parse.GeoPoint} point the other Parse.GeoPoint. - * @return {Number} - */ - radiansTo(point) { - var d2r = Math.PI / 180.0; - var lat1rad = this.latitude * d2r; - var long1rad = this.longitude * d2r; - var lat2rad = point.latitude * d2r; - var long2rad = point.longitude * d2r; - - var sinDeltaLatDiv2 = Math.sin((lat1rad - lat2rad) / 2); - var sinDeltaLongDiv2 = Math.sin((long1rad - long2rad) / 2); - // Square of half the straight line chord distance between both points. - var a = sinDeltaLatDiv2 * sinDeltaLatDiv2 + Math.cos(lat1rad) * Math.cos(lat2rad) * sinDeltaLongDiv2 * sinDeltaLongDiv2; - a = Math.min(1.0, a); - return 2 * Math.asin(Math.sqrt(a)); - } - - /** - * Returns the distance from this GeoPoint to another in kilometers. - * @method kilometersTo - * @param {Parse.GeoPoint} point the other Parse.GeoPoint. - * @return {Number} - */ - kilometersTo(point) { - return this.radiansTo(point) * 6371.0; - } - - /** - * Returns the distance from this GeoPoint to another in miles. - * @method milesTo - * @param {Parse.GeoPoint} point the other Parse.GeoPoint. - * @return {Number} - */ - milesTo(point) { - return this.radiansTo(point) * 3958.8; - } - - /** - * Throws an exception if the given lat-long is out of bounds. - */ - static _validate(latitude, longitude) { - if (latitude !== latitude || longitude !== longitude) { - throw new TypeError('GeoPoint latitude and longitude must be valid numbers'); - } - if (latitude < -90.0) { - throw new TypeError('GeoPoint latitude out of bounds: ' + latitude + ' < -90.0.'); - } - if (latitude > 90.0) { - throw new TypeError('GeoPoint latitude out of bounds: ' + latitude + ' > 90.0.'); - } - if (longitude < -180.0) { - throw new TypeError('GeoPoint longitude out of bounds: ' + longitude + ' < -180.0.'); - } - if (longitude > 180.0) { - throw new TypeError('GeoPoint longitude out of bounds: ' + longitude + ' > 180.0.'); - } - } - - /** - * Creates a GeoPoint with the user's current location, if available. - * Calls options.success with a new GeoPoint instance or calls options.error. - * @method current - * @param {Object} options An object with success and error callbacks. - * @static - */ - static current(options) { - var promise = new ParsePromise(); - navigator.geolocation.getCurrentPosition(function (location) { - promise.resolve(new ParseGeoPoint(location.coords.latitude, location.coords.longitude)); - }, function (error) { - promise.reject(error); - }); - - return promise._thenRunCallbacks(options); - } -} \ No newline at end of file diff --git a/lib/react-native/ParseHooks.js b/lib/react-native/ParseHooks.js deleted file mode 100644 index e079fcd51..000000000 --- a/lib/react-native/ParseHooks.js +++ /dev/null @@ -1,125 +0,0 @@ -import CoreManager from './CoreManager'; -import decode from './decode'; -import encode from './encode'; -import ParseError from './ParseError'; -import ParsePromise from './ParsePromise'; - -export function getFunctions() { - return CoreManager.getHooksController().get("functions"); -} - -export function getTriggers() { - return CoreManager.getHooksController().get("triggers"); -} - -export function getFunction(name) { - return CoreManager.getHooksController().get("functions", name); -} - -export function getTrigger(className, triggerName) { - return CoreManager.getHooksController().get("triggers", className, triggerName); -} - -export function createFunction(functionName, url) { - return create({ functionName: functionName, url: url }); -} - -export function createTrigger(className, triggerName, url) { - return create({ className: className, triggerName: triggerName, url: url }); -} - -export function create(hook) { - return CoreManager.getHooksController().create(hook); -} - -export function updateFunction(functionName, url) { - return update({ functionName: functionName, url: url }); -} - -export function updateTrigger(className, triggerName, url) { - return update({ className: className, triggerName: triggerName, url: url }); -} - -export function update(hook) { - return CoreManager.getHooksController().update(hook); -} - -export function removeFunction(functionName) { - return remove({ functionName: functionName }); -} - -export function removeTrigger(className, triggerName) { - return remove({ className: className, triggerName: triggerName }); -} - -export function remove(hook) { - return CoreManager.getHooksController().remove(hook); -} - -var DefaultController = { - - get(type, functionName, triggerName) { - var url = "/hooks/" + type; - if (functionName) { - url += "/" + functionName; - if (triggerName) { - url += "/" + triggerName; - } - } - return this.sendRequest("GET", url); - }, - - create(hook) { - var url; - if (hook.functionName && hook.url) { - url = "/hooks/functions"; - } else if (hook.className && hook.triggerName && hook.url) { - url = "/hooks/triggers"; - } else { - return Promise.reject({ error: 'invalid hook declaration', code: 143 }); - } - return this.sendRequest("POST", url, hook); - }, - - remove(hook) { - var url; - if (hook.functionName) { - url = "/hooks/functions/" + hook.functionName; - delete hook.functionName; - } else if (hook.className && hook.triggerName) { - url = "/hooks/triggers/" + hook.className + "/" + hook.triggerName; - delete hook.className; - delete hook.triggerName; - } else { - return Promise.reject({ error: 'invalid hook declaration', code: 143 }); - } - return this.sendRequest("PUT", url, { "__op": "Delete" }); - }, - - update(hook) { - var url; - if (hook.functionName && hook.url) { - url = "/hooks/functions/" + hook.functionName; - delete hook.functionName; - } else if (hook.className && hook.triggerName && hook.url) { - url = "/hooks/triggers/" + hook.className + "/" + hook.triggerName; - delete hook.className; - delete hook.triggerName; - } else { - return Promise.reject({ error: 'invalid hook declaration', code: 143 }); - } - return this.sendRequest('PUT', url, hook); - }, - - sendRequest(method, url, body) { - return CoreManager.getRESTController().request(method, url, body, { useMasterKey: true }).then(res => { - var decoded = decode(res); - if (decoded) { - return ParsePromise.as(decoded); - } - return ParsePromise.error(new ParseError(ParseError.INVALID_JSON, 'The server returned an invalid response.')); - }); - } -}; - -CoreManager.setHooksController(DefaultController); \ No newline at end of file diff --git a/lib/react-native/ParseInstallation.js b/lib/react-native/ParseInstallation.js deleted file mode 100644 index fd29a3f91..000000000 --- a/lib/react-native/ParseInstallation.js +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * - */ - -import ParseObject from './ParseObject'; - -export default class Installation extends ParseObject { - constructor(attributes) { - super('_Installation'); - if (attributes && typeof attributes === 'object') { - if (!this.set(attributes || {})) { - throw new Error('Can\'t create an invalid Session'); - } - } - } -} - -ParseObject.registerSubclass('_Installation', Installation); \ No newline at end of file diff --git a/lib/react-native/ParseLiveQuery.js b/lib/react-native/ParseLiveQuery.js deleted file mode 100644 index 3ad072a72..000000000 --- a/lib/react-native/ParseLiveQuery.js +++ /dev/null @@ -1,212 +0,0 @@ -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * - */ - -import EventEmitter from './EventEmitter'; -import LiveQueryClient from './LiveQueryClient'; -import CoreManager from './CoreManager'; -import ParsePromise from './ParsePromise'; - -function open() { - const LiveQueryController = CoreManager.getLiveQueryController(); - LiveQueryController.open(); -} - -function close() { - const LiveQueryController = CoreManager.getLiveQueryController(); - LiveQueryController.close(); -} - -/** - * - * We expose three events to help you monitor the status of the WebSocket connection: - * - *
Open - When we establish the WebSocket connection to the LiveQuery server, you'll get this event. - * - *
- * Parse.LiveQuery.on('open', () => { - * - * });- * - *
Close - When we lose the WebSocket connection to the LiveQuery server, you'll get this event. - * - *
- * Parse.LiveQuery.on('close', () => { - * - * });- * - *
Error - When some network error or LiveQuery server error happens, you'll get this event. - * - *
- * Parse.LiveQuery.on('error', (error) => { - * - * });- * - * @class Parse.LiveQuery - * @static - * - */ -let LiveQuery = new EventEmitter(); - -/** - * After open is called, the LiveQuery will try to send a connect request - * to the LiveQuery server. - * - * @method open - */ -LiveQuery.open = open; - -/** - * When you're done using LiveQuery, you can call Parse.LiveQuery.close(). - * This function will close the WebSocket connection to the LiveQuery server, - * cancel the auto reconnect, and unsubscribe all subscriptions based on it. - * If you call query.subscribe() after this, we'll create a new WebSocket - * connection to the LiveQuery server. - * - * @method close - */ - -LiveQuery.close = close; -// Register a default onError callback to make sure we do not crash on error -LiveQuery.on('error', () => {}); - -export default LiveQuery; - -function getSessionToken() { - const controller = CoreManager.getUserController(); - return controller.currentUserAsync().then(currentUser => { - return currentUser ? currentUser.getSessionToken() : undefined; - }); -} - -function getLiveQueryClient() { - return CoreManager.getLiveQueryController().getDefaultLiveQueryClient(); -} - -let defaultLiveQueryClient; -const DefaultLiveQueryController = { - setDefaultLiveQueryClient(liveQueryClient) { - defaultLiveQueryClient = liveQueryClient; - }, - getDefaultLiveQueryClient() { - if (defaultLiveQueryClient) { - return ParsePromise.as(defaultLiveQueryClient); - } - - return getSessionToken().then(sessionToken => { - let liveQueryServerURL = CoreManager.get('LIVEQUERY_SERVER_URL'); - - if (liveQueryServerURL && liveQueryServerURL.indexOf('ws') !== 0) { - throw new Error('You need to set a proper Parse LiveQuery server url before using LiveQueryClient'); - } - - // If we can not find Parse.liveQueryServerURL, we try to extract it from Parse.serverURL - if (!liveQueryServerURL) { - const tempServerURL = CoreManager.get('SERVER_URL'); - let protocol = 'ws://'; - // If Parse is being served over SSL/HTTPS, ensure LiveQuery Server uses 'wss://' prefix - if (tempServerURL.indexOf('https') === 0) { - protocol = 'wss://'; - } - const host = tempServerURL.replace(/^https?:\/\//, ''); - liveQueryServerURL = protocol + host; - CoreManager.set('LIVEQUERY_SERVER_URL', liveQueryServerURL); - } - - const applicationId = CoreManager.get('APPLICATION_ID'); - const javascriptKey = CoreManager.get('JAVASCRIPT_KEY'); - const masterKey = CoreManager.get('MASTER_KEY'); - // Get currentUser sessionToken if possible - defaultLiveQueryClient = new LiveQueryClient({ - applicationId, - serverURL: liveQueryServerURL, - javascriptKey, - masterKey, - sessionToken - }); - // Register a default onError callback to make sure we do not crash on error - // Cannot create these events on a nested way because of EventEmiiter from React Native - defaultLiveQueryClient.on('error', error => { - LiveQuery.emit('error', error); - }); - defaultLiveQueryClient.on('open', () => { - LiveQuery.emit('open'); - }); - defaultLiveQueryClient.on('close', () => { - LiveQuery.emit('close'); - }); - - return defaultLiveQueryClient; - }); - }, - open() { - getLiveQueryClient().then(liveQueryClient => { - this.resolve(liveQueryClient.open()); - }); - }, - close() { - getLiveQueryClient().then(liveQueryClient => { - this.resolve(liveQueryClient.close()); - }); - }, - subscribe(query) { - let subscriptionWrap = new EventEmitter(); - - getLiveQueryClient().then(liveQueryClient => { - if (liveQueryClient.shouldOpen()) { - liveQueryClient.open(); - } - let promiseSessionToken = getSessionToken(); - // new event emitter - return promiseSessionToken.then(sessionToken => { - - let subscription = liveQueryClient.subscribe(query, sessionToken); - // enter, leave create, etc - - subscriptionWrap.id = subscription.id; - subscriptionWrap.query = subscription.query; - subscriptionWrap.sessionToken = subscription.sessionToken; - subscriptionWrap.unsubscribe = subscription.unsubscribe; - // Cannot create these events on a nested way because of EventEmiiter from React Native - subscription.on('open', () => { - subscriptionWrap.emit('open'); - }); - subscription.on('create', object => { - subscriptionWrap.emit('create', object); - }); - subscription.on('update', object => { - subscriptionWrap.emit('update', object); - }); - subscription.on('enter', object => { - subscriptionWrap.emit('enter', object); - }); - subscription.on('leave', object => { - subscriptionWrap.emit('leave', object); - }); - subscription.on('delete', object => { - subscriptionWrap.emit('delete', object); - }); - - this.resolve(); - }); - }); - return subscriptionWrap; - }, - unsubscribe(subscription) { - getLiveQueryClient().then(liveQueryClient => { - this.resolve(liveQueryClient.unsubscribe(subscription)); - }); - }, - _clearCachedDefaultClient() { - defaultLiveQueryClient = null; - } -}; - -CoreManager.setLiveQueryController(DefaultLiveQueryController); \ No newline at end of file diff --git a/lib/react-native/ParseObject.js b/lib/react-native/ParseObject.js deleted file mode 100644 index e92c14e18..000000000 --- a/lib/react-native/ParseObject.js +++ /dev/null @@ -1,1742 +0,0 @@ -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * - */ - -import CoreManager from './CoreManager'; -import canBeSerialized from './canBeSerialized'; -import decode from './decode'; -import encode from './encode'; -import equals from './equals'; -import escape from './escape'; -import ParseACL from './ParseACL'; -import parseDate from './parseDate'; -import ParseError from './ParseError'; -import ParseFile from './ParseFile'; -import { opFromJSON, Op, SetOp, UnsetOp, IncrementOp, AddOp, AddUniqueOp, RemoveOp, RelationOp } from './ParseOp'; -import ParsePromise from './ParsePromise'; -import ParseQuery from './ParseQuery'; -import ParseRelation from './ParseRelation'; -import * as SingleInstanceStateController from './SingleInstanceStateController'; -import unique from './unique'; -import * as UniqueInstanceStateController from './UniqueInstanceStateController'; -import unsavedChildren from './unsavedChildren'; - -// Mapping of class names to constructors, so we can populate objects from the -// server with appropriate subclasses of ParseObject -var classMap = {}; - -// Global counter for generating unique local Ids -var localCount = 0; -// Global counter for generating unique Ids for non-single-instance objects -var objectCount = 0; -// On web clients, objects are single-instance: any two objects with the same Id -// will have the same attributes. However, this may be dangerous default -// behavior in a server scenario -var singleInstance = !CoreManager.get('IS_NODE'); -if (singleInstance) { - CoreManager.setObjectStateController(SingleInstanceStateController); -} else { - CoreManager.setObjectStateController(UniqueInstanceStateController); -} - -function getServerUrlPath() { - var serverUrl = CoreManager.get('SERVER_URL'); - if (serverUrl[serverUrl.length - 1] !== '/') { - serverUrl += '/'; - } - var url = serverUrl.replace(/https?:\/\//, ''); - return url.substr(url.indexOf('/')); -} - -/** - * Creates a new model with defined attributes. - * - *
You won't normally call this method directly. It is recommended that
- * you use a subclass of Parse.Object
instead, created by calling
- * extend
.
However, if you don't want to use a subclass, or aren't sure which - * subclass is appropriate, you can use this form:
- * var object = new Parse.Object("ClassName"); - *- * That is basically equivalent to:
- * var MyClass = Parse.Object.extend("ClassName"); - * var object = new MyClass(); - *- * - * @class Parse.Object - * @constructor - * @param {String} className The class name for the object - * @param {Object} attributes The initial set of data to store in the object. - * @param {Object} options The options for this object instance. - */ -export default class ParseObject { - /** - * The ID of this object, unique within its class. - * @property id - * @type String - */ - constructor(className, attributes, options) { - // Enable legacy initializers - if (typeof this.initialize === 'function') { - this.initialize.apply(this, arguments); - } - - var toSet = null; - this._objCount = objectCount++; - if (typeof className === 'string') { - this.className = className; - if (attributes && typeof attributes === 'object') { - toSet = attributes; - } - } else if (className && typeof className === 'object') { - this.className = className.className; - toSet = {}; - for (var attr in className) { - if (attr !== 'className') { - toSet[attr] = className[attr]; - } - } - if (attributes && typeof attributes === 'object') { - options = attributes; - } - } - if (toSet && !this.set(toSet, options)) { - throw new Error('Can\'t create an invalid Parse Object'); - } - } - - /** Prototype getters / setters **/ - - get attributes() { - let stateController = CoreManager.getObjectStateController(); - return Object.freeze(stateController.estimateAttributes(this._getStateIdentifier())); - } - - /** - * The first time this object was saved on the server. - * @property createdAt - * @type Date - */ - get createdAt() { - return this._getServerData().createdAt; - } - - /** - * The last time this object was updated on the server. - * @property updatedAt - * @type Date - */ - get updatedAt() { - return this._getServerData().updatedAt; - } - - /** Private methods **/ - - /** - * Returns a local or server Id used uniquely identify this object - */ - _getId() { - if (typeof this.id === 'string') { - return this.id; - } - if (typeof this._localId === 'string') { - return this._localId; - } - var localId = 'local' + String(localCount++); - this._localId = localId; - return localId; - } - - /** - * Returns a unique identifier used to pull data from the State Controller. - */ - _getStateIdentifier() { - if (singleInstance) { - let id = this.id; - if (!id) { - id = this._getId(); - } - return { - id: id, - className: this.className - }; - } else { - return this; - } - } - - _getServerData() { - let stateController = CoreManager.getObjectStateController(); - return stateController.getServerData(this._getStateIdentifier()); - } - - _clearServerData() { - var serverData = this._getServerData(); - var unset = {}; - for (var attr in serverData) { - unset[attr] = undefined; - } - let stateController = CoreManager.getObjectStateController(); - stateController.setServerData(this._getStateIdentifier(), unset); - } - - _getPendingOps() { - let stateController = CoreManager.getObjectStateController(); - return stateController.getPendingOps(this._getStateIdentifier()); - } - - _clearPendingOps() { - var pending = this._getPendingOps(); - var latest = pending[pending.length - 1]; - var keys = Object.keys(latest); - keys.forEach(key => { - delete latest[key]; - }); - } - - _getDirtyObjectAttributes() { - var attributes = this.attributes; - var stateController = CoreManager.getObjectStateController(); - var objectCache = stateController.getObjectCache(this._getStateIdentifier()); - var dirty = {}; - for (var attr in attributes) { - var val = attributes[attr]; - if (val && typeof val === 'object' && !(val instanceof ParseObject) && !(val instanceof ParseFile) && !(val instanceof ParseRelation)) { - // Due to the way browsers construct maps, the key order will not change - // unless the object is changed - try { - var json = encode(val, false, true); - var stringified = JSON.stringify(json); - if (objectCache[attr] !== stringified) { - dirty[attr] = val; - } - } catch (e) { - // Error occurred, possibly by a nested unsaved pointer in a mutable container - // No matter how it happened, it indicates a change in the attribute - dirty[attr] = val; - } - } - } - return dirty; - } - - _toFullJSON(seen) { - var json = this.toJSON(seen); - json.__type = 'Object'; - json.className = this.className; - return json; - } - - _getSaveJSON() { - var pending = this._getPendingOps(); - var dirtyObjects = this._getDirtyObjectAttributes(); - var json = {}; - - for (var attr in dirtyObjects) { - json[attr] = new SetOp(dirtyObjects[attr]).toJSON(); - } - for (attr in pending[0]) { - json[attr] = pending[0][attr].toJSON(); - } - return json; - } - - _getSaveParams() { - var method = this.id ? 'PUT' : 'POST'; - var body = this._getSaveJSON(); - var path = 'classes/' + this.className; - if (this.id) { - path += '/' + this.id; - } else if (this.className === '_User') { - path = 'users'; - } - return { - method, - body, - path - }; - } - - _finishFetch(serverData) { - if (!this.id && serverData.objectId) { - this.id = serverData.objectId; - } - let stateController = CoreManager.getObjectStateController(); - stateController.initializeState(this._getStateIdentifier()); - var decoded = {}; - for (var attr in serverData) { - if (attr === 'ACL') { - decoded[attr] = new ParseACL(serverData[attr]); - } else if (attr !== 'objectId') { - decoded[attr] = decode(serverData[attr]); - if (decoded[attr] instanceof ParseRelation) { - decoded[attr]._ensureParentAndKey(this, attr); - } - } - } - if (decoded.createdAt && typeof decoded.createdAt === 'string') { - decoded.createdAt = parseDate(decoded.createdAt); - } - if (decoded.updatedAt && typeof decoded.updatedAt === 'string') { - decoded.updatedAt = parseDate(decoded.updatedAt); - } - if (!decoded.updatedAt && decoded.createdAt) { - decoded.updatedAt = decoded.createdAt; - } - stateController.commitServerChanges(this._getStateIdentifier(), decoded); - } - - _setExisted(existed) { - let stateController = CoreManager.getObjectStateController(); - let state = stateController.getState(this._getStateIdentifier()); - if (state) { - state.existed = existed; - } - } - - _migrateId(serverId) { - if (this._localId && serverId) { - if (singleInstance) { - let stateController = CoreManager.getObjectStateController(); - let oldState = stateController.removeState(this._getStateIdentifier()); - this.id = serverId; - delete this._localId; - if (oldState) { - stateController.initializeState(this._getStateIdentifier(), oldState); - } - } else { - this.id = serverId; - delete this._localId; - } - } - } - - _handleSaveResponse(response, status) { - var changes = {}; - - var stateController = CoreManager.getObjectStateController(); - var pending = stateController.popPendingState(this._getStateIdentifier()); - for (var attr in pending) { - if (pending[attr] instanceof RelationOp) { - changes[attr] = pending[attr].applyTo(undefined, this, attr); - } else if (!(attr in response)) { - // Only SetOps and UnsetOps should not come back with results - changes[attr] = pending[attr].applyTo(undefined); - } - } - for (attr in response) { - if ((attr === 'createdAt' || attr === 'updatedAt') && typeof response[attr] === 'string') { - changes[attr] = parseDate(response[attr]); - } else if (attr === 'ACL') { - changes[attr] = new ParseACL(response[attr]); - } else if (attr !== 'objectId') { - changes[attr] = decode(response[attr]); - if (changes[attr] instanceof UnsetOp) { - changes[attr] = undefined; - } - } - } - if (changes.createdAt && !changes.updatedAt) { - changes.updatedAt = changes.createdAt; - } - - this._migrateId(response.objectId); - - if (status !== 201) { - this._setExisted(true); - } - - stateController.commitServerChanges(this._getStateIdentifier(), changes); - } - - _handleSaveError() { - this._getPendingOps(); - - let stateController = CoreManager.getObjectStateController(); - stateController.mergeFirstPendingState(this._getStateIdentifier()); - } - - /** Public methods **/ - - initialize() {} - // NOOP - - - /** - * Returns a JSON version of the object suitable for saving to Parse. - * @method toJSON - * @return {Object} - */ - toJSON(seen) { - var seenEntry = this.id ? this.className + ':' + this.id : this; - var seen = seen || [seenEntry]; - var json = {}; - var attrs = this.attributes; - for (var attr in attrs) { - if ((attr === 'createdAt' || attr === 'updatedAt') && attrs[attr].toJSON) { - json[attr] = attrs[attr].toJSON(); - } else { - json[attr] = encode(attrs[attr], false, false, seen); - } - } - var pending = this._getPendingOps(); - for (var attr in pending[0]) { - json[attr] = pending[0][attr].toJSON(); - } - - if (this.id) { - json.objectId = this.id; - } - return json; - } - - /** - * Determines whether this ParseObject is equal to another ParseObject - * @method equals - * @return {Boolean} - */ - equals(other) { - if (this === other) { - return true; - } - return other instanceof ParseObject && this.className === other.className && this.id === other.id && typeof this.id !== 'undefined'; - } - - /** - * Returns true if this object has been modified since its last - * save/refresh. If an attribute is specified, it returns true only if that - * particular attribute has been modified since the last save/refresh. - * @method dirty - * @param {String} attr An attribute name (optional). - * @return {Boolean} - */ - dirty(attr) { - if (!this.id) { - return true; - } - var pendingOps = this._getPendingOps(); - var dirtyObjects = this._getDirtyObjectAttributes(); - if (attr) { - if (dirtyObjects.hasOwnProperty(attr)) { - return true; - } - for (var i = 0; i < pendingOps.length; i++) { - if (pendingOps[i].hasOwnProperty(attr)) { - return true; - } - } - return false; - } - if (Object.keys(pendingOps[0]).length !== 0) { - return true; - } - if (Object.keys(dirtyObjects).length !== 0) { - return true; - } - return false; - } - - /** - * Returns an array of keys that have been modified since last save/refresh - * @method dirtyKeys - * @return {Array of string} - */ - dirtyKeys() { - var pendingOps = this._getPendingOps(); - var keys = {}; - for (var i = 0; i < pendingOps.length; i++) { - for (var attr in pendingOps[i]) { - keys[attr] = true; - } - } - var dirtyObjects = this._getDirtyObjectAttributes(); - for (var attr in dirtyObjects) { - keys[attr] = true; - } - return Object.keys(keys); - } - - /** - * Gets a Pointer referencing this Object. - * @method toPointer - * @return {Object} - */ - toPointer() { - if (!this.id) { - throw new Error('Cannot create a pointer to an unsaved ParseObject'); - } - return { - __type: 'Pointer', - className: this.className, - objectId: this.id - }; - } - - /** - * Gets the value of an attribute. - * @method get - * @param {String} attr The string name of an attribute. - */ - get(attr) { - return this.attributes[attr]; - } - - /** - * Gets a relation on the given class for the attribute. - * @method relation - * @param String attr The attribute to get the relation for. - */ - relation(attr) { - var value = this.get(attr); - if (value) { - if (!(value instanceof ParseRelation)) { - throw new Error('Called relation() on non-relation field ' + attr); - } - value._ensureParentAndKey(this, attr); - return value; - } - return new ParseRelation(this, attr); - } - - /** - * Gets the HTML-escaped value of an attribute. - * @method escape - * @param {String} attr The string name of an attribute. - */ - escape(attr) { - var val = this.attributes[attr]; - if (val == null) { - return ''; - } - - if (typeof val !== 'string') { - if (typeof val.toString !== 'function') { - return ''; - } - val = val.toString(); - } - return escape(val); - } - - /** - * Returns
true
if the attribute contains a value that is not
- * null or undefined.
- * @method has
- * @param {String} attr The string name of the attribute.
- * @return {Boolean}
- */
- has(attr) {
- var attributes = this.attributes;
- if (attributes.hasOwnProperty(attr)) {
- return attributes[attr] != null;
- }
- return false;
- }
-
- /**
- * Sets a hash of model attributes on the object.
- *
- * You can call it with an object containing keys and values, or with one - * key and value. For example:
- * gameTurn.set({ - * player: player1, - * diceRoll: 2 - * }, { - * error: function(gameTurnAgain, error) { - * // The set failed validation. - * } - * }); - * - * game.set("currentPlayer", player2, { - * error: function(gameTurnAgain, error) { - * // The set failed validation. - * } - * }); - * - * game.set("finished", true);- * - * @method set - * @param {String} key The key to set. - * @param {} value The value to give it. - * @param {Object} options A set of options for the set. - * The only supported option is
error
.
- * @return {Boolean} true if the set succeeded.
- */
- set(key, value, options) {
- var changes = {};
- var newOps = {};
- if (key && typeof key === 'object') {
- changes = key;
- options = value;
- } else if (typeof key === 'string') {
- changes[key] = value;
- } else {
- return this;
- }
-
- options = options || {};
- var readonly = [];
- if (typeof this.constructor.readOnlyAttributes === 'function') {
- readonly = readonly.concat(this.constructor.readOnlyAttributes());
- }
- for (var k in changes) {
- if (k === 'createdAt' || k === 'updatedAt') {
- // This property is read-only, but for legacy reasons we silently
- // ignore it
- continue;
- }
- if (readonly.indexOf(k) > -1) {
- throw new Error('Cannot modify readonly attribute: ' + k);
- }
- if (options.unset) {
- newOps[k] = new UnsetOp();
- } else if (changes[k] instanceof Op) {
- newOps[k] = changes[k];
- } else if (changes[k] && typeof changes[k] === 'object' && typeof changes[k].__op === 'string') {
- newOps[k] = opFromJSON(changes[k]);
- } else if (k === 'objectId' || k === 'id') {
- if (typeof changes[k] === 'string') {
- this.id = changes[k];
- }
- } else if (k === 'ACL' && typeof changes[k] === 'object' && !(changes[k] instanceof ParseACL)) {
- newOps[k] = new SetOp(new ParseACL(changes[k]));
- } else {
- newOps[k] = new SetOp(changes[k]);
- }
- }
-
- // Calculate new values
- var currentAttributes = this.attributes;
- var newValues = {};
- for (var attr in newOps) {
- if (newOps[attr] instanceof RelationOp) {
- newValues[attr] = newOps[attr].applyTo(currentAttributes[attr], this, attr);
- } else if (!(newOps[attr] instanceof UnsetOp)) {
- newValues[attr] = newOps[attr].applyTo(currentAttributes[attr]);
- }
- }
-
- // Validate changes
- if (!options.ignoreValidation) {
- var validation = this.validate(newValues);
- if (validation) {
- if (typeof options.error === 'function') {
- options.error(this, validation);
- }
- return false;
- }
- }
-
- // Consolidate Ops
- var pendingOps = this._getPendingOps();
- var last = pendingOps.length - 1;
- var stateController = CoreManager.getObjectStateController();
- for (var attr in newOps) {
- var nextOp = newOps[attr].mergeWith(pendingOps[last][attr]);
- stateController.setPendingOp(this._getStateIdentifier(), attr, nextOp);
- }
-
- return this;
- }
-
- /**
- * Remove an attribute from the model. This is a noop if the attribute doesn't
- * exist.
- * @method unset
- * @param {String} attr The string name of an attribute.
- */
- unset(attr, options) {
- options = options || {};
- options.unset = true;
- return this.set(attr, null, options);
- }
-
- /**
- * Atomically increments the value of the given attribute the next time the
- * object is saved. If no amount is specified, 1 is used by default.
- *
- * @method increment
- * @param attr {String} The key.
- * @param amount {Number} The amount to increment by (optional).
- */
- increment(attr, amount) {
- if (typeof amount === 'undefined') {
- amount = 1;
- }
- if (typeof amount !== 'number') {
- throw new Error('Cannot increment by a non-numeric amount.');
- }
- return this.set(attr, new IncrementOp(amount));
- }
-
- /**
- * Atomically add an object to the end of the array associated with a given
- * key.
- * @method add
- * @param attr {String} The key.
- * @param item {} The item to add.
- */
- add(attr, item) {
- return this.set(attr, new AddOp([item]));
- }
-
- /**
- * Atomically add an object to the array associated with a given key, only
- * if it is not already present in the array. The position of the insert is
- * not guaranteed.
- *
- * @method addUnique
- * @param attr {String} The key.
- * @param item {} The object to add.
- */
- addUnique(attr, item) {
- return this.set(attr, new AddUniqueOp([item]));
- }
-
- /**
- * Atomically remove all instances of an object from the array associated
- * with a given key.
- *
- * @method remove
- * @param attr {String} The key.
- * @param item {} The object to remove.
- */
- remove(attr, item) {
- return this.set(attr, new RemoveOp([item]));
- }
-
- /**
- * Returns an instance of a subclass of Parse.Op describing what kind of
- * modification has been performed on this field since the last time it was
- * saved. For example, after calling object.increment("x"), calling
- * object.op("x") would return an instance of Parse.Op.Increment.
- *
- * @method op
- * @param attr {String} The key.
- * @returns {Parse.Op} The operation, or undefined if none.
- */
- op(attr) {
- var pending = this._getPendingOps();
- for (var i = pending.length; i--;) {
- if (pending[i][attr]) {
- return pending[i][attr];
- }
- }
- }
-
- /**
- * Creates a new model with identical attributes to this one, similar to Backbone.Model's clone()
- * @method clone
- * @return {Parse.Object}
- */
- clone() {
- let clone = new this.constructor();
- if (!clone.className) {
- clone.className = this.className;
- }
- let attributes = this.attributes;
- if (typeof this.constructor.readOnlyAttributes === 'function') {
- let readonly = this.constructor.readOnlyAttributes() || [];
- // Attributes are frozen, so we have to rebuild an object,
- // rather than delete readonly keys
- let copy = {};
- for (let a in attributes) {
- if (readonly.indexOf(a) < 0) {
- copy[a] = attributes[a];
- }
- }
- attributes = copy;
- }
- if (clone.set) {
- clone.set(attributes);
- }
- return clone;
- }
-
- /**
- * Creates a new instance of this object. Not to be confused with clone()
- * @method newInstance
- * @return {Parse.Object}
- */
- newInstance() {
- let clone = new this.constructor();
- if (!clone.className) {
- clone.className = this.className;
- }
- clone.id = this.id;
- if (singleInstance) {
- // Just return an object with the right id
- return clone;
- }
-
- let stateController = CoreManager.getObjectStateController();
- if (stateController) {
- stateController.duplicateState(this._getStateIdentifier(), clone._getStateIdentifier());
- }
- return clone;
- }
-
- /**
- * Returns true if this object has never been saved to Parse.
- * @method isNew
- * @return {Boolean}
- */
- isNew() {
- return !this.id;
- }
-
- /**
- * Returns true if this object was created by the Parse server when the
- * object might have already been there (e.g. in the case of a Facebook
- * login)
- * @method existed
- * @return {Boolean}
- */
- existed() {
- if (!this.id) {
- return false;
- }
- let stateController = CoreManager.getObjectStateController();
- let state = stateController.getState(this._getStateIdentifier());
- if (state) {
- return state.existed;
- }
- return false;
- }
-
- /**
- * Checks if the model is currently in a valid state.
- * @method isValid
- * @return {Boolean}
- */
- isValid() {
- return !this.validate(this.attributes);
- }
-
- /**
- * You should not call this function directly unless you subclass
- * Parse.Object
, in which case you can override this method
- * to provide additional validation on set
and
- * save
. Your implementation should return
- *
- * @method validate
- * @param {Object} attrs The current data to validate.
- * @return {} False if the data is valid. An error object otherwise.
- * @see Parse.Object#set
- */
- validate(attrs) {
- if (attrs.hasOwnProperty('ACL') && !(attrs.ACL instanceof ParseACL)) {
- return new ParseError(ParseError.OTHER_CAUSE, 'ACL must be a Parse ACL.');
- }
- for (var key in attrs) {
- if (!/^[A-Za-z][0-9A-Za-z_]*$/.test(key)) {
- return new ParseError(ParseError.INVALID_KEY_NAME);
- }
- }
- return false;
- }
-
- /**
- * Returns the ACL for this object.
- * @method getACL
- * @returns {Parse.ACL} An instance of Parse.ACL.
- * @see Parse.Object#get
- */
- getACL() {
- var acl = this.get('ACL');
- if (acl instanceof ParseACL) {
- return acl;
- }
- return null;
- }
-
- /**
- * Sets the ACL to be used for this object.
- * @method setACL
- * @param {Parse.ACL} acl An instance of Parse.ACL.
- * @param {Object} options Optional Backbone-like options object to be
- * passed in to set.
- * @return {Boolean} Whether the set passed validation.
- * @see Parse.Object#set
- */
- setACL(acl, options) {
- return this.set('ACL', acl, options);
- }
-
- /**
- * Clears any changes to this object made since the last call to save()
- * @method revert
- */
- revert() {
- this._clearPendingOps();
- }
-
- /**
- * Clears all attributes on a model
- * @method clear
- */
- clear() {
- var attributes = this.attributes;
- var erasable = {};
- var readonly = ['createdAt', 'updatedAt'];
- if (typeof this.constructor.readOnlyAttributes === 'function') {
- readonly = readonly.concat(this.constructor.readOnlyAttributes());
- }
- for (var attr in attributes) {
- if (readonly.indexOf(attr) < 0) {
- erasable[attr] = true;
- }
- }
- return this.set(erasable, { unset: true });
- }
-
- /**
- * Fetch the model from the server. If the server's representation of the
- * model differs from its current attributes, they will be overriden.
- *
- * @method fetch
- * @param {Object} options A Backbone-style callback object.
- * Valid options are:- * object.save();- * or
- * object.save(null, options);- * or
- * object.save(attrs, options);- * or
- * object.save(key, value, options);- * - * For example,
- * gameTurn.save({ - * player: "Jake Cutter", - * diceRoll: 2 - * }, { - * success: function(gameTurnAgain) { - * // The save was successful. - * }, - * error: function(gameTurnAgain, error) { - * // The save failed. Error is an instance of Parse.Error. - * } - * });- * or with promises:
- * gameTurn.save({ - * player: "Jake Cutter", - * diceRoll: 2 - * }).then(function(gameTurnAgain) { - * // The save was successful. - * }, function(error) { - * // The save failed. Error is an instance of Parse.Error. - * });- * - * @method save - * @param {Object} options A Backbone-style callback object. - * Valid options are:
- * Parse.Object.fetchAll([object1, object2, ...], { - * success: function(list) { - * // All the objects were fetched. - * }, - * error: function(error) { - * // An error occurred while fetching one of the objects. - * }, - * }); - *- * - * @method fetchAll - * @param {Array} list A list of
Parse.Object
.
- * @param {Object} options A Backbone-style callback object.
- * @static
- * Valid options are:- * Parse.Object.fetchAllIfNeeded([object1, ...], { - * success: function(list) { - * // Objects were fetched and updated. - * }, - * error: function(error) { - * // An error occurred while fetching one of the objects. - * }, - * }); - *- * - * @method fetchAllIfNeeded - * @param {Array} list A list of
Parse.Object
.
- * @param {Object} options A Backbone-style callback object.
- * @static
- * Valid options are:Unlike saveAll, if an error occurs while deleting an individual model, - * this method will continue trying to delete the rest of the models if - * possible, except in the case of a fatal error like a connection error. - * - *
In particular, the Parse.Error object returned in the case of error may - * be one of two types: - * - *
- * Parse.Object.destroyAll([object1, object2, ...], { - * success: function() { - * // All the objects were deleted. - * }, - * error: function(error) { - * // An error occurred while deleting one or more of the objects. - * // If this is an aggregate error, then we can inspect each error - * // object individually to determine the reason why a particular - * // object was not deleted. - * if (error.code === Parse.Error.AGGREGATE_ERROR) { - * for (var i = 0; i < error.errors.length; i++) { - * console.log("Couldn't delete " + error.errors[i].object.id + - * "due to " + error.errors[i].message); - * } - * } else { - * console.log("Delete aborted because of " + error.message); - * } - * }, - * }); - *- * - * @method destroyAll - * @param {Array} list A list of
Parse.Object
.
- * @param {Object} options A Backbone-style callback object.
- * @static
- * Valid options are:- * Parse.Object.saveAll([object1, object2, ...], { - * success: function(list) { - * // All the objects were saved. - * }, - * error: function(error) { - * // An error occurred while saving one of the objects. - * }, - * }); - *- * - * @method saveAll - * @param {Array} list A list of
Parse.Object
.
- * @param {Object} options A Backbone-style callback object.
- * @static
- * Valid options are:A shortcut for:
- * var Foo = Parse.Object.extend("Foo"); - * var pointerToFoo = new Foo(); - * pointerToFoo.id = "myObjectId"; - *- * - * @method createWithoutData - * @param {String} id The ID of the object to create a reference to. - * @static - * @return {Parse.Object} A Parse.Object reference. - */ - static createWithoutData(id) { - var obj = new this(); - obj.id = id; - return obj; - } - - /** - * Creates a new instance of a Parse Object from a JSON representation. - * @method fromJSON - * @param {Object} json The JSON map of the Object's data - * @param {boolean} override In single instance mode, all old server data - * is overwritten if this is set to true - * @static - * @return {Parse.Object} A Parse.Object reference - */ - static fromJSON(json, override) { - if (!json.className) { - throw new Error('Cannot create an object without a className'); - } - var constructor = classMap[json.className]; - var o = constructor ? new constructor() : new ParseObject(json.className); - var otherAttributes = {}; - for (var attr in json) { - if (attr !== 'className' && attr !== '__type') { - otherAttributes[attr] = json[attr]; - } - } - if (override) { - // id needs to be set before clearServerData can work - if (otherAttributes.objectId) { - o.id = otherAttributes.objectId; - } - let preserved = null; - if (typeof o._preserveFieldsOnFetch === 'function') { - preserved = o._preserveFieldsOnFetch(); - } - o._clearServerData(); - if (preserved) { - o._finishFetch(preserved); - } - } - o._finishFetch(otherAttributes); - if (json.objectId) { - o._setExisted(true); - } - return o; - } - - /** - * Registers a subclass of Parse.Object with a specific class name. - * When objects of that class are retrieved from a query, they will be - * instantiated with this subclass. - * This is only necessary when using ES6 subclassing. - * @method registerSubclass - * @param {String} className The class name of the subclass - * @param {Class} constructor The subclass - */ - static registerSubclass(className, constructor) { - if (typeof className !== 'string') { - throw new TypeError('The first argument must be a valid class name.'); - } - if (typeof constructor === 'undefined') { - throw new TypeError('You must supply a subclass constructor.'); - } - if (typeof constructor !== 'function') { - throw new TypeError('You must register the subclass constructor. ' + 'Did you attempt to register an instance of the subclass?'); - } - classMap[className] = constructor; - if (!constructor.className) { - constructor.className = className; - } - } - - /** - * Creates a new subclass of Parse.Object for the given Parse class name. - * - *
Every extension of a Parse class will inherit from the most recent - * previous extension of that class. When a Parse.Object is automatically - * created by parsing JSON, it will use the most recent extension of that - * class.
- * - *You should call either:
- * var MyClass = Parse.Object.extend("MyClass", { - * Instance methods, - * initialize: function(attrs, options) { - * this.someInstanceProperty = [], - * Other instance properties - * } - * }, { - * Class properties - * });- * or, for Backbone compatibility:
- * var MyClass = Parse.Object.extend({ - * className: "MyClass", - * Instance methods, - * initialize: function(attrs, options) { - * this.someInstanceProperty = [], - * Other instance properties - * } - * }, { - * Class properties - * });- * - * @method extend - * @param {String} className The name of the Parse class backing this model. - * @param {Object} protoProps Instance properties to add to instances of the - * class returned from this method. - * @param {Object} classProps Class properties to add the class returned from - * this method. - * @return {Class} A new subclass of Parse.Object. - */ - static extend(className, protoProps, classProps) { - if (typeof className !== 'string') { - if (className && typeof className.className === 'string') { - return ParseObject.extend(className.className, className, protoProps); - } else { - throw new Error('Parse.Object.extend\'s first argument should be the className.'); - } - } - var adjustedClassName = className; - - if (adjustedClassName === 'User' && CoreManager.get('PERFORM_USER_REWRITE')) { - adjustedClassName = '_User'; - } - - var parentProto = ParseObject.prototype; - if (this.hasOwnProperty('__super__') && this.__super__) { - parentProto = this.prototype; - } else if (classMap[adjustedClassName]) { - parentProto = classMap[adjustedClassName].prototype; - } - var ParseObjectSubclass = function (attributes, options) { - this.className = adjustedClassName; - this._objCount = objectCount++; - // Enable legacy initializers - if (typeof this.initialize === 'function') { - this.initialize.apply(this, arguments); - } - - if (attributes && typeof attributes === 'object') { - if (!this.set(attributes || {}, options)) { - throw new Error('Can\'t create an invalid Parse Object'); - } - } - }; - ParseObjectSubclass.className = adjustedClassName; - ParseObjectSubclass.__super__ = parentProto; - - ParseObjectSubclass.prototype = Object.create(parentProto, { - constructor: { - value: ParseObjectSubclass, - enumerable: false, - writable: true, - configurable: true - } - }); - - if (protoProps) { - for (var prop in protoProps) { - if (prop !== 'className') { - Object.defineProperty(ParseObjectSubclass.prototype, prop, { - value: protoProps[prop], - enumerable: false, - writable: true, - configurable: true - }); - } - } - } - - if (classProps) { - for (var prop in classProps) { - if (prop !== 'className') { - Object.defineProperty(ParseObjectSubclass, prop, { - value: classProps[prop], - enumerable: false, - writable: true, - configurable: true - }); - } - } - } - - ParseObjectSubclass.extend = function (name, protoProps, classProps) { - if (typeof name === 'string') { - return ParseObject.extend.call(ParseObjectSubclass, name, protoProps, classProps); - } - return ParseObject.extend.call(ParseObjectSubclass, adjustedClassName, name, protoProps); - }; - ParseObjectSubclass.createWithoutData = ParseObject.createWithoutData; - - classMap[adjustedClassName] = ParseObjectSubclass; - return ParseObjectSubclass; - } - - /** - * Enable single instance objects, where any local objects with the same Id - * share the same attributes, and stay synchronized with each other. - * This is disabled by default in server environments, since it can lead to - * security issues. - * @method enableSingleInstance - */ - static enableSingleInstance() { - singleInstance = true; - CoreManager.setObjectStateController(SingleInstanceStateController); - } - - /** - * Disable single instance objects, where any local objects with the same Id - * share the same attributes, and stay synchronized with each other. - * When disabled, you can have two instances of the same object in memory - * without them sharing attributes. - * @method disableSingleInstance - */ - static disableSingleInstance() { - singleInstance = false; - CoreManager.setObjectStateController(UniqueInstanceStateController); - } -} - -var DefaultController = { - fetch(target, forceFetch, options) { - if (Array.isArray(target)) { - if (target.length < 1) { - return ParsePromise.as([]); - } - var objs = []; - var ids = []; - var className = null; - var results = []; - var error = null; - target.forEach((el, i) => { - if (error) { - return; - } - if (!className) { - className = el.className; - } - if (className !== el.className) { - error = new ParseError(ParseError.INVALID_CLASS_NAME, 'All objects should be of the same class'); - } - if (!el.id) { - error = new ParseError(ParseError.MISSING_OBJECT_ID, 'All objects must have an ID'); - } - if (forceFetch || Object.keys(el._getServerData()).length === 0) { - ids.push(el.id); - objs.push(el); - } - results.push(el); - }); - if (error) { - return ParsePromise.error(error); - } - var query = new ParseQuery(className); - query.containedIn('objectId', ids); - query._limit = ids.length; - return query.find(options).then(objects => { - var idMap = {}; - objects.forEach(o => { - idMap[o.id] = o; - }); - for (var i = 0; i < objs.length; i++) { - var obj = objs[i]; - if (!obj || !obj.id || !idMap[obj.id]) { - if (forceFetch) { - return ParsePromise.error(new ParseError(ParseError.OBJECT_NOT_FOUND, 'All objects must exist on the server.')); - } - } - } - if (!singleInstance) { - // If single instance objects are disabled, we need to replace the - for (var i = 0; i < results.length; i++) { - var obj = results[i]; - if (obj && obj.id && idMap[obj.id]) { - var id = obj.id; - obj._finishFetch(idMap[id].toJSON()); - results[i] = idMap[id]; - } - } - } - return ParsePromise.as(results); - }); - } else { - var RESTController = CoreManager.getRESTController(); - return RESTController.request('GET', 'classes/' + target.className + '/' + target._getId(), {}, options).then((response, status, xhr) => { - if (target instanceof ParseObject) { - target._clearPendingOps(); - target._clearServerData(); - target._finishFetch(response); - } - return target; - }); - } - }, - - destroy(target, options) { - var RESTController = CoreManager.getRESTController(); - if (Array.isArray(target)) { - if (target.length < 1) { - return ParsePromise.as([]); - } - var batches = [[]]; - target.forEach(obj => { - if (!obj.id) { - return; - } - batches[batches.length - 1].push(obj); - if (batches[batches.length - 1].length >= 20) { - batches.push([]); - } - }); - if (batches[batches.length - 1].length === 0) { - // If the last batch is empty, remove it - batches.pop(); - } - var deleteCompleted = ParsePromise.as(); - var errors = []; - batches.forEach(batch => { - deleteCompleted = deleteCompleted.then(() => { - return RESTController.request('POST', 'batch', { - requests: batch.map(obj => { - return { - method: 'DELETE', - path: getServerUrlPath() + 'classes/' + obj.className + '/' + obj._getId(), - body: {} - }; - }) - }, options).then(results => { - for (var i = 0; i < results.length; i++) { - if (results[i] && results[i].hasOwnProperty('error')) { - var err = new ParseError(results[i].error.code, results[i].error.error); - err.object = batch[i]; - errors.push(err); - } - } - }); - }); - }); - return deleteCompleted.then(() => { - if (errors.length) { - var aggregate = new ParseError(ParseError.AGGREGATE_ERROR); - aggregate.errors = errors; - return ParsePromise.error(aggregate); - } - return ParsePromise.as(target); - }); - } else if (target instanceof ParseObject) { - return RESTController.request('DELETE', 'classes/' + target.className + '/' + target._getId(), {}, options).then(() => { - return ParsePromise.as(target); - }); - } - return ParsePromise.as(target); - }, - - save(target, options) { - var RESTController = CoreManager.getRESTController(); - var stateController = CoreManager.getObjectStateController(); - if (Array.isArray(target)) { - if (target.length < 1) { - return ParsePromise.as([]); - } - - var unsaved = target.concat(); - for (var i = 0; i < target.length; i++) { - if (target[i] instanceof ParseObject) { - unsaved = unsaved.concat(unsavedChildren(target[i], true)); - } - } - unsaved = unique(unsaved); - - var filesSaved = ParsePromise.as(); - var pending = []; - unsaved.forEach(el => { - if (el instanceof ParseFile) { - filesSaved = filesSaved.then(() => { - return el.save(); - }); - } else if (el instanceof ParseObject) { - pending.push(el); - } - }); - - return filesSaved.then(() => { - var objectError = null; - return ParsePromise._continueWhile(() => { - return pending.length > 0; - }, () => { - var batch = []; - var nextPending = []; - pending.forEach(el => { - if (batch.length < 20 && canBeSerialized(el)) { - batch.push(el); - } else { - nextPending.push(el); - } - }); - pending = nextPending; - if (batch.length < 1) { - return ParsePromise.error(new ParseError(ParseError.OTHER_CAUSE, 'Tried to save a batch with a cycle.')); - } - - // Queue up tasks for each object in the batch. - // When every task is ready, the API request will execute - var batchReturned = new ParsePromise(); - var batchReady = []; - var batchTasks = []; - batch.forEach((obj, index) => { - var ready = new ParsePromise(); - batchReady.push(ready); - - stateController.pushPendingState(obj._getStateIdentifier()); - batchTasks.push(stateController.enqueueTask(obj._getStateIdentifier(), function () { - ready.resolve(); - return batchReturned.then((responses, status) => { - if (responses[index].hasOwnProperty('success')) { - obj._handleSaveResponse(responses[index].success, status); - } else { - if (!objectError && responses[index].hasOwnProperty('error')) { - var serverError = responses[index].error; - objectError = new ParseError(serverError.code, serverError.error); - // Cancel the rest of the save - pending = []; - } - obj._handleSaveError(); - } - }); - })); - }); - - ParsePromise.when(batchReady).then(() => { - // Kick off the batch request - return RESTController.request('POST', 'batch', { - requests: batch.map(obj => { - var params = obj._getSaveParams(); - params.path = getServerUrlPath() + params.path; - return params; - }) - }, options); - }).then((response, status, xhr) => { - batchReturned.resolve(response, status); - }); - - return ParsePromise.when(batchTasks); - }).then(() => { - if (objectError) { - return ParsePromise.error(objectError); - } - return ParsePromise.as(target); - }); - }); - } else if (target instanceof ParseObject) { - // copying target lets Flow guarantee the pointer isn't modified elsewhere - var targetCopy = target; - var task = function () { - var params = targetCopy._getSaveParams(); - return RESTController.request(params.method, params.path, params.body, options).then((response, status) => { - targetCopy._handleSaveResponse(response, status); - }, error => { - targetCopy._handleSaveError(); - return ParsePromise.error(error); - }); - }; - - stateController.pushPendingState(target._getStateIdentifier()); - return stateController.enqueueTask(target._getStateIdentifier(), task).then(() => { - return target; - }, error => { - return ParsePromise.error(error); - }); - } - return ParsePromise.as(); - } -}; - -CoreManager.setObjectController(DefaultController); \ No newline at end of file diff --git a/lib/react-native/ParseOp.js b/lib/react-native/ParseOp.js deleted file mode 100644 index db70be82c..000000000 --- a/lib/react-native/ParseOp.js +++ /dev/null @@ -1,434 +0,0 @@ -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * - */ - -import arrayContainsObject from './arrayContainsObject'; -import decode from './decode'; -import encode from './encode'; -import ParseObject from './ParseObject'; -import ParseRelation from './ParseRelation'; -import unique from './unique'; - -export function opFromJSON(json) { - if (!json || !json.__op) { - return null; - } - switch (json.__op) { - case 'Delete': - return new UnsetOp(); - case 'Increment': - return new IncrementOp(json.amount); - case 'Add': - return new AddOp(decode(json.objects)); - case 'AddUnique': - return new AddUniqueOp(decode(json.objects)); - case 'Remove': - return new RemoveOp(decode(json.objects)); - case 'AddRelation': - var toAdd = decode(json.objects); - if (!Array.isArray(toAdd)) { - return new RelationOp([], []); - } - return new RelationOp(toAdd, []); - case 'RemoveRelation': - var toRemove = decode(json.objects); - if (!Array.isArray(toRemove)) { - return new RelationOp([], []); - } - return new RelationOp([], toRemove); - case 'Batch': - var toAdd = []; - var toRemove = []; - for (var i = 0; i < json.ops.length; i++) { - if (json.ops[i].__op === 'AddRelation') { - toAdd = toAdd.concat(decode(json.ops[i].objects)); - } else if (json.ops[i].__op === 'RemoveRelation') { - toRemove = toRemove.concat(decode(json.ops[i].objects)); - } - } - return new RelationOp(toAdd, toRemove); - } - return null; -} - -export class Op { - // Empty parent class - applyTo(value) {} - mergeWith(previous) {} - toJSON() {} -} - -export class SetOp extends Op { - - constructor(value) { - super(); - this._value = value; - } - - applyTo(value) { - return this._value; - } - - mergeWith(previous) { - return new SetOp(this._value); - } - - toJSON() { - return encode(this._value, false, true); - } -} - -export class UnsetOp extends Op { - applyTo(value) { - return undefined; - } - - mergeWith(previous) { - return new UnsetOp(); - } - - toJSON() { - return { __op: 'Delete' }; - } -} - -export class IncrementOp extends Op { - - constructor(amount) { - super(); - if (typeof amount !== 'number') { - throw new TypeError('Increment Op must be initialized with a numeric amount.'); - } - this._amount = amount; - } - - applyTo(value) { - if (typeof value === 'undefined') { - return this._amount; - } - if (typeof value !== 'number') { - throw new TypeError('Cannot increment a non-numeric value.'); - } - return this._amount + value; - } - - mergeWith(previous) { - if (!previous) { - return this; - } - if (previous instanceof SetOp) { - return new SetOp(this.applyTo(previous._value)); - } - if (previous instanceof UnsetOp) { - return new SetOp(this._amount); - } - if (previous instanceof IncrementOp) { - return new IncrementOp(this.applyTo(previous._amount)); - } - throw new Error('Cannot merge Increment Op with the previous Op'); - } - - toJSON() { - return { __op: 'Increment', amount: this._amount }; - } -} - -export class AddOp extends Op { - - constructor(value) { - super(); - this._value = Array.isArray(value) ? value : [value]; - } - - applyTo(value) { - if (value == null) { - return this._value; - } - if (Array.isArray(value)) { - return value.concat(this._value); - } - throw new Error('Cannot add elements to a non-array value'); - } - - mergeWith(previous) { - if (!previous) { - return this; - } - if (previous instanceof SetOp) { - return new SetOp(this.applyTo(previous._value)); - } - if (previous instanceof UnsetOp) { - return new SetOp(this._value); - } - if (previous instanceof AddOp) { - return new AddOp(this.applyTo(previous._value)); - } - throw new Error('Cannot merge Add Op with the previous Op'); - } - - toJSON() { - return { __op: 'Add', objects: encode(this._value, false, true) }; - } -} - -export class AddUniqueOp extends Op { - - constructor(value) { - super(); - this._value = unique(Array.isArray(value) ? value : [value]); - } - - applyTo(value) { - if (value == null) { - return this._value || []; - } - if (Array.isArray(value)) { - // copying value lets Flow guarantee the pointer isn't modified elsewhere - var valueCopy = value; - var toAdd = []; - this._value.forEach(v => { - if (v instanceof ParseObject) { - if (!arrayContainsObject(valueCopy, v)) { - toAdd.push(v); - } - } else { - if (valueCopy.indexOf(v) < 0) { - toAdd.push(v); - } - } - }); - return value.concat(toAdd); - } - throw new Error('Cannot add elements to a non-array value'); - } - - mergeWith(previous) { - if (!previous) { - return this; - } - if (previous instanceof SetOp) { - return new SetOp(this.applyTo(previous._value)); - } - if (previous instanceof UnsetOp) { - return new SetOp(this._value); - } - if (previous instanceof AddUniqueOp) { - return new AddUniqueOp(this.applyTo(previous._value)); - } - throw new Error('Cannot merge AddUnique Op with the previous Op'); - } - - toJSON() { - return { __op: 'AddUnique', objects: encode(this._value, false, true) }; - } -} - -export class RemoveOp extends Op { - - constructor(value) { - super(); - this._value = unique(Array.isArray(value) ? value : [value]); - } - - applyTo(value) { - if (value == null) { - return []; - } - if (Array.isArray(value)) { - var i = value.indexOf(this._value); - var removed = value.concat([]); - for (var i = 0; i < this._value.length; i++) { - var index = removed.indexOf(this._value[i]); - while (index > -1) { - removed.splice(index, 1); - index = removed.indexOf(this._value[i]); - } - if (this._value[i] instanceof ParseObject && this._value[i].id) { - for (var j = 0; j < removed.length; j++) { - if (removed[j] instanceof ParseObject && this._value[i].id === removed[j].id) { - removed.splice(j, 1); - j--; - } - } - } - } - return removed; - } - throw new Error('Cannot remove elements from a non-array value'); - } - - mergeWith(previous) { - if (!previous) { - return this; - } - if (previous instanceof SetOp) { - return new SetOp(this.applyTo(previous._value)); - } - if (previous instanceof UnsetOp) { - return new UnsetOp(); - } - if (previous instanceof RemoveOp) { - var uniques = previous._value.concat([]); - for (var i = 0; i < this._value.length; i++) { - if (this._value[i] instanceof ParseObject) { - if (!arrayContainsObject(uniques, this._value[i])) { - uniques.push(this._value[i]); - } - } else { - if (uniques.indexOf(this._value[i]) < 0) { - uniques.push(this._value[i]); - } - } - } - return new RemoveOp(uniques); - } - throw new Error('Cannot merge Remove Op with the previous Op'); - } - - toJSON() { - return { __op: 'Remove', objects: encode(this._value, false, true) }; - } -} - -export class RelationOp extends Op { - - constructor(adds, removes) { - super(); - this._targetClassName = null; - - if (Array.isArray(adds)) { - this.relationsToAdd = unique(adds.map(this._extractId, this)); - } - - if (Array.isArray(removes)) { - this.relationsToRemove = unique(removes.map(this._extractId, this)); - } - } - - _extractId(obj) { - if (typeof obj === 'string') { - return obj; - } - if (!obj.id) { - throw new Error('You cannot add or remove an unsaved Parse Object from a relation'); - } - if (!this._targetClassName) { - this._targetClassName = obj.className; - } - if (this._targetClassName !== obj.className) { - throw new Error('Tried to create a Relation with 2 different object types: ' + this._targetClassName + ' and ' + obj.className + '.'); - } - return obj.id; - } - - applyTo(value, object, key) { - if (!value) { - if (!object || !key) { - throw new Error('Cannot apply a RelationOp without either a previous value, or an object and a key'); - } - var parent = new ParseObject(object.className); - if (object.id && object.id.indexOf('local') === 0) { - parent._localId = object.id; - } else if (object.id) { - parent.id = object.id; - } - var relation = new ParseRelation(parent, key); - relation.targetClassName = this._targetClassName; - return relation; - } - if (value instanceof ParseRelation) { - if (this._targetClassName) { - if (value.targetClassName) { - if (this._targetClassName !== value.targetClassName) { - throw new Error('Related object must be a ' + value.targetClassName + ', but a ' + this._targetClassName + ' was passed in.'); - } - } else { - value.targetClassName = this._targetClassName; - } - } - return value; - } else { - throw new Error('Relation cannot be applied to a non-relation field'); - } - } - - mergeWith(previous) { - if (!previous) { - return this; - } else if (previous instanceof UnsetOp) { - throw new Error('You cannot modify a relation after deleting it.'); - } else if (previous instanceof RelationOp) { - if (previous._targetClassName && previous._targetClassName !== this._targetClassName) { - throw new Error('Related object must be of class ' + previous._targetClassName + ', but ' + (this._targetClassName || 'null') + ' was passed in.'); - } - var newAdd = previous.relationsToAdd.concat([]); - this.relationsToRemove.forEach(r => { - var index = newAdd.indexOf(r); - if (index > -1) { - newAdd.splice(index, 1); - } - }); - this.relationsToAdd.forEach(r => { - var index = newAdd.indexOf(r); - if (index < 0) { - newAdd.push(r); - } - }); - - var newRemove = previous.relationsToRemove.concat([]); - this.relationsToAdd.forEach(r => { - var index = newRemove.indexOf(r); - if (index > -1) { - newRemove.splice(index, 1); - } - }); - this.relationsToRemove.forEach(r => { - var index = newRemove.indexOf(r); - if (index < 0) { - newRemove.push(r); - } - }); - - var newRelation = new RelationOp(newAdd, newRemove); - newRelation._targetClassName = this._targetClassName; - return newRelation; - } - throw new Error('Cannot merge Relation Op with the previous Op'); - } - - toJSON() { - var idToPointer = id => { - return { - __type: 'Pointer', - className: this._targetClassName, - objectId: id - }; - }; - - var adds = null; - var removes = null; - var pointers = null; - - if (this.relationsToAdd.length > 0) { - pointers = this.relationsToAdd.map(idToPointer); - adds = { __op: 'AddRelation', objects: pointers }; - } - if (this.relationsToRemove.length > 0) { - pointers = this.relationsToRemove.map(idToPointer); - removes = { __op: 'RemoveRelation', objects: pointers }; - } - - if (adds && removes) { - return { __op: 'Batch', ops: [adds, removes] }; - } - - return adds || removes || {}; - } -} \ No newline at end of file diff --git a/lib/react-native/ParsePromise.js b/lib/react-native/ParsePromise.js deleted file mode 100644 index ef7626b6d..000000000 --- a/lib/react-native/ParsePromise.js +++ /dev/null @@ -1,575 +0,0 @@ -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -var isPromisesAPlusCompliant = true; - -/** - * A Promise is returned by async methods as a hook to provide callbacks to be - * called when the async task is fulfilled. - * - *
Typical usage would be like:
- * query.find().then(function(results) { - * results[0].set("foo", "bar"); - * return results[0].saveAsync(); - * }).then(function(result) { - * console.log("Updated " + result.id); - * }); - *- * - * @class Parse.Promise - * @constructor - */ -export default class ParsePromise { - constructor(executor) { - this._resolved = false; - this._rejected = false; - this._resolvedCallbacks = []; - this._rejectedCallbacks = []; - - if (typeof executor === 'function') { - executor(this.resolve.bind(this), this.reject.bind(this)); - } - } - - /** - * Marks this promise as fulfilled, firing any callbacks waiting on it. - * @method resolve - * @param {Object} result the result to pass to the callbacks. - */ - resolve(...results) { - if (this._resolved || this._rejected) { - throw new Error('A promise was resolved even though it had already been ' + (this._resolved ? 'resolved' : 'rejected') + '.'); - } - this._resolved = true; - this._result = results; - for (var i = 0; i < this._resolvedCallbacks.length; i++) { - this._resolvedCallbacks[i].apply(this, results); - } - - this._resolvedCallbacks = []; - this._rejectedCallbacks = []; - } - - /** - * Marks this promise as fulfilled, firing any callbacks waiting on it. - * @method reject - * @param {Object} error the error to pass to the callbacks. - */ - reject(error) { - if (this._resolved || this._rejected) { - throw new Error('A promise was rejected even though it had already been ' + (this._resolved ? 'resolved' : 'rejected') + '.'); - } - this._rejected = true; - this._error = error; - for (var i = 0; i < this._rejectedCallbacks.length; i++) { - this._rejectedCallbacks[i](error); - } - this._resolvedCallbacks = []; - this._rejectedCallbacks = []; - } - - /** - * Adds callbacks to be called when this promise is fulfilled. Returns a new - * Promise that will be fulfilled when the callback is complete. It allows - * chaining. If the callback itself returns a Promise, then the one returned - * by "then" will not be fulfilled until that one returned by the callback - * is fulfilled. - * @method then - * @param {Function} resolvedCallback Function that is called when this - * Promise is resolved. Once the callback is complete, then the Promise - * returned by "then" will also be fulfilled. - * @param {Function} rejectedCallback Function that is called when this - * Promise is rejected with an error. Once the callback is complete, then - * the promise returned by "then" with be resolved successfully. If - * rejectedCallback is null, or it returns a rejected Promise, then the - * Promise returned by "then" will be rejected with that error. - * @return {Parse.Promise} A new Promise that will be fulfilled after this - * Promise is fulfilled and either callback has completed. If the callback - * returned a Promise, then this Promise will not be fulfilled until that - * one is. - */ - then(resolvedCallback, rejectedCallback) { - var promise = new ParsePromise(); - - var wrappedResolvedCallback = function (...results) { - if (typeof resolvedCallback === 'function') { - if (isPromisesAPlusCompliant) { - try { - results = [resolvedCallback.apply(this, results)]; - } catch (e) { - results = [ParsePromise.error(e)]; - } - } else { - results = [resolvedCallback.apply(this, results)]; - } - } - if (results.length === 1 && ParsePromise.is(results[0])) { - results[0].then(function () { - promise.resolve.apply(promise, arguments); - }, function (error) { - promise.reject(error); - }); - } else { - promise.resolve.apply(promise, results); - } - }; - - var wrappedRejectedCallback = function (error) { - var result = []; - if (typeof rejectedCallback === 'function') { - if (isPromisesAPlusCompliant) { - try { - result = [rejectedCallback(error)]; - } catch (e) { - result = [ParsePromise.error(e)]; - } - } else { - result = [rejectedCallback(error)]; - } - if (result.length === 1 && ParsePromise.is(result[0])) { - result[0].then(function () { - promise.resolve.apply(promise, arguments); - }, function (error) { - promise.reject(error); - }); - } else { - if (isPromisesAPlusCompliant) { - promise.resolve.apply(promise, result); - } else { - promise.reject(result[0]); - } - } - } else { - promise.reject(error); - } - }; - - var runLater = function (fn) { - fn.call(); - }; - if (isPromisesAPlusCompliant) { - if (typeof process !== 'undefined' && typeof process.nextTick === 'function') { - runLater = function (fn) { - process.nextTick(fn); - }; - } else if (typeof setTimeout === 'function') { - runLater = function (fn) { - setTimeout(fn, 0); - }; - } - } - - if (this._resolved) { - runLater(() => { - wrappedResolvedCallback.apply(this, this._result); - }); - } else if (this._rejected) { - runLater(() => { - wrappedRejectedCallback(this._error); - }); - } else { - this._resolvedCallbacks.push(wrappedResolvedCallback); - this._rejectedCallbacks.push(wrappedRejectedCallback); - } - - return promise; - } - - /** - * Add handlers to be called when the promise - * is either resolved or rejected - * @method always - */ - always(callback) { - return this.then(callback, callback); - } - - /** - * Add handlers to be called when the Promise object is resolved - * @method done - */ - done(callback) { - return this.then(callback); - } - - /** - * Add handlers to be called when the Promise object is rejected - * Alias for catch(). - * @method fail - */ - fail(callback) { - return this.then(null, callback); - } - - /** - * Add handlers to be called when the Promise object is rejected - * @method catch - */ - catch(callback) { - return this.then(null, callback); - } - - /** - * Run the given callbacks after this promise is fulfilled. - * @method _thenRunCallbacks - * @param optionsOrCallback {} A Backbone-style options callback, or a - * callback function. If this is an options object and contains a "model" - * attributes, that will be passed to error callbacks as the first argument. - * @param model {} If truthy, this will be passed as the first result of - * error callbacks. This is for Backbone-compatability. - * @return {Parse.Promise} A promise that will be resolved after the - * callbacks are run, with the same result as this. - */ - _thenRunCallbacks(optionsOrCallback, model) { - var options = {}; - if (typeof optionsOrCallback === 'function') { - options.success = function (result) { - optionsOrCallback(result, null); - }; - options.error = function (error) { - optionsOrCallback(null, error); - }; - } else if (typeof optionsOrCallback === 'object') { - if (typeof optionsOrCallback.success === 'function') { - options.success = optionsOrCallback.success; - } - if (typeof optionsOrCallback.error === 'function') { - options.error = optionsOrCallback.error; - } - } - - return this.then(function (...results) { - if (options.success) { - options.success.apply(this, results); - } - return ParsePromise.as.apply(ParsePromise, arguments); - }, function (error) { - if (options.error) { - if (typeof model !== 'undefined') { - options.error(model, error); - } else { - options.error(error); - } - } - // By explicitly returning a rejected Promise, this will work with - // either jQuery or Promises/A+ semantics. - return ParsePromise.error(error); - }); - } - - /** - * Adds a callback function that should be called regardless of whether - * this promise failed or succeeded. The callback will be given either the - * array of results for its first argument, or the error as its second, - * depending on whether this Promise was rejected or resolved. Returns a - * new Promise, like "then" would. - * @method _continueWith - * @param {Function} continuation the callback. - */ - _continueWith(continuation) { - return this.then(function (...args) { - return continuation(args, null); - }, function (error) { - return continuation(null, error); - }); - } - - /** - * Returns true iff the given object fulfils the Promise interface. - * @method is - * @param {Object} promise The object to test - * @static - * @return {Boolean} - */ - static is(promise) { - return promise != null && typeof promise.then === 'function'; - } - - /** - * Returns a new promise that is resolved with a given value. - * @method as - * @param value The value to resolve the promise with - * @static - * @return {Parse.Promise} the new promise. - */ - static as(...values) { - var promise = new ParsePromise(); - promise.resolve.apply(promise, values); - return promise; - } - - /** - * Returns a new promise that is resolved with a given value. - * If that value is a thenable Promise (has a .then() prototype - * method), the new promise will be chained to the end of the - * value. - * @method resolve - * @param value The value to resolve the promise with - * @static - * @return {Parse.Promise} the new promise. - */ - static resolve(value) { - return new ParsePromise((resolve, reject) => { - if (ParsePromise.is(value)) { - value.then(resolve, reject); - } else { - resolve(value); - } - }); - } - - /** - * Returns a new promise that is rejected with a given error. - * @method error - * @param error The error to reject the promise with - * @static - * @return {Parse.Promise} the new promise. - */ - static error(...errors) { - var promise = new ParsePromise(); - promise.reject.apply(promise, errors); - return promise; - } - - /** - * Returns a new promise that is rejected with a given error. - * This is an alias for Parse.Promise.error, for compliance with - * the ES6 implementation. - * @method reject - * @param error The error to reject the promise with - * @static - * @return {Parse.Promise} the new promise. - */ - static reject(...errors) { - return ParsePromise.error.apply(null, errors); - } - - /** - * Returns a new promise that is fulfilled when all of the input promises - * are resolved. If any promise in the list fails, then the returned promise - * will be rejected with an array containing the error from each promise. - * If they all succeed, then the returned promise will succeed, with the - * results being the results of all the input - * promises. For example:
- * var p1 = Parse.Promise.as(1); - * var p2 = Parse.Promise.as(2); - * var p3 = Parse.Promise.as(3); - * - * Parse.Promise.when(p1, p2, p3).then(function(r1, r2, r3) { - * console.log(r1); // prints 1 - * console.log(r2); // prints 2 - * console.log(r3); // prints 3 - * });- * - * The input promises can also be specified as an array:
- * var promises = [p1, p2, p3]; - * Parse.Promise.when(promises).then(function(results) { - * console.log(results); // prints [1,2,3] - * }); - *- * @method when - * @param {Array} promises a list of promises to wait for. - * @static - * @return {Parse.Promise} the new promise. - */ - static when(promises) { - var objects; - var arrayArgument = Array.isArray(promises); - if (arrayArgument) { - objects = promises; - } else { - objects = arguments; - } - - var total = objects.length; - var hadError = false; - var results = []; - var returnValue = arrayArgument ? [results] : results; - var errors = []; - results.length = objects.length; - errors.length = objects.length; - - if (total === 0) { - return ParsePromise.as.apply(this, returnValue); - } - - var promise = new ParsePromise(); - - var resolveOne = function () { - total--; - if (total <= 0) { - if (hadError) { - promise.reject(errors); - } else { - promise.resolve.apply(promise, returnValue); - } - } - }; - - var chain = function (object, index) { - if (ParsePromise.is(object)) { - object.then(function (result) { - results[index] = result; - resolveOne(); - }, function (error) { - errors[index] = error; - hadError = true; - resolveOne(); - }); - } else { - results[i] = object; - resolveOne(); - } - }; - for (var i = 0; i < objects.length; i++) { - chain(objects[i], i); - } - - return promise; - } - - /** - * Returns a new promise that is fulfilled when all of the promises in the - * iterable argument are resolved. If any promise in the list fails, then - * the returned promise will be immediately rejected with the reason that - * single promise rejected. If they all succeed, then the returned promise - * will succeed, with the results being the results of all the input - * promises. If the iterable provided is empty, the returned promise will - * be immediately resolved. - * - * For example:
- * var p1 = Parse.Promise.as(1); - * var p2 = Parse.Promise.as(2); - * var p3 = Parse.Promise.as(3); - * - * Parse.Promise.all([p1, p2, p3]).then(function([r1, r2, r3]) { - * console.log(r1); // prints 1 - * console.log(r2); // prints 2 - * console.log(r3); // prints 3 - * });- * - * @method all - * @param {Iterable} promises an iterable of promises to wait for. - * @static - * @return {Parse.Promise} the new promise. - */ - static all(promises) { - let total = 0; - let objects = []; - - for (let p of promises) { - objects[total++] = p; - } - - if (total === 0) { - return ParsePromise.as([]); - } - - let hadError = false; - let promise = new ParsePromise(); - let resolved = 0; - let results = []; - objects.forEach((object, i) => { - if (ParsePromise.is(object)) { - object.then(result => { - if (hadError) { - return false; - } - results[i] = result; - resolved++; - if (resolved >= total) { - promise.resolve(results); - } - }, error => { - // Reject immediately - promise.reject(error); - hadError = true; - }); - } else { - results[i] = object; - resolved++; - if (!hadError && resolved >= total) { - promise.resolve(results); - } - } - }); - - return promise; - } - - /** - * Returns a new promise that is immediately fulfilled when any of the - * promises in the iterable argument are resolved or rejected. If the - * first promise to complete is resolved, the returned promise will be - * resolved with the same value. Likewise, if the first promise to - * complete is rejected, the returned promise will be rejected with the - * same reason. - * - * @method race - * @param {Iterable} promises an iterable of promises to wait for. - * @static - * @return {Parse.Promise} the new promise. - */ - static race(promises) { - let completed = false; - let promise = new ParsePromise(); - for (let p of promises) { - if (ParsePromise.is(p)) { - p.then(result => { - if (completed) { - return; - } - completed = true; - promise.resolve(result); - }, error => { - if (completed) { - return; - } - completed = true; - promise.reject(error); - }); - } else if (!completed) { - completed = true; - promise.resolve(p); - } - } - - return promise; - } - - /** - * Runs the given asyncFunction repeatedly, as long as the predicate - * function returns a truthy value. Stops repeating if asyncFunction returns - * a rejected promise. - * @method _continueWhile - * @param {Function} predicate should return false when ready to stop. - * @param {Function} asyncFunction should return a Promise. - * @static - */ - static _continueWhile(predicate, asyncFunction) { - if (predicate()) { - return asyncFunction().then(function () { - return ParsePromise._continueWhile(predicate, asyncFunction); - }); - } - return ParsePromise.as(); - } - - static isPromisesAPlusCompliant() { - return isPromisesAPlusCompliant; - } - - static enableAPlusCompliant() { - isPromisesAPlusCompliant = true; - } - - static disableAPlusCompliant() { - isPromisesAPlusCompliant = false; - } -} \ No newline at end of file diff --git a/lib/react-native/ParseQuery.js b/lib/react-native/ParseQuery.js deleted file mode 100644 index a32275dc1..000000000 --- a/lib/react-native/ParseQuery.js +++ /dev/null @@ -1,1113 +0,0 @@ -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * - */ - -import CoreManager from './CoreManager'; -import encode from './encode'; -import ParseError from './ParseError'; -import ParseGeoPoint from './ParseGeoPoint'; -import ParseObject from './ParseObject'; -import ParsePromise from './ParsePromise'; - -/** - * Converts a string into a regex that matches it. - * Surrounding with \Q .. \E does this, we just need to escape any \E's in - * the text separately. - */ -function quote(s) { - return '\\Q' + s.replace('\\E', '\\E\\\\E\\Q') + '\\E'; -} - -/** - * Handles pre-populating the result data of a query with select fields, - * making sure that the data object contains keys for all objects that have - * been requested with a select, so that our cached state updates correctly. - */ -function handleSelectResult(data, select) { - var serverDataMask = {}; - - select.forEach(field => { - let hasSubObjectSelect = field.indexOf(".") !== -1; - if (!hasSubObjectSelect && !data.hasOwnProperty(field)) { - // this field was selected, but is missing from the retrieved data - data[field] = undefined; - } else if (hasSubObjectSelect) { - // this field references a sub-object, - // so we need to walk down the path components - let pathComponents = field.split("."); - var obj = data; - var serverMask = serverDataMask; - - pathComponents.forEach((component, index, arr) => { - // add keys if the expected data is missing - if (!obj[component]) { - obj[component] = index == arr.length - 1 ? undefined : {}; - } - obj = obj[component]; - - //add this path component to the server mask so we can fill it in later if needed - if (index < arr.length - 1) { - if (!serverMask[component]) { - serverMask[component] = {}; - } - } - }); - } - }); - - if (Object.keys(serverDataMask).length > 0) { - // When selecting from sub-objects, we don't want to blow away the missing - // information that we may have retrieved before. We've already added any - // missing selected keys to sub-objects, but we still need to add in the - // data for any previously retrieved sub-objects that were not selected. - - let serverData = CoreManager.getObjectStateController().getServerData({ id: data.objectId, className: data.className }); - - function copyMissingDataWithMask(src, dest, mask, copyThisLevel) { - //copy missing elements at this level - if (copyThisLevel) { - for (var key in src) { - if (src.hasOwnProperty(key) && !dest.hasOwnProperty(key)) { - dest[key] = src[key]; - } - } - } - for (var key in mask) { - //traverse into objects as needed - copyMissingDataWithMask(src[key], dest[key], mask[key], true); - } - } - - copyMissingDataWithMask(serverData, data, serverDataMask, false); - } -} - -/** - * Creates a new parse Parse.Query for the given Parse.Object subclass. - * @class Parse.Query - * @constructor - * @param {} objectClass An instance of a subclass of Parse.Object, or a Parse className string. - * - *
Parse.Query defines a query that is used to fetch Parse.Objects. The
- * most common use case is finding all objects that match a query through the
- * find
method. For example, this sample code fetches all objects
- * of class MyClass
. It calls a different function depending on
- * whether the fetch succeeded or not.
- *
- *
- * var query = new Parse.Query(MyClass); - * query.find({ - * success: function(results) { - * // results is an array of Parse.Object. - * }, - * - * error: function(error) { - * // error is an instance of Parse.Error. - * } - * });- * - *
A Parse.Query can also be used to retrieve a single object whose id is
- * known, through the get method. For example, this sample code fetches an
- * object of class MyClass
and id myId
. It calls a
- * different function depending on whether the fetch succeeded or not.
- *
- *
- * var query = new Parse.Query(MyClass); - * query.get(myId, { - * success: function(object) { - * // object is an instance of Parse.Object. - * }, - * - * error: function(object, error) { - * // error is an instance of Parse.Error. - * } - * });- * - *
A Parse.Query can also be used to count the number of objects that match
- * the query without retrieving all of those objects. For example, this
- * sample code counts the number of objects of the class MyClass
- *
- * var query = new Parse.Query(MyClass); - * query.count({ - * success: function(number) { - * // There are number instances of MyClass. - * }, - * - * error: function(error) { - * // error is an instance of Parse.Error. - * } - * });- */ -export default class ParseQuery { - - constructor(objectClass) { - if (typeof objectClass === 'string') { - if (objectClass === 'User' && CoreManager.get('PERFORM_USER_REWRITE')) { - this.className = '_User'; - } else { - this.className = objectClass; - } - } else if (objectClass instanceof ParseObject) { - this.className = objectClass.className; - } else if (typeof objectClass === 'function') { - if (typeof objectClass.className === 'string') { - this.className = objectClass.className; - } else { - var obj = new objectClass(); - this.className = obj.className; - } - } else { - throw new TypeError('A ParseQuery must be constructed with a ParseObject or class name.'); - } - - this._where = {}; - this._include = []; - this._limit = -1; // negative limit is not sent in the server request - this._skip = 0; - this._extraOptions = {}; - } - - /** - * Adds constraint that at least one of the passed in queries matches. - * @method _orQuery - * @param {Array} queries - * @return {Parse.Query} Returns the query, so you can chain this call. - */ - _orQuery(queries) { - var queryJSON = queries.map(q => { - return q.toJSON().where; - }); - - this._where.$or = queryJSON; - return this; - } - - /** - * Helper for condition queries - */ - _addCondition(key, condition, value) { - if (!this._where[key] || typeof this._where[key] === 'string') { - this._where[key] = {}; - } - this._where[key][condition] = encode(value, false, true); - return this; - } - - /** - * Converts string for regular expression at the beginning - */ - _regexStartWith(string) { - return '^' + quote(string); - } - - /** - * Returns a JSON representation of this query. - * @method toJSON - * @return {Object} The JSON representation of the query. - */ - toJSON() { - var params = { - where: this._where - }; - - if (this._include.length) { - params.include = this._include.join(','); - } - if (this._select) { - params.keys = this._select.join(','); - } - if (this._limit >= 0) { - params.limit = this._limit; - } - if (this._skip > 0) { - params.skip = this._skip; - } - if (this._order) { - params.order = this._order.join(','); - } - for (var key in this._extraOptions) { - params[key] = this._extraOptions[key]; - } - - return params; - } - - /** - * Constructs a Parse.Object whose id is already known by fetching data from - * the server. Either options.success or options.error is called when the - * find completes. - * - * @method get - * @param {String} objectId The id of the object to be fetched. - * @param {Object} options A Backbone-style options object. - * Valid options are:
var compoundQuery = Parse.Query.or(query1, query2, query3);- * - * will create a compoundQuery that is an or of the query1, query2, and - * query3. - * @method or - * @param {...Parse.Query} var_args The list of queries to OR. - * @static - * @return {Parse.Query} The query that is the OR of the passed in queries. - */ - static or(...queries) { - var className = null; - queries.forEach(q => { - if (!className) { - className = q.className; - } - - if (className !== q.className) { - throw new Error('All queries must be for the same class.'); - } - }); - - var query = new ParseQuery(className); - query._orQuery(queries); - return query; - } -} - -var DefaultController = { - find(className, params, options) { - var RESTController = CoreManager.getRESTController(); - - return RESTController.request('GET', 'classes/' + className, params, options); - } -}; - -CoreManager.setQueryController(DefaultController); \ No newline at end of file diff --git a/lib/react-native/ParseRelation.js b/lib/react-native/ParseRelation.js deleted file mode 100644 index c07b48cc1..000000000 --- a/lib/react-native/ParseRelation.js +++ /dev/null @@ -1,140 +0,0 @@ -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * - */ - -import { RelationOp } from './ParseOp'; -import ParseObject from './ParseObject'; -import ParseQuery from './ParseQuery'; - -/** - * Creates a new Relation for the given parent object and key. This - * constructor should rarely be used directly, but rather created by - * Parse.Object.relation. - * @class Parse.Relation - * @constructor - * @param {Parse.Object} parent The parent of this relation. - * @param {String} key The key for this relation on the parent. - * - *
- * A class that is used to access all of the children of a many-to-many - * relationship. Each instance of Parse.Relation is associated with a - * particular parent object and key. - *
- */ -export default class ParseRelation { - - constructor(parent, key) { - this.parent = parent; - this.key = key; - this.targetClassName = null; - } - - /** - * Makes sure that this relation has the right parent and key. - */ - _ensureParentAndKey(parent, key) { - this.key = this.key || key; - if (this.key !== key) { - throw new Error('Internal Error. Relation retrieved from two different keys.'); - } - if (this.parent) { - if (this.parent.className !== parent.className) { - throw new Error('Internal Error. Relation retrieved from two different Objects.'); - } - if (this.parent.id) { - if (this.parent.id !== parent.id) { - throw new Error('Internal Error. Relation retrieved from two different Objects.'); - } - } else if (parent.id) { - this.parent = parent; - } - } else { - this.parent = parent; - } - } - - /** - * Adds a Parse.Object or an array of Parse.Objects to the relation. - * @method add - * @param {} objects The item or items to add. - */ - add(objects) { - if (!Array.isArray(objects)) { - objects = [objects]; - } - - var change = new RelationOp(objects, []); - var parent = this.parent; - if (!parent) { - throw new Error('Cannot add to a Relation without a parent'); - } - parent.set(this.key, change); - this.targetClassName = change._targetClassName; - return parent; - } - - /** - * Removes a Parse.Object or an array of Parse.Objects from this relation. - * @method remove - * @param {} objects The item or items to remove. - */ - remove(objects) { - if (!Array.isArray(objects)) { - objects = [objects]; - } - - var change = new RelationOp([], objects); - if (!this.parent) { - throw new Error('Cannot remove from a Relation without a parent'); - } - this.parent.set(this.key, change); - this.targetClassName = change._targetClassName; - } - - /** - * Returns a JSON version of the object suitable for saving to disk. - * @method toJSON - * @return {Object} - */ - toJSON() { - return { - __type: 'Relation', - className: this.targetClassName - }; - } - - /** - * Returns a Parse.Query that is limited to objects in this - * relation. - * @method query - * @return {Parse.Query} - */ - query() { - var query; - var parent = this.parent; - if (!parent) { - throw new Error('Cannot construct a query for a Relation without a parent'); - } - if (!this.targetClassName) { - query = new ParseQuery(parent.className); - query._extraOptions.redirectClassNameForKey = this.key; - } else { - query = new ParseQuery(this.targetClassName); - } - query._addCondition('$relatedTo', 'object', { - __type: 'Pointer', - className: parent.className, - objectId: parent.id - }); - query._addCondition('$relatedTo', 'key', this.key); - - return query; - } -} \ No newline at end of file diff --git a/lib/react-native/ParseRole.js b/lib/react-native/ParseRole.js deleted file mode 100644 index 04164b2e4..000000000 --- a/lib/react-native/ParseRole.js +++ /dev/null @@ -1,133 +0,0 @@ -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * - */ - -import ParseACL from './ParseACL'; -import ParseError from './ParseError'; -import ParseObject from './ParseObject'; - -/** - * Represents a Role on the Parse server. Roles represent groupings of - * Users for the purposes of granting permissions (e.g. specifying an ACL - * for an Object). Roles are specified by their sets of child users and - * child roles, all of which are granted any permissions that the parent - * role has. - * - *Roles must have a name (which cannot be changed after creation of the - * role), and must specify an ACL.
- * @class Parse.Role - * @constructor - * @param {String} name The name of the Role to create. - * @param {Parse.ACL} acl The ACL for this role. Roles must have an ACL. - * A Parse.Role is a local representation of a role persisted to the Parse - * cloud. - */ -export default class ParseRole extends ParseObject { - constructor(name, acl) { - super('_Role'); - if (typeof name === 'string' && acl instanceof ParseACL) { - this.setName(name); - this.setACL(acl); - } - } - - /** - * Gets the name of the role. You can alternatively call role.get("name") - * - * @method getName - * @return {String} the name of the role. - */ - getName() { - const name = this.get('name'); - if (name == null || typeof name === 'string') { - return name; - } - return ''; - } - - /** - * Sets the name for a role. This value must be set before the role has - * been saved to the server, and cannot be set once the role has been - * saved. - * - *- * A role's name can only contain alphanumeric characters, _, -, and - * spaces. - *
- * - *This is equivalent to calling role.set("name", name)
- * - * @method setName - * @param {String} name The name of the role. - * @param {Object} options Standard options object with success and error - * callbacks. - */ - setName(name, options) { - return this.set('name', name, options); - } - - /** - * Gets the Parse.Relation for the Parse.Users that are direct - * children of this role. These users are granted any privileges that this - * role has been granted (e.g. read or write access through ACLs). You can - * add or remove users from the role through this relation. - * - *This is equivalent to calling role.relation("users")
- * - * @method getUsers - * @return {Parse.Relation} the relation for the users belonging to this - * role. - */ - getUsers() { - return this.relation('users'); - } - - /** - * Gets the Parse.Relation for the Parse.Roles that are direct - * children of this role. These roles' users are granted any privileges that - * this role has been granted (e.g. read or write access through ACLs). You - * can add or remove child roles from this role through this relation. - * - *This is equivalent to calling role.relation("roles")
- * - * @method getRoles - * @return {Parse.Relation} the relation for the roles belonging to this - * role. - */ - getRoles() { - return this.relation('roles'); - } - - validate(attrs, options) { - var isInvalid = super.validate(attrs, options); - if (isInvalid) { - return isInvalid; - } - - if ('name' in attrs && attrs.name !== this.getName()) { - var newName = attrs.name; - if (this.id && this.id !== attrs.objectId) { - // Check to see if the objectId being set matches this.id - // This happens during a fetch -- the id is set before calling fetch - // Let the name be set in this case - return new ParseError(ParseError.OTHER_CAUSE, 'A role\'s name can only be set before it has been saved.'); - } - if (typeof newName !== 'string') { - return new ParseError(ParseError.OTHER_CAUSE, 'A role\'s name must be a String.'); - } - if (!/^[0-9a-zA-Z\-_ ]+$/.test(newName)) { - return new ParseError(ParseError.OTHER_CAUSE, 'A role\'s name can be only contain alphanumeric characters, _, ' + '-, and spaces.'); - } - } - return false; - } -} - -ParseObject.registerSubclass('_Role', ParseRole); \ No newline at end of file diff --git a/lib/react-native/ParseSession.js b/lib/react-native/ParseSession.js deleted file mode 100644 index f1554fc9a..000000000 --- a/lib/react-native/ParseSession.js +++ /dev/null @@ -1,114 +0,0 @@ -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * - */ - -import CoreManager from './CoreManager'; -import isRevocableSession from './isRevocableSession'; -import ParseObject from './ParseObject'; -import ParsePromise from './ParsePromise'; -import ParseUser from './ParseUser'; - -/** - * @class Parse.Session - * @constructor - * - *A Parse.Session object is a local representation of a revocable session. - * This class is a subclass of a Parse.Object, and retains the same - * functionality of a Parse.Object.
- */ -export default class ParseSession extends ParseObject { - constructor(attributes) { - super('_Session'); - if (attributes && typeof attributes === 'object') { - if (!this.set(attributes || {})) { - throw new Error('Can\'t create an invalid Session'); - } - } - } - - /** - * Returns the session token string. - * @method getSessionToken - * @return {String} - */ - getSessionToken() { - const token = this.get('sessionToken'); - if (typeof token === 'string') { - return token; - } - return ''; - } - - static readOnlyAttributes() { - return ['createdWith', 'expiresAt', 'installationId', 'restricted', 'sessionToken', 'user']; - } - - /** - * Retrieves the Session object for the currently logged in session. - * @method current - * @static - * @return {Parse.Promise} A promise that is resolved with the Parse.Session - * object after it has been fetched. If there is no current user, the - * promise will be rejected. - */ - static current(options) { - options = options || {}; - var controller = CoreManager.getSessionController(); - - var sessionOptions = {}; - if (options.hasOwnProperty('useMasterKey')) { - sessionOptions.useMasterKey = options.useMasterKey; - } - return ParseUser.currentAsync().then(user => { - if (!user) { - return ParsePromise.error('There is no current user.'); - } - user.getSessionToken(); - - sessionOptions.sessionToken = user.getSessionToken(); - return controller.getSession(sessionOptions); - }); - } - - /** - * Determines whether the current session token is revocable. - * This method is useful for migrating Express.js or Node.js web apps to - * use revocable sessions. If you are migrating an app that uses the Parse - * SDK in the browser only, please use Parse.User.enableRevocableSession() - * instead, so that sessions can be automatically upgraded. - * @method isCurrentSessionRevocable - * @static - * @return {Boolean} - */ - static isCurrentSessionRevocable() { - var currentUser = ParseUser.current(); - if (currentUser) { - return isRevocableSession(currentUser.getSessionToken() || ''); - } - return false; - } -} - -ParseObject.registerSubclass('_Session', ParseSession); - -var DefaultController = { - getSession(options) { - var RESTController = CoreManager.getRESTController(); - var session = new ParseSession(); - - return RESTController.request('GET', 'sessions/me', {}, options).then(sessionData => { - session._finishFetch(sessionData); - session._setExisted(true); - return session; - }); - } -}; - -CoreManager.setSessionController(DefaultController); \ No newline at end of file diff --git a/lib/react-native/ParseUser.js b/lib/react-native/ParseUser.js deleted file mode 100644 index 129c4cfbb..000000000 --- a/lib/react-native/ParseUser.js +++ /dev/null @@ -1,952 +0,0 @@ -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * - */ - -import CoreManager from './CoreManager'; -import isRevocableSession from './isRevocableSession'; -import ParseError from './ParseError'; -import ParseObject from './ParseObject'; -import ParsePromise from './ParsePromise'; -import ParseSession from './ParseSession'; -import Storage from './Storage'; - -var CURRENT_USER_KEY = 'currentUser'; -var canUseCurrentUser = !CoreManager.get('IS_NODE'); -var currentUserCacheMatchesDisk = false; -var currentUserCache = null; - -var authProviders = {}; - -/** - * @class Parse.User - * @constructor - * - *A Parse.User object is a local representation of a user persisted to the - * Parse cloud. This class is a subclass of a Parse.Object, and retains the - * same functionality of a Parse.Object, but also extends it with various - * user specific methods, like authentication, signing up, and validation of - * uniqueness.
- */ -export default class ParseUser extends ParseObject { - constructor(attributes) { - super('_User'); - if (attributes && typeof attributes === 'object') { - if (!this.set(attributes || {})) { - throw new Error('Can\'t create an invalid Parse User'); - } - } - } - - /** - * Request a revocable session token to replace the older style of token. - * @method _upgradeToRevocableSession - * @param {Object} options A Backbone-style options object. - * @return {Parse.Promise} A promise that is resolved when the replacement - * token has been fetched. - */ - _upgradeToRevocableSession(options) { - options = options || {}; - - var upgradeOptions = {}; - if (options.hasOwnProperty('useMasterKey')) { - upgradeOptions.useMasterKey = options.useMasterKey; - } - - var controller = CoreManager.getUserController(); - return controller.upgradeToRevocableSession(this, upgradeOptions)._thenRunCallbacks(options); - } - - /** - * Unlike in the Android/iOS SDKs, logInWith is unnecessary, since you can - * call linkWith on the user (even if it doesn't exist yet on the server). - * @method _linkWith - */ - _linkWith(provider, options) { - var authType; - if (typeof provider === 'string') { - authType = provider; - provider = authProviders[provider]; - } else { - authType = provider.getAuthType(); - } - if (options && options.hasOwnProperty('authData')) { - var authData = this.get('authData') || {}; - if (typeof authData !== 'object') { - throw new Error('Invalid type: authData field should be an object'); - } - authData[authType] = options.authData; - - var controller = CoreManager.getUserController(); - return controller.linkWith(this, authData)._thenRunCallbacks(options, this); - } else { - var promise = new ParsePromise(); - provider.authenticate({ - success: (provider, result) => { - var opts = {}; - opts.authData = result; - if (options.success) { - opts.success = options.success; - } - if (options.error) { - opts.error = options.error; - } - this._linkWith(provider, opts).then(() => { - promise.resolve(this); - }, error => { - promise.reject(error); - }); - }, - error: (provider, error) => { - if (typeof options.error === 'function') { - options.error(this, error); - } - promise.reject(error); - } - }); - return promise; - } - } - - /** - * Synchronizes auth data for a provider (e.g. puts the access token in the - * right place to be used by the Facebook SDK). - * @method _synchronizeAuthData - */ - _synchronizeAuthData(provider) { - if (!this.isCurrent() || !provider) { - return; - } - var authType; - if (typeof provider === 'string') { - authType = provider; - provider = authProviders[authType]; - } else { - authType = provider.getAuthType(); - } - var authData = this.get('authData'); - if (!provider || !authData || typeof authData !== 'object') { - return; - } - var success = provider.restoreAuthentication(authData[authType]); - if (!success) { - this._unlinkFrom(provider); - } - } - - /** - * Synchronizes authData for all providers. - * @method _synchronizeAllAuthData - */ - _synchronizeAllAuthData() { - var authData = this.get('authData'); - if (typeof authData !== 'object') { - return; - } - - for (var key in authData) { - this._synchronizeAuthData(key); - } - } - - /** - * Removes null values from authData (which exist temporarily for - * unlinking) - * @method _cleanupAuthData - */ - _cleanupAuthData() { - if (!this.isCurrent()) { - return; - } - var authData = this.get('authData'); - if (typeof authData !== 'object') { - return; - } - - for (var key in authData) { - if (!authData[key]) { - delete authData[key]; - } - } - } - - /** - * Unlinks a user from a service. - * @method _unlinkFrom - */ - _unlinkFrom(provider, options) { - if (typeof provider === 'string') { - provider = authProviders[provider]; - } else { - provider.getAuthType(); - } - return this._linkWith(provider, { authData: null }).then(() => { - this._synchronizeAuthData(provider); - return ParsePromise.as(this); - })._thenRunCallbacks(options); - } - - /** - * Checks whether a user is linked to a service. - * @method _isLinked - */ - _isLinked(provider) { - var authType; - if (typeof provider === 'string') { - authType = provider; - } else { - authType = provider.getAuthType(); - } - var authData = this.get('authData') || {}; - if (typeof authData !== 'object') { - return false; - } - return !!authData[authType]; - } - - /** - * Deauthenticates all providers. - * @method _logOutWithAll - */ - _logOutWithAll() { - var authData = this.get('authData'); - if (typeof authData !== 'object') { - return; - } - - for (var key in authData) { - this._logOutWith(key); - } - } - - /** - * Deauthenticates a single provider (e.g. removing access tokens from the - * Facebook SDK). - * @method _logOutWith - */ - _logOutWith(provider) { - if (!this.isCurrent()) { - return; - } - if (typeof provider === 'string') { - provider = authProviders[provider]; - } - if (provider && provider.deauthenticate) { - provider.deauthenticate(); - } - } - - /** - * Class instance method used to maintain specific keys when a fetch occurs. - * Used to ensure that the session token is not lost. - */ - _preserveFieldsOnFetch() { - return { - sessionToken: this.get('sessionToken') - }; - } - - /** - * Returns true ifcurrent
would return this user.
- * @method isCurrent
- * @return {Boolean}
- */
- isCurrent() {
- var current = ParseUser.current();
- return !!current && current.id === this.id;
- }
-
- /**
- * Returns get("username").
- * @method getUsername
- * @return {String}
- */
- getUsername() {
- const username = this.get('username');
- if (username == null || typeof username === 'string') {
- return username;
- }
- return '';
- }
-
- /**
- * Calls set("username", username, options) and returns the result.
- * @method setUsername
- * @param {String} username
- * @param {Object} options A Backbone-style options object.
- * @return {Boolean}
- */
- setUsername(username) {
- // Strip anonymity, even we do not support anonymous user in js SDK, we may
- // encounter anonymous user created by android/iOS in cloud code.
- var authData = this.get('authData');
- if (authData && typeof authData === 'object' && authData.hasOwnProperty('anonymous')) {
- // We need to set anonymous to null instead of deleting it in order to remove it from Parse.
- authData.anonymous = null;
- }
- this.set('username', username);
- }
-
- /**
- * Calls set("password", password, options) and returns the result.
- * @method setPassword
- * @param {String} password
- * @param {Object} options A Backbone-style options object.
- * @return {Boolean}
- */
- setPassword(password) {
- this.set('password', password);
- }
-
- /**
- * Returns get("email").
- * @method getEmail
- * @return {String}
- */
- getEmail() {
- const email = this.get('email');
- if (email == null || typeof email === 'string') {
- return email;
- }
- return '';
- }
-
- /**
- * Calls set("email", email, options) and returns the result.
- * @method setEmail
- * @param {String} email
- * @param {Object} options A Backbone-style options object.
- * @return {Boolean}
- */
- setEmail(email) {
- this.set('email', email);
- }
-
- /**
- * Returns the session token for this user, if the user has been logged in,
- * or if it is the result of a query with the master key. Otherwise, returns
- * undefined.
- * @method getSessionToken
- * @return {String} the session token, or undefined
- */
- getSessionToken() {
- const token = this.get('sessionToken');
- if (token == null || typeof token === 'string') {
- return token;
- }
- return '';
- }
-
- /**
- * Checks whether this user is the current user and has been authenticated.
- * @method authenticated
- * @return (Boolean) whether this user is the current user and is logged in.
- */
- authenticated() {
- var current = ParseUser.current();
- return !!this.get('sessionToken') && !!current && current.id === this.id;
- }
-
- /**
- * Signs up a new user. You should call this instead of save for
- * new Parse.Users. This will create a new Parse.User on the server, and
- * also persist the session on disk so that you can access the user using
- * current
.
- *
- * A username and password must be set before calling signUp.
- * - *Calls options.success or options.error on completion.
- * - * @method signUp - * @param {Object} attrs Extra fields to set on the new user, or null. - * @param {Object} options A Backbone-style options object. - * @return {Parse.Promise} A promise that is fulfilled when the signup - * finishes. - */ - signUp(attrs, options) { - options = options || {}; - - var signupOptions = {}; - if (options.hasOwnProperty('useMasterKey')) { - signupOptions.useMasterKey = options.useMasterKey; - } - if (options.hasOwnProperty('installationId')) { - signupOptions.installationId = options.installationId; - } - - var controller = CoreManager.getUserController(); - return controller.signUp(this, attrs, signupOptions)._thenRunCallbacks(options, this); - } - - /** - * Logs in a Parse.User. On success, this saves the session to disk, - * so you can retrieve the currently logged in user using - *current
.
- *
- * A username and password must be set before calling logIn.
- * - *Calls options.success or options.error on completion.
- * - * @method logIn - * @param {Object} options A Backbone-style options object. - * @return {Parse.Promise} A promise that is fulfilled with the user when - * the login is complete. - */ - logIn(options) { - options = options || {}; - - var loginOptions = {}; - if (options.hasOwnProperty('useMasterKey')) { - loginOptions.useMasterKey = options.useMasterKey; - } - if (options.hasOwnProperty('installationId')) { - loginOptions.installationId = options.installationId; - } - - var controller = CoreManager.getUserController(); - return controller.logIn(this, loginOptions)._thenRunCallbacks(options, this); - } - - /** - * Wrap the default save behavior with functionality to save to local - * storage if this is current user. - */ - save(...args) { - return super.save.apply(this, args).then(() => { - if (this.isCurrent()) { - return CoreManager.getUserController().updateUserOnDisk(this); - } - return this; - }); - } - - /** - * Wrap the default destroy behavior with functionality that logs out - * the current user when it is destroyed - */ - destroy(...args) { - return super.destroy.apply(this, args).then(() => { - if (this.isCurrent()) { - return CoreManager.getUserController().removeUserFromDisk(); - } - return this; - }); - } - - /** - * Wrap the default fetch behavior with functionality to save to local - * storage if this is current user. - */ - fetch(...args) { - return super.fetch.apply(this, args).then(() => { - if (this.isCurrent()) { - return CoreManager.getUserController().updateUserOnDisk(this); - } - return this; - }); - } - - static readOnlyAttributes() { - return ['sessionToken']; - } - - /** - * Adds functionality to the existing Parse.User class - * @method extend - * @param {Object} protoProps A set of properties to add to the prototype - * @param {Object} classProps A set of static properties to add to the class - * @static - * @return {Class} The newly extended Parse.User class - */ - static extend(protoProps, classProps) { - if (protoProps) { - for (var prop in protoProps) { - if (prop !== 'className') { - Object.defineProperty(ParseUser.prototype, prop, { - value: protoProps[prop], - enumerable: false, - writable: true, - configurable: true - }); - } - } - } - - if (classProps) { - for (var prop in classProps) { - if (prop !== 'className') { - Object.defineProperty(ParseUser, prop, { - value: classProps[prop], - enumerable: false, - writable: true, - configurable: true - }); - } - } - } - - return ParseUser; - } - - /** - * Retrieves the currently logged in ParseUser with a valid session, - * either from memory or localStorage, if necessary. - * @method current - * @static - * @return {Parse.Object} The currently logged in Parse.User. - */ - static current() { - if (!canUseCurrentUser) { - return null; - } - var controller = CoreManager.getUserController(); - return controller.currentUser(); - } - - /** - * Retrieves the currently logged in ParseUser from asynchronous Storage. - * @method currentAsync - * @static - * @return {Parse.Promise} A Promise that is resolved with the currently - * logged in Parse User - */ - static currentAsync() { - if (!canUseCurrentUser) { - return ParsePromise.as(null); - } - var controller = CoreManager.getUserController(); - return controller.currentUserAsync(); - } - - /** - * Signs up a new user with a username (or email) and password. - * This will create a new Parse.User on the server, and also persist the - * session in localStorage so that you can access the user using - * {@link #current}. - * - *Calls options.success or options.error on completion.
- * - * @method signUp - * @param {String} username The username (or email) to sign up with. - * @param {String} password The password to sign up with. - * @param {Object} attrs Extra fields to set on the new user. - * @param {Object} options A Backbone-style options object. - * @static - * @return {Parse.Promise} A promise that is fulfilled with the user when - * the signup completes. - */ - static signUp(username, password, attrs, options) { - attrs = attrs || {}; - attrs.username = username; - attrs.password = password; - var user = new ParseUser(attrs); - return user.signUp({}, options); - } - - /** - * Logs in a user with a username (or email) and password. On success, this - * saves the session to disk, so you can retrieve the currently logged in - * user usingcurrent
.
- *
- * Calls options.success or options.error on completion.
- * - * @method logIn - * @param {String} username The username (or email) to log in with. - * @param {String} password The password to log in with. - * @param {Object} options A Backbone-style options object. - * @static - * @return {Parse.Promise} A promise that is fulfilled with the user when - * the login completes. - */ - static logIn(username, password, options) { - if (typeof username !== 'string') { - return ParsePromise.error(new ParseError(ParseError.OTHER_CAUSE, 'Username must be a string.')); - } else if (typeof password !== 'string') { - return ParsePromise.error(new ParseError(ParseError.OTHER_CAUSE, 'Password must be a string.')); - } - var user = new ParseUser(); - user._finishFetch({ username: username, password: password }); - return user.logIn(options); - } - - /** - * Logs in a user with a session token. On success, this saves the session - * to disk, so you can retrieve the currently logged in user using - *current
.
- *
- * Calls options.success or options.error on completion.
- * - * @method become - * @param {String} sessionToken The sessionToken to log in with. - * @param {Object} options A Backbone-style options object. - * @static - * @return {Parse.Promise} A promise that is fulfilled with the user when - * the login completes. - */ - static become(sessionToken, options) { - if (!canUseCurrentUser) { - throw new Error('It is not memory-safe to become a user in a server environment'); - } - options = options || {}; - - var becomeOptions = { - sessionToken: sessionToken - }; - if (options.hasOwnProperty('useMasterKey')) { - becomeOptions.useMasterKey = options.useMasterKey; - } - - var controller = CoreManager.getUserController(); - return controller.become(becomeOptions)._thenRunCallbacks(options); - } - - static logInWith(provider, options) { - return ParseUser._logInWith(provider, options); - } - - /** - * Logs out the currently logged in user session. This will remove the - * session from disk, log out of linked services, and future calls to - *current
will return null
.
- * @method logOut
- * @static
- * @return {Parse.Promise} A promise that is resolved when the session is
- * destroyed on the server.
- */
- static logOut() {
- if (!canUseCurrentUser) {
- throw new Error('There is no current user user on a node.js server environment.');
- }
-
- var controller = CoreManager.getUserController();
- return controller.logOut();
- }
-
- /**
- * Requests a password reset email to be sent to the specified email address
- * associated with the user account. This email allows the user to securely
- * reset their password on the Parse site.
- *
- * Calls options.success or options.error on completion.
- * - * @method requestPasswordReset - * @param {String} email The email address associated with the user that - * forgot their password. - * @param {Object} options A Backbone-style options object. - * @static - */ - static requestPasswordReset(email, options) { - options = options || {}; - - var requestOptions = {}; - if (options.hasOwnProperty('useMasterKey')) { - requestOptions.useMasterKey = options.useMasterKey; - } - - var controller = CoreManager.getUserController(); - return controller.requestPasswordReset(email, requestOptions)._thenRunCallbacks(options); - } - - /** - * Allow someone to define a custom User class without className - * being rewritten to _User. The default behavior is to rewrite - * User to _User for legacy reasons. This allows developers to - * override that behavior. - * - * @method allowCustomUserClass - * @param {Boolean} isAllowed Whether or not to allow custom User class - * @static - */ - static allowCustomUserClass(isAllowed) { - CoreManager.set('PERFORM_USER_REWRITE', !isAllowed); - } - - /** - * Allows a legacy application to start using revocable sessions. If the - * current session token is not revocable, a request will be made for a new, - * revocable session. - * It is not necessary to call this method from cloud code unless you are - * handling user signup or login from the server side. In a cloud code call, - * this function will not attempt to upgrade the current token. - * @method enableRevocableSession - * @param {Object} options A Backbone-style options object. - * @static - * @return {Parse.Promise} A promise that is resolved when the process has - * completed. If a replacement session token is requested, the promise - * will be resolved after a new token has been fetched. - */ - static enableRevocableSession(options) { - options = options || {}; - CoreManager.set('FORCE_REVOCABLE_SESSION', true); - if (canUseCurrentUser) { - var current = ParseUser.current(); - if (current) { - return current._upgradeToRevocableSession(options); - } - } - return ParsePromise.as()._thenRunCallbacks(options); - } - - /** - * Enables the use of become or the current user in a server - * environment. These features are disabled by default, since they depend on - * global objects that are not memory-safe for most servers. - * @method enableUnsafeCurrentUser - * @static - */ - static enableUnsafeCurrentUser() { - canUseCurrentUser = true; - } - - /** - * Disables the use of become or the current user in any environment. - * These features are disabled on servers by default, since they depend on - * global objects that are not memory-safe for most servers. - * @method disableUnsafeCurrentUser - * @static - */ - static disableUnsafeCurrentUser() { - canUseCurrentUser = false; - } - - static _registerAuthenticationProvider(provider) { - authProviders[provider.getAuthType()] = provider; - // Synchronize the current user with the auth provider. - ParseUser.currentAsync().then(current => { - if (current) { - current._synchronizeAuthData(provider.getAuthType()); - } - }); - } - - static _logInWith(provider, options) { - var user = new ParseUser(); - return user._linkWith(provider, options); - } - - static _clearCache() { - currentUserCache = null; - currentUserCacheMatchesDisk = false; - } - - static _setCurrentUserCache(user) { - currentUserCache = user; - } -} - -ParseObject.registerSubclass('_User', ParseUser); - -var DefaultController = { - updateUserOnDisk(user) { - var path = Storage.generatePath(CURRENT_USER_KEY); - var json = user.toJSON(); - json.className = '_User'; - return Storage.setItemAsync(path, JSON.stringify(json)).then(() => { - return user; - }); - }, - - removeUserFromDisk() { - let path = Storage.generatePath(CURRENT_USER_KEY); - currentUserCacheMatchesDisk = true; - currentUserCache = null; - return Storage.removeItemAsync(path); - }, - - setCurrentUser(user) { - currentUserCache = user; - user._cleanupAuthData(); - user._synchronizeAllAuthData(); - return DefaultController.updateUserOnDisk(user); - }, - - currentUser() { - if (currentUserCache) { - return currentUserCache; - } - if (currentUserCacheMatchesDisk) { - return null; - } - if (Storage.async()) { - throw new Error('Cannot call currentUser() when using a platform with an async ' + 'storage system. Call currentUserAsync() instead.'); - } - var path = Storage.generatePath(CURRENT_USER_KEY); - var userData = Storage.getItem(path); - currentUserCacheMatchesDisk = true; - if (!userData) { - currentUserCache = null; - return null; - } - userData = JSON.parse(userData); - if (!userData.className) { - userData.className = '_User'; - } - if (userData._id) { - if (userData.objectId !== userData._id) { - userData.objectId = userData._id; - } - delete userData._id; - } - if (userData._sessionToken) { - userData.sessionToken = userData._sessionToken; - delete userData._sessionToken; - } - var current = ParseObject.fromJSON(userData); - currentUserCache = current; - current._synchronizeAllAuthData(); - return current; - }, - - currentUserAsync() { - if (currentUserCache) { - return ParsePromise.as(currentUserCache); - } - if (currentUserCacheMatchesDisk) { - return ParsePromise.as(null); - } - var path = Storage.generatePath(CURRENT_USER_KEY); - return Storage.getItemAsync(path).then(userData => { - currentUserCacheMatchesDisk = true; - if (!userData) { - currentUserCache = null; - return ParsePromise.as(null); - } - userData = JSON.parse(userData); - if (!userData.className) { - userData.className = '_User'; - } - if (userData._id) { - if (userData.objectId !== userData._id) { - userData.objectId = userData._id; - } - delete userData._id; - } - if (userData._sessionToken) { - userData.sessionToken = userData._sessionToken; - delete userData._sessionToken; - } - var current = ParseObject.fromJSON(userData); - currentUserCache = current; - current._synchronizeAllAuthData(); - return ParsePromise.as(current); - }); - }, - - signUp(user, attrs, options) { - var username = attrs && attrs.username || user.get('username'); - var password = attrs && attrs.password || user.get('password'); - - if (!username || !username.length) { - return ParsePromise.error(new ParseError(ParseError.OTHER_CAUSE, 'Cannot sign up user with an empty name.')); - } - if (!password || !password.length) { - return ParsePromise.error(new ParseError(ParseError.OTHER_CAUSE, 'Cannot sign up user with an empty password.')); - } - - return user.save(attrs, options).then(() => { - // Clear the password field - user._finishFetch({ password: undefined }); - - if (canUseCurrentUser) { - return DefaultController.setCurrentUser(user); - } - return user; - }); - }, - - logIn(user, options) { - var RESTController = CoreManager.getRESTController(); - var stateController = CoreManager.getObjectStateController(); - var auth = { - username: user.get('username'), - password: user.get('password') - }; - return RESTController.request('GET', 'login', auth, options).then((response, status) => { - user._migrateId(response.objectId); - user._setExisted(true); - stateController.setPendingOp(user._getStateIdentifier(), 'username', undefined); - stateController.setPendingOp(user._getStateIdentifier(), 'password', undefined); - response.password = undefined; - user._finishFetch(response); - if (!canUseCurrentUser) { - // We can't set the current user, so just return the one we logged in - return ParsePromise.as(user); - } - return DefaultController.setCurrentUser(user); - }); - }, - - become(options) { - var user = new ParseUser(); - var RESTController = CoreManager.getRESTController(); - return RESTController.request('GET', 'users/me', {}, options).then((response, status) => { - user._finishFetch(response); - user._setExisted(true); - return DefaultController.setCurrentUser(user); - }); - }, - - logOut() { - return DefaultController.currentUserAsync().then(currentUser => { - var path = Storage.generatePath(CURRENT_USER_KEY); - var promise = Storage.removeItemAsync(path); - var RESTController = CoreManager.getRESTController(); - if (currentUser !== null) { - var currentSession = currentUser.getSessionToken(); - if (currentSession && isRevocableSession(currentSession)) { - promise = promise.then(() => { - return RESTController.request('POST', 'logout', {}, { sessionToken: currentSession }); - }); - } - currentUser._logOutWithAll(); - currentUser._finishFetch({ sessionToken: undefined }); - } - currentUserCacheMatchesDisk = true; - currentUserCache = null; - - return promise; - }); - }, - - requestPasswordReset(email, options) { - var RESTController = CoreManager.getRESTController(); - return RESTController.request('POST', 'requestPasswordReset', { email: email }, options); - }, - - upgradeToRevocableSession(user, options) { - var token = user.getSessionToken(); - if (!token) { - return ParsePromise.error(new ParseError(ParseError.SESSION_MISSING, 'Cannot upgrade a user with no session token')); - } - - options.sessionToken = token; - - var RESTController = CoreManager.getRESTController(); - return RESTController.request('POST', 'upgradeToRevocableSession', {}, options).then(result => { - var session = new ParseSession(); - session._finishFetch(result); - user._finishFetch({ sessionToken: session.getSessionToken() }); - if (user.isCurrent()) { - return DefaultController.setCurrentUser(user); - } - return ParsePromise.as(user); - }); - }, - - linkWith(user, authData) { - return user.save({ authData }).then(() => { - if (canUseCurrentUser) { - return DefaultController.setCurrentUser(user); - } - return user; - }); - } -}; - -CoreManager.setUserController(DefaultController); \ No newline at end of file diff --git a/lib/react-native/Push.js b/lib/react-native/Push.js deleted file mode 100644 index 2710abe4d..000000000 --- a/lib/react-native/Push.js +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * - */ - -import CoreManager from './CoreManager'; -import ParseQuery from './ParseQuery'; - -/** - * Contains functions to deal with Push in Parse. - * @class Parse.Push - * @static - */ - -/** - * Sends a push notification. - * @method send - * @param {Object} data - The data of the push notification. Valid fields - * are: - *