Skip to content

Instantly share code, notes, and snippets.

@cr0ybot
Created May 11, 2018 15:56
Show Gist options
  • Save cr0ybot/001c5aafcfd35251304961670287ee21 to your computer and use it in GitHub Desktop.
Save cr0ybot/001c5aafcfd35251304961670287ee21 to your computer and use it in GitHub Desktop.
Parse JS SDK modifications for Titanium SDK
{
"global": {
"parse": {
"applicationId": "com.example.parseAppID",
"javascriptKey": "XXXX",
"clientKey": "YYYY",
"restAPIKey": "ZZZZ",
"serverURL": "https://example.com/parse/"
}
}
}
/**
* File: lib/ti.parse.js
*
* Parse JS SDK modifications for Titanium
*
* Tested with:
* Parse JS SDK 1.11.1
* Titanium SDK 7.1.1.GA
*
* Based on https://github.com/aaronksaunders/TiParseTestApp/blob/master/app/lib/ti.parse_mine.js
*
* @author Cory Hughart
*
* @requires parse.js
* @requires websocket.js
*/
/**
* TiParse Parse wrapper
*
* This object becomes your stand-in for the Parse object
*
* Usage:
* var tiParse = require('ti.parse');
* tiParse.Foo();
*/
var TiParse = function(options) {
this.options = (typeof options == 'object') ? _.extendOwn({}, Alloy.CFG.parse, options) : Alloy.CFG.parse;
// Define global WebSocket object for Parse to find with _getWebSocketImplementation()
WebSocket = require('websocket');
// Download parse.js from https://github.com/parse-community/Parse-SDK-JS/releases
var Parse = require('parse');
Parse._ = _.noConflict();
/**
* Overwrite Parse local storage so it works in Titanium
*/
var StorageController = {
async: 0,
getItem : function(key) {
return Ti.App.Properties.getObject(this.fixKey(key));
},
setItem : function(key, value) {
return Ti.App.Properties.setObject(this.fixKey(key), value);
},
removeItem : function(key, value) {
return Ti.App.Properties.removeProperty(this.fixKey(key));
},
/**
* Fix Parse Keys.
* Parse uses a Key containing slashes "/"; this is invalid for
* Titanium Android. We'll replace those slashes with underscores "_".
*/
fixKey : function(key) {
return key.split("/").join("_");
}
};
Parse.CoreManager.setStorageController(StorageController);
/**
* Save Eventually
* https://github.com/francimedia/parse-js-local-storage
*/
Parse.saveEventually = {
localStorageKey: "Parse.saveEventually.Queue",
initialize: function () {
},
save: function (objectType, object) {
this.addToQueue('save', objectType, object);
},
addToQueue: function (action, objectType, object) {
var queueData = this.getQueue();
// create queueId to avoid duplicates. Maintain previously saved data.
var queueId = ([objectType, object.id, object.get('hash')]).join('_');
var i = this.queueItemExists(queueData, queueId);
if (i > -1) {
for (var prop in queueData[i].data) {
if (object.get(prop) == 'undefined') {
object.set(prop, queueData[i].data[prop]);
}
}
}
else {
i = queueData.length;
}
queueData[i] = {
queueId: queueId,
type: objectType,
action: action,
id: object.id,
hash: object.get('hash'),
createdAt: new Date(),
data: object
};
this.setQueue(queueData);
},
getQueue: function () {
var q = Parse.getStorageController().getItem(this.localStorageKey) || null;
if(q && (typeof JSON.parse(q) == 'object'))
return JSON.parse(q);
else
return [];
},
setQueue: function (queueData) {
Parse.getStorageController().setItem(this.localStorageKey, JSON.stringify(queueData));
},
clearQueue: function () {
Parse.getStorageController().setItem(this.localStorageKey, JSON.stringify([]));
},
queueItemExists: function(queueData, queueId) {
for (var i = 0; i < queueData.length; i++) {
if(queueData[i].queueId == queueId) {
return i;
}
}
return -1;
},
countQueue: function(){
return this.getQueue().length;
},
sendQueue: function () {
var queueData = this.getQueue();
if(queueData.length < 1)
return false;
for (var i = 0; i < queueData.length; i++) {
var myObjectType = Parse.Object.extend(queueData[i].type);
// if object has a parse data id, update existing object
if (queueData[i].id) {
this.reprocess.byId(myObjectType, queueData[i]);
}
// if object has no id but a unique identifier, look for existing object, update or create new
else if (queueData[i].hash) {
this.reprocess.byHash(myObjectType, queueData[i]);
}
// else create a new object
else {
this.reprocess.create(myObjectType, queueData[i]);
}
}
return true;
// empty queue - 2do: verify queue has been sent
// this.clearQueue();
},
sendQueueCallback: function (myObject, queueObject) {
switch (queueObject.action) {
case 'save':
// queued update was overwritten by other request > do not save
if (typeof myObject.updatedAt != 'undefined' && myObject.updatedAt > new Date(queueObject.createdAt)) {
return false;
}
myObject.save(queueObject.data, {
success: function (object) {
console.log(object);
},
error: function (model, error) {
console.log(error);
}
});
break;
case 'delete':
// 2do: code to delete queued objects
break;
}
},
reprocess: {
create: function (myObjectType, queueObject) {
var myObject = new myObjectType();
Parse.saveEventually.sendQueueCallback(myObject, queueObject);
},
byId: function (myObjectType, queueObject) {
var query = new Parse.Query(myObjectType);
query.get(queueObject.id, {
success: function (myObject) {
// The object was retrieved successfully.
Parse.saveEventually.sendQueueCallback(myObject, queueObject);
},
error: function (myObject, error) {
// The object was not retrieved successfully.
// error is a Parse.Error with an error code and description.
}
});
},
byHash: function (myObjectType, queueObject) {
var query = new Parse.Query(myObjectType);
query.equalTo("hash", queueObject.hash);
query.find({
success: function (results) {
// The object was retrieved successfully.
if(results.length > 0) {
Parse.saveEventually.sendQueueCallback(results[0], queueObject);
}
// The object was not found, create a new one
else {
Parse.saveEventually.reprocess.create(myObjectType, queueObject);
}
},
error: function (myObject, error) {
// The object was not retrieved successfully.
// error is a Parse.Error with an error code and description.
}
});
}
}
};
/**
* Overwrite Parse XHR Client to use Titanium HTTPClient
*
* Titanium XHR clients must be created with the createHTTPClient method,
* so we replace the Parse XHR object with this function that returns the
* created Titanium HTTPClient.
*/
var XHR = function() {
return Ti.Network.createHTTPClient({
timeout : 5e3,
onload: function() {
Ti.API.debug('Parse XHR response: '+this.responseText);
},
onerror : function(e) {
Ti.API.error('Parse XHR error: '+this.responseText);
}
});
};
Parse.CoreManager.getRESTController()._setXHR(XHR);
/**
* Enter appropriate parameters for initializing Parse
*
* options.applicationId, options.javascriptKey, options.serverURL
*/
Parse.initialize(this.options.applicationId, this.options.javascriptKey);
Parse.serverURL = this.options.serverURL;
/**
* Parse LiveQuery monitor
*/
Parse.LiveQuery.on('open', function(e) {
Ti.API.debug('Parse LiveQuery socket established');
});
Parse.LiveQuery.on('close', function(e) {
Ti.API.debug('Parse LiveQuery socket closed');
});
Parse.LiveQuery.on('error', function(e) {
Ti.API.debug('Parse LiveQuery error: '+JSON.stringify(e));
});
/**
* Add method to send data with the Parse REST API if needed.
*
* @param {string} method GET or POST
* @param {string} endpoint API endpoint with no slashes (ie 'installations')
* @param {object} data Data to send
* @param {function} onload Callback for success
* @param {function} onerror Callback for error
*/
Parse.restAPI = function(method, endpoint, data, onload, onerror) {
var url = Alloy.CFG.parse.serverURL+endpoint;
if (typeof data === 'undefined' || data == null) data = {};
if (typeof data !== 'object') data = {'data': data};
if (typeof onload !== 'function') {
onload = function(e) {
Ti.API.debug(JSON.stringify(e));
};
}
if (typeof onerror !== 'function') {
onerror = function(e) {
Ti.API.debug('request failed: '+url);
Ti.API.debug("STATUS: " + this.status);
Ti.API.debug("TEXT: " + this.responseText);
Ti.API.debug("ERROR: " + e.error);
};
}
var xhr = Ti.Network.createHTTPClient({
onload : function(e) {
onload(e);
},
onerror : function(e) {
onerror(e);
},
timeout : 10000
});
xhr.open(method, url, true);
xhr.setRequestHeader('X-Parse-Application-Id', Alloy.CFG.parse.applicationId);
//xhr.setRequestHeader('X-Parse-REST-API-Key', Alloy.CFG.parse.restAPIKey);
xhr.setRequestHeader('X-Parse-Client-Key', Alloy.CFG.parse.clientKey);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send(JSON.stringify(data));
};
// Return our modified Parse object
return Parse;
};
module.exports = new TiParse();
/**
* File: lib/websocket.js
*
* Parse-compatible WebSocket implementation with tiws
* @requires net.iamyellow.tiws https://github.com/omorandi/tiws
*/
var WebSocket = function(url) {
Ti.API.debug('Creating WebSocket for '+url);
var self = this;
var TiWS = require('net.iamyellow.tiws');
this.ws = TiWS.createWS();
// Default listeners with debug messages
this.ws.addEventListener('open', function() {
Ti.API.debug('WebSocket open');
self.onopen();
});
this.ws.addEventListener('message', function(e) {
Ti.API.debug('WebSocket message: '+JSON.stringify(e));
self.onmessage(e);
});
this.ws.addEventListener('close', function(e) {
Ti.API.debug('WebSocket closed: '+JSON.stringify(e));
self.onclose(e);
});
this.ws.addEventListener('error', function(error) {
Ti.API.debug('WebSocket error: '+JSON.stringify(error));
self.onerror(error);
});
this.ws.open(url);
Ti.API.debug('WebSocket created');
};
// These should be overridden when creating an instance
WebSocket.prototype.onopen = function() {};
WebSocket.prototype.onmessage = function(e) {};
WebSocket.prototype.onclose = function(e) {};
WebSocket.prototype.onerror = function(error) {};
WebSocket.prototype.send = function(string) {
Ti.API.debug('WebSocket sending: '+string);
this.ws.send(string);
};
WebSocket.prototype.close = function() {
Ti.API.debug('Websocket closing');
this.ws.close();
};
module.exports = WebSocket;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment