From bf9fcd29e47c0088c96a652ca75da35a18e65c88 Mon Sep 17 00:00:00 2001 From: Xelio Date: Fri, 3 May 2013 13:07:16 +0800 Subject: [PATCH] New Plugin: Sync Use Google Realtime API, store key value pair in CollaborativeMap. Allow plugin to sync data between client. --- external/gapi.js | 7 + plugins/sync.user.js | 536 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 543 insertions(+) create mode 100644 external/gapi.js create mode 100644 plugins/sync.user.js diff --git a/external/gapi.js b/external/gapi.js new file mode 100644 index 00000000..32d3e200 --- /dev/null +++ b/external/gapi.js @@ -0,0 +1,7 @@ +var gapi=window.gapi=window.gapi||{};gapi._bs=new Date().getTime();(function(){var f=null,g=encodeURIComponent,k=window,m=decodeURIComponent,n="push",r="test",t="shift",u="replace",y="length",B="split",C="join";var D=k,E=document,aa=D.location,ba=function(){},ca=/\[native code\]/,G=function(a,b,c){return a[b]=a[b]||c},da=function(a){for(var b=0;bda.call(b,e)&&c[n](e)}return c},sa=function(a){"loading"!=E.readyState?Z(a):E.write("<"+X+' src="'+encodeURI(a)+'">")},Z=function(a){var b=E.createElement(X);b.setAttribute("src",a);b.async="true";(a=E.getElementsByTagName(X)[0])?a.parentNode.insertBefore(b,a):(E.head||E.body||E.documentElement).appendChild(b)},ta=function(a,b){var c=b&&b._c;if(c)for(var d=0;d 0) { + this.timer = setTimeout(function() {_this.initializeWorker()}, 10000); + } +} +//// end registerdPluginsFields + + + + +//// Authorizer +// authorize user google account and create a folder 'IITC-SYNC-DATA' to store Realtime document +window.plugin.sync.Authorizer = function(options) { + this.authCallback = options['authCallback']; + this.folderId = null; + this.authorize = this.authorize.bind(this); +} + +window.plugin.sync.Authorizer.prototype.CLIENT_ID = '686674438052.apps.googleusercontent.com'; +window.plugin.sync.Authorizer.prototype.SCOPES = ['https://www.googleapis.com/auth/drive.file', 'https://www.googleapis.com/auth/drive.metadata.readonly']; + +window.plugin.sync.Authorizer.prototype.isAuthed = function() { + return this.folderId !== null; +} + +window.plugin.sync.Authorizer.prototype.addAuthCallback = function(callback) { + if(typeof(this.authCallback) === 'function') this.authCallback = [this.authCallback]; + this.authCallback.push(callback); +} + +window.plugin.sync.Authorizer.prototype.authComplete = function() { + if(this.authCallback) { + if(typeof(this.authCallback) === 'function') this.authCallback(); + if(this.authCallback instanceof Array && this.authCallback.length > 0) { + $.each(this.authCallback, function(ind, func) { + func(); + }); + } + } +} + +window.plugin.sync.Authorizer.prototype.initFolder = function(callback) { + var queryOption, createOption, searchCallBack, _this; + _this = this; + + queryOption = 'title = "IITC-SYNC-DATA" and mimeType = "application/vnd.google-apps.folder" and trashed = false'; + createOption = {'convert': 'false' + , 'ocr': 'false' + , 'resource': {'title': 'IITC-SYNC-DATA', + 'description': 'Store IITC sync data', + 'mimeType': 'application/vnd.google-apps.folder' + } + }; + searchCallBack = function(resp) { + if(resp.items) { + _this.folderId = resp.items[0].id; + if(callback) callback(); + _this.authComplete(); + } else { + plugin.sync.createFileOrFolder(createOption, function(resp) { + if (resp.id) { + _this.folderId = resp.id; + if(callback) callback(); + } else { + console.log('Plugin Sync: Could not create folder "IITC-SYNC-DATA"'); + } + _this.authComplete(); + }); + } + }; + + plugin.sync.searchFileOrFolder(queryOption, searchCallBack); +} + +window.plugin.sync.Authorizer.prototype.authorize = function(popup, callback) { + var handleAuthResult, _this; + _this = this; + + handleAuthResult = function(authResult) { + if(authResult && !authResult.error) { + _this.initFolder(callback); + } else { + _this.folderId = null; + _this.authComplete(); + console.log('Plugin Sync: Authorization error.'); + } + }; + + gapi.auth.authorize({'client_id': this.CLIENT_ID, 'scope': this.SCOPES, 'immediate': !popup} + , handleAuthResult); +} +//// end Authorizer + +window.plugin.sync.createFileOrFolder = function(option, callback) { + gapi.client.load('drive', 'v2', function() { + gapi.client.drive.files.insert(option).execute(callback); + }); +} + +window.plugin.sync.searchFileOrFolder = function(queryOption, callback) { + gapi.client.load('drive', 'v2', function() { + var option = {'q': queryOption}; + gapi.client.drive.files.list(option).execute(callback); + }); +} + + +// http://stackoverflow.com/a/8809472/2322660 +// http://stackoverflow.com/a/7221797/2322660 +// With format fixing: xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx where y in [8,9,a,b] +window.plugin.sync.generateUUID = function() { + if(window.crypto) { + var buf = new Uint16Array(8); + window.crypto.getRandomValues(buf); + var S4 = function(num) { + var ret = num.toString(16); + return "000".substring(0, 4-ret.length) + ret; + }; + var yxxx = function(num) { + return num&0x3fff|0x8000; + } + return (S4(buf[0])+S4(buf[1])+"-"+S4(buf[2])+"-4"+S4(buf[3]).substring(1)+"-"+S4(yxxx(buf[4]))+"-"+S4(buf[5])+S4(buf[6])+S4(buf[7])); + } else { + var d = new Date().getTime(); + var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { + var r = (d + Math.random()*16)%16 | 0; + d = Math.floor(d/16); + return (c=='x' ? r : (r&0x3|0x8)).toString(16); + }); + return uuid; + } +} + +window.plugin.sync.storeLocal = function(mapping) { + if(typeof(plugin.sync[mapping.field]) !== 'undefined' && plugin.sync[mapping.field] !== null) { + localStorage[mapping.key] = JSON.stringify(plugin.sync[mapping.field]); + } else { + localStorage.removeItem(mapping.key); + } +} + +window.plugin.sync.loadLocal = function(mapping) { + var objectJSON = localStorage[mapping.key]; + if(!objectJSON) return; + plugin.sync[mapping.field] = mapping.convertFunc + ? mapping.convertFunc(JSON.parse(objectJSON)) + : JSON.parse(objectJSON); +} + +window.plugin.sync.loadUUID = function() { + plugin.sync.loadLocal(plugin.sync.KEY_UUID); + if(!plugin.sync.uuid) { + plugin.sync.uuid = plugin.sync.generateUUID(); + plugin.sync.storeLocal(plugin.sync.KEY_UUID); + } +} + +window.plugin.sync.loadGapi = function() { + try { console.log('Loading Gapi JS now'); } catch(e) {} + @@INCLUDERAW:external/gapi.js@@ + try { console.log('done loading delaunay JS'); } catch(e) {} +} + +window.plugin.sync.toggleAuthButton = function() { + var authed = plugin.sync.authorizer.isAuthed(); + $('#sync-authButton').attr('disabled', authed); + $('#sync-authButton').html(authed ? 'Authorized' : 'Authorize'); + if(authed) { + $('#sync-authButton').addClass('sync-authButton-authed'); + $('#sync-show-dialog').removeClass('sync-show-dialog-error'); + } else { + $('#sync-authButton').removeClass('sync-authButton-authed'); + $('#sync-show-dialog').addClass('sync-show-dialog-error'); + } +} + +window.plugin.sync.showDialog = function() { + alert(plugin.sync.dialogHTML); + plugin.sync.toggleAuthButton(); +} + +window.plugin.sync.setupDialog = function() { + plugin.sync.dialogHTML = '
' + + '' + + '
'; + $('#toolbox').append('Sync '); +} + +window.plugin.sync.setupCSS = function() { + $("