UNPKG

maxleap-react-native

Version:
712 lines (642 loc) 24.4 kB
'use strict'; var _ = require('underscore'); import React, { NativeModules } from "react-native"; const MaxLeapCore = NativeModules.MaxLeapCore; /*global _: false, $: false, localStorage: false, process: true, XMLHttpRequest: false, XDomainRequest: false, exports: false, require: false */ module.exports = function (ML) { /** * Contains all ML API classes and functions. * @name ML * @namespace * * Contains all ML API classes and functions. */ // If jQuery or Zepto has been included, grab a reference to it. if (typeof($) !== "undefined") { ML.$ = $; } // Helpers // ------- // Shared empty constructor function to aid in prototype-chain creation. var EmptyConstructor = function () { }; // Helper function to correctly set up the prototype chain, for subclasses. // Similar to `goog.inherits`, but uses a hash of prototype properties and // class properties to be extended. var inherits = function (parent, protoProps, staticProps) { var child; // The constructor function for the new subclass is either defined by you // (the "constructor" property in your `extend` definition), or defaulted // by us to simply call the parent's constructor. if (protoProps && protoProps.hasOwnProperty('constructor')) { child = protoProps.constructor; } else { /** @ignore */ child = function () { parent.apply(this, arguments); }; } // Inherit class (static) properties from parent. ML._.extend(child, parent); // Set the prototype chain to inherit from `parent`, without calling // `parent`'s constructor function. EmptyConstructor.prototype = parent.prototype; child.prototype = new EmptyConstructor(); // Add prototype properties (instance properties) to the subclass, // if supplied. if (protoProps) { ML._.extend(child.prototype, protoProps); } // Add static properties to the constructor function, if supplied. if (staticProps) { ML._.extend(child, staticProps); } // Correctly set child's `prototype.constructor`. child.prototype.constructor = child; // Set a convenience property in case the parent's prototype is // needed later. child.__super__ = parent.prototype; return child; }; // Set the server for ML to talk to. ML.serverURL = "https://api.leap.as"; // Check whether we are running in Node.js. if (typeof(process) !== "undefined" && process.versions && process.versions.node) { ML._isNode = true; } /** * Call this method first to set up your authentication tokens for ML. * You can get your keys from the Data Browser on maxleap.com. * @param {String} applicationId Your ML Application ID. * @param {String} applicationKey Your ML JavaScript Key. * @param {String} masterKey (optional) Your LeapCloud Master Key. (Node.js only!). */ // ML.initialize = function (applicationId, applicationKey, masterKey) { // if (masterKey) { // throw new Error("ML.initialize() was passed a Master Key, which is only " + // "allowed from within Node.js."); // } // ML._initialize(applicationId, applicationKey, masterKey); // }; /** * Call this method first to set up authentication tokens for ML. * This method is for ML's own private use. * @param {String} applicationId Your ML Application ID. * @param {String} applicationKey Your ML Application Key */ // ML._initialize = function (applicationId, applicationKey, masterKey) { // if (ML.applicationId !== undefined && // applicationId !== ML.applicationId && // applicationKey !== ML.applicationKey && // masterKey !== ML.masterKey) { // console.warn('LeapCloud SDK is already initialized, please don\'t reinitialize it.'); // } // ML.applicationId = applicationId; // ML.applicationKey = applicationKey; // ML.masterKey = masterKey; // ML._useMasterKey = false; // }; /** * Call this method to set production environment variable. * @param {Boolean} production True is production environment,and * it's true by default. */ ML.setProduction = function (production) { if (!ML._isNullOrUndefined(production)) { //make sure it's a number production = production ? 1 : 0; } //default is 1 ML.applicationProduction = ML._isNullOrUndefined(production) ? 1 : production; }; // If we're running in node.js, allow using the master key. if (ML._isNode) { ML.initialize = ML._initialize; ML.Cloud = ML.Cloud || {}; /** * Switches the LeapCloud SDK to using the Master key. The Master key grants * priveleged access to the data in LeapCloud and can be used to bypass ACLs and * other restrictions that are applied to the client SDKs. * <p><strong><em>Available in Cloud Code and Node.js only.</em></strong> * </p> */ ML.Cloud.useMasterKey = function () { ML._useMasterKey = true; }; } /** *Use china maxleap API service:https://maxleap.cn */ ML.useLeapCloudCN = function () { ML.serverURL = "https://maxleap.cn"; }; /** *Use USA maxleap API service:https://maxleap.com */ ML.useLeapCloudUS = function () { ML.serverURL = "https://maxleap.com"; }; /** * Returns prefix for localStorage keys used by this instance of ML. * @param {String} path The relative suffix to append to it. * null or undefined is treated as the empty string. * @return {String} The full key name. */ ML._getMLPath = function (path) { // if (!ML.applicationId) { // throw "You need to call ML.initialize before using ML."; // } if (!path) { path = ""; } // if (!ML._.isString(path)) { // throw "Tried to get a localStorage path that wasn't a String."; // } if (path[0] === "/") { path = path.substring(1); } return "ML/" + ML.applicationId + "/" + path; }; /** * no use * Returns the unique string for this app on this machine. * Gets reset when localStorage is cleared. */ ML._installationId = null; ML._getInstallationId = function () { // See if it's cached in RAM. if (ML._installationId) { return ML._installationId; } var path = ML._getMLPath("installationId"); // ML._installationId = ML.localStorage.getItem(path); if (!ML._installationId || ML._installationId === "") { // It wasn't in localStorage, so create a new one. var hexOctet = function () { return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1); }; ML._installationId = ( hexOctet() + hexOctet() + "-" + hexOctet() + "-" + hexOctet() + "-" + hexOctet() + "-" + hexOctet() + hexOctet() + hexOctet()); // ML.localStorage.setItem(path, ML._installationId); } // console.log(ML._installationId); return ML._installationId; }; ML._parseDate = function (iso8601) { var regexp = new RegExp( "^([0-9]{1,4})-([0-9]{1,2})-([0-9]{1,2})" + "T" + "([0-9]{1,2}):([0-9]{1,2}):([0-9]{1,2})" + "(.([0-9]+))?" + "Z$"); var match = regexp.exec(iso8601); if (!match) { return null; } var year = match[1] || 0; var month = (match[2] || 1) - 1; var day = match[3] || 0; var hour = match[4] || 0; var minute = match[5] || 0; var second = match[6] || 0; var milli = match[8] || 0; return new Date(Date.UTC(year, month, day, hour, minute, second, milli)); }; // ML._ajaxIE8 = function (method, url, data) { // var promise = new ML.Promise(); // var xdr = new XDomainRequest(); // xdr.onload = function () { // var response; // try { // response = JSON.parse(xdr.responseText); // } catch (e) { // promise.reject(e); // } // if (response) { // promise.resolve(response); // } // }; // xdr.onerror = xdr.ontimeout = function () { // // Let's fake a real error message. // var fakeResponse = { // responseText: JSON.stringify({ // code: ML.Error.X_DOMAIN_REQUEST, // error: "IE's XDomainRequest does not supply error info." // }) // }; // promise.reject(xdr); // }; // xdr.onprogress = function () { // }; // xdr.open(method, url); // xdr.send(data); // return promise; // }; // ML._useXDomainRequest = function () { // if (typeof(XDomainRequest) !== "undefined") { // // We're in IE 8+. // if ('withCredentials' in new XMLHttpRequest()) { // // We're in IE 10+. // return false; // } // return true; // } // return false; // }; ML._ajax = function (method, url, data, success, error, headers) { var options = { success: success, error: error }; // if (ML._useXDomainRequest()) { // return ML._ajaxIE8(method, url, data)._thenRunCallbacks(options); // } var promise = new ML.Promise(); var handled = false; var headers2 = headers || {}; var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function () { if (xhr.readyState === 4) { if (handled) { return; } handled = true; if (xhr.status >= 200 && xhr.status < 300) { var response; try { response = JSON.parse(xhr.responseText); } catch (e) { //对于一些请求(如delete files),服务器不返回response,只能通过xhr.status判断请求结果 promise.resolve(xhr.status); } if (response) { promise.resolve(response, xhr.status, xhr); } } else { promise.reject(xhr); } } }; xhr.open(method, url, true); // xhr.setRequestHeader("X-ML-AppId", "ML.applicationId"); // xhr.setRequestHeader("X-ML-APIKey", "ML.applicationKey"); //xhr.setRequestHeader("X-LAS-Fid", "zcf-7dd01734-8272-40de-89e7-dbb199f04bf9.txt"); // if (ML._isNode) { // // Add a special user agent just for request from node.js. // xhr.setRequestHeader("User-Agent", // "ML/" + ML.VERSION + // " (NodeJS " + process.versions.node + ")"); // } // xhr.send(data); MaxLeapCore.getHeader().then((entries)=> { for (let item in entries) { xhr.setRequestHeader(item, entries[item]); } for (let item in headers2){ xhr.setRequestHeader(item, headers2[item]); } if (method != 'GET') { xhr.send(data); } else { xhr.send(); } }); return promise._thenRunCallbacks(options); }; // A self-propagating extend function. ML._extend = function (protoProps, classProps) { var child = inherits(this, protoProps, classProps); child.extend = this.extend; return child; }; /** * route is classes, users, login, etc. * objectId is null if there is no associated objectId. * method is the http method for the REST API. * dataObject is the payload as an object, or null if there is none. * headers is extra http headers. * @ignore */ ML._request = function (route, className, objectId, method, dataObject, headers) { // if (!ML.applicationId) { // throw "You must specify your applicationId using ML.initialize"; // } // // if (!ML.applicationKey && !ML.masterKey) { // throw "You must specify a key using ML.initialize"; // } if (route !== "batch" && route !== "classes" && route !== "files" && route !== "functions" && route !== "login" && route !== "marketing/push/msg" && route !== "search/select" && route !== "requestPasswordReset" && route !== "requestEmailVerify" && route !== "requestPasswordResetBySmsCode" && route !== "resetPasswordBySmsCode" && route !== "requestMobilePhoneVerify" && route !== "requestLoginSmsCode" && route !== "loginByMobilePhone" && route !== "verifyMobilePhone" && route !== "requestSmsCode" && route !== "verifySmsCode" && route !== "users" && route !== 'updatePassword' && route !== "usersByMobilePhone" && route !== "cloudQuery" && route !== "qiniu" && route !== "statuses" && route !== "bigquery" && route !== 'search/select' && route !== 'subscribe/statuses/count' && route !== 'subscribe/statuses' && !(/users\/[^\/]+\/updatePassword/.test(route)) && !(/users\/[^\/]+\/friendship\/[^\/]+/.test(route))) { throw "Bad route: '" + route + "'."; } var url = ML.serverURL; if (url.charAt(url.length - 1) !== "/") { url += "/"; } url += "2.0/" + route; if (className) { url += "/" + className; } if (objectId) { url += "/" + objectId; } if ((route === 'users' || route === 'classes') && dataObject && dataObject._fetchWhenSave) { delete dataObject._fetchWhenSave; url += '?new=true'; } headers = headers || {}; var data = dataObject; var currentUser = ML.User._currentUser; if (currentUser && currentUser._sessionToken) { headers['X-ML-Session-Token'] = currentUser._sessionToken; } if (!data['contentType']) { dataObject = ML._.clone(dataObject || {}); if (method !== "POST") { dataObject._method = method; } dataObject._ApplicationId = ML.applicationId; dataObject._ApplicationKey = ML.applicationKey; if (!ML._isNullOrUndefined(ML.applicationProduction)) { dataObject._ApplicationProduction = ML.applicationProduction; } // if (ML._useMasterKey) // dataObject._MasterKey = ML.masterKey; // dataObject._ClientVersion = ML.VERSION; // dataObject._InstallationId = ML._getInstallationId(); // Pass the session token on every request. var currentUser = ML.User._currentUser; if (currentUser && currentUser._sessionToken) { dataObject._SessionToken = currentUser._sessionToken; } data = JSON.stringify(dataObject); headers['Content-Type'] = 'application/json'; } else { headers['Content-Type'] = data['contentType']; } return ML._ajax(method, url, data, null, null, headers).then(null, function (response) { // Transform the error into an instance of ML.Error by trying to parse // the error string as JSON. var error; if (response && response.responseText) { try { var errorJSON = JSON.parse(response.responseText); if (errorJSON) { error = new ML.Error(errorJSON.errorCode, errorJSON.errorMessage); } } catch (e) { // If we fail to parse the error text, that's okay. } } error = error || new ML.Error(-1, response.responseText); // By explicitly returning a rejected Promise, this will work with // either jQuery or Promises/A semantics. return ML.Promise.error(error); }); }; // Helper function to get a value from a Backbone object as a property // or as a function. ML._getValue = function (object, prop) { if (!(object && object[prop])) { return null; } return ML._.isFunction(object[prop]) ? object[prop]() : object[prop]; }; /** * Converts a value in a ML Object into the appropriate representation. * This is the JS equivalent of Java's ML.maybeReferenceAndEncode(Object) * if seenObjects is falsey. Otherwise any ML.Objects not in * seenObjects will be fully embedded rather than encoded * as a pointer. This array will be used to prevent going into an infinite * loop because we have circular references. If <seenObjects> * is set, then none of the ML Objects that are serialized can be dirty. */ ML._encode = function (value, seenObjects, disallowObjects) { var _ = ML._; if (value instanceof ML.Object) { if (disallowObjects) { throw "ML.Objects not allowed here"; } if (!seenObjects || _.include(seenObjects, value) || !value._hasData) { return value._toPointer(); } if (!value.dirty()) { seenObjects = seenObjects.concat(value); return ML._encode(value._toFullJSON(seenObjects), seenObjects, disallowObjects); } throw "Tried to save an object with a pointer to a new, unsaved object."; } if (_.isDate(value)) { return {"__type": "Date", "iso": value.toJSON()}; } if (value instanceof ML.GeoPoint) { return value.toJSON(); } if (_.isArray(value)) { return _.map(value, function (x) { return ML._encode(x, seenObjects, disallowObjects); }); } if (_.isRegExp(value)) { return value.source; } if (value instanceof ML.Relation) { return value.toJSON(); } if (value instanceof ML.Op) { return value.toJSON(); } if (value instanceof ML.File) { if (!value.url() && !value.id) { throw "Tried to save an object containing an unsaved file."; } return { __type: "File", id: value.id, name: value.name(), url: value.url() }; } if (_.isObject(value)) { var output = {}; ML._objectEach(value, function (v, k) { output[k] = ML._encode(v, seenObjects, disallowObjects); }); return output; } return value; }; /** * The inverse function of ML._encode. * TODO: make decode not mutate value. */ ML._decode = function (key, value) { var _ = ML._; if (!_.isObject(value)) { return value; } if (_.isArray(value)) { ML._arrayEach(value, function (v, k) { value[k] = ML._decode(k, v); }); return value; } if (value instanceof ML.Object) { return value; } if (value instanceof ML.File) { return value; } if (value instanceof ML.Op) { return value; } if (value.__op) { return ML.Op._decode(value); } if (value.__type === "Pointer") { var className = value.className; var pointer = ML.Object._create(className); if (value.createdAt) { delete value.__type; delete value.className; pointer._finishFetch(value, true); } else { pointer._finishFetch({objectId: value.objectId}, false); } return pointer; } if (value.__type === "Object") { // It's an Object included in a query result. var className = value.className; delete value.__type; delete value.className; var object = ML.Object._create(className); object._finishFetch(value, true); return object; } if (value.__type === "Date") { return ML._parseDate(value.iso); } if (value.__type === "GeoPoint") { return new ML.GeoPoint({ latitude: value.latitude, longitude: value.longitude }); } if (value.__type === "Relation") { var relation = new ML.Relation(null, key); relation.targetClassName = value.className; return relation; } if (value.__type === "File") { var file = new ML.File(value.name); file._metaData = value.metaData || {}; file._url = value.url; file.id = value.objectId; return file; } ML._objectEach(value, function (v, k) { value[k] = ML._decode(k, v); }); return value; }; ML._arrayEach = ML._.each; /** * Does a deep traversal of every item in object, calling func on every one. * @param {Object} object The object or array to traverse deeply. * @param {Function} func The function to call for every item. It will * be passed the item as an argument. If it returns a truthy value, that * value will replace the item in its parent container. * @returns {} the result of calling func on the top-level object itself. */ ML._traverse = function (object, func, seen) { if (object instanceof ML.Object) { seen = seen || []; if (ML._.indexOf(seen, object) >= 0) { // We've already visited this object in this call. return; } seen.push(object); ML._traverse(object.attributes, func, seen); return func(object); } if (object instanceof ML.Relation || object instanceof ML.File) { // Nothing needs to be done, but we don't want to recurse into the // object's parent infinitely, so we catch this case. return func(object); } if (ML._.isArray(object)) { ML._.each(object, function (child, index) { var newChild = ML._traverse(child, func, seen); if (newChild) { object[index] = newChild; } }); return func(object); } if (ML._.isObject(object)) { ML._each(object, function (child, key) { var newChild = ML._traverse(child, func, seen); if (newChild) { object[key] = newChild; } }); return func(object); } return func(object); }; /** * This is like _.each, except: * * it doesn't work for so-called array-like objects, * * it does work for dictionaries with a "length" attribute. */ ML._objectEach = ML._each = function (obj, callback) { var _ = ML._; if (_.isObject(obj)) { _.each(_.keys(obj), function (key) { callback(obj[key], key); }); } else { _.each(obj, callback); } }; // Helper function to check null or undefined. ML._isNullOrUndefined = function (x) { return ML._.isNull(x) || ML._.isUndefined(x); }; };