Created
July 2, 2014 20:25
-
-
Save MaxMorais/bb45f19ca960d1f5b9d6 to your computer and use it in GitHub Desktop.
A pure javascript frappe client
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Classy - classy classes for JavaScript | |
* | |
* :copyright: (c) 2011 by Armin Ronacher. | |
* :license: BSD. | |
*/ | |
!function (definition) { | |
if (typeof module != 'undefined' && module.exports) module.exports = definition() | |
else if (typeof define == 'function' && typeof define.amd == 'object') define(definition) | |
else this.Class = definition() | |
}(function (undefined) { | |
var | |
CLASSY_VERSION = '1.4', | |
context = this, | |
old = context.Class, | |
disable_constructor = false; | |
/* we check if $super is in use by a class if we can. But first we have to | |
check if the JavaScript interpreter supports that. This also matches | |
to false positives later, but that does not do any harm besides slightly | |
slowing calls down. */ | |
var probe_super = (function(){$super();}).toString().indexOf('$super') > 0; | |
function usesSuper(obj) { | |
return !probe_super || /\B\$super\b/.test(obj.toString()); | |
} | |
/* helper function to set the attribute of something to a value or | |
removes it if the value is undefined. */ | |
function setOrUnset(obj, key, value) { | |
if (value === undefined) | |
delete obj[key]; | |
else | |
obj[key] = value; | |
} | |
/* gets the own property of an object */ | |
function getOwnProperty(obj, name) { | |
return Object.prototype.hasOwnProperty.call(obj, name) | |
? obj[name] : undefined; | |
} | |
/* instanciate a class without calling the constructor */ | |
function cheapNew(cls) { | |
disable_constructor = true; | |
var rv = new cls; | |
disable_constructor = false; | |
return rv; | |
} | |
/* the base class we export */ | |
var Class = function() {}; | |
/* restore the global Class name and pass it to a function. This allows | |
different versions of the classy library to be used side by side and | |
in combination with other libraries. */ | |
Class.$noConflict = function() { | |
try { | |
setOrUnset(context, 'Class', old); | |
} | |
catch (e) { | |
// fix for IE that does not support delete on window | |
context.Class = old; | |
} | |
return Class; | |
}; | |
/* what version of classy are we using? */ | |
Class.$classyVersion = CLASSY_VERSION; | |
/* extend functionality */ | |
Class.$extend = function(properties) { | |
var super_prototype = this.prototype; | |
/* disable constructors and instanciate prototype. Because the | |
prototype can't raise an exception when created, we are safe | |
without a try/finally here. */ | |
var prototype = cheapNew(this); | |
/* copy all properties of the includes over if there are any */ | |
if (properties.__include__) | |
for (var i = 0, n = properties.__include__.length; i != n; ++i) { | |
var mixin = properties.__include__[i]; | |
for (var name in mixin) { | |
var value = getOwnProperty(mixin, name); | |
if (value !== undefined) | |
prototype[name] = mixin[name]; | |
} | |
} | |
/* copy class vars from the superclass */ | |
properties.__classvars__ = properties.__classvars__ || {}; | |
if (prototype.__classvars__) | |
for (var key in prototype.__classvars__) | |
if (!properties.__classvars__[key]) { | |
var value = getOwnProperty(prototype.__classvars__, key); | |
properties.__classvars__[key] = value; | |
} | |
/* copy all properties over to the new prototype */ | |
for (var name in properties) { | |
var value = getOwnProperty(properties, name); | |
if (name === '__include__' || | |
value === undefined) | |
continue; | |
prototype[name] = typeof value === 'function' && usesSuper(value) ? | |
(function(meth, name) { | |
return function() { | |
var old_super = getOwnProperty(this, '$super'); | |
this.$super = super_prototype[name]; | |
try { | |
return meth.apply(this, arguments); | |
} | |
finally { | |
setOrUnset(this, '$super', old_super); | |
} | |
}; | |
})(value, name) : value | |
} | |
/* dummy constructor */ | |
var rv = function() { | |
if (disable_constructor) | |
return; | |
var proper_this = context === this ? cheapNew(arguments.callee) : this; | |
if (proper_this.__init__) | |
proper_this.__init__.apply(proper_this, arguments); | |
proper_this.$class = rv; | |
return proper_this; | |
} | |
/* copy all class vars over of any */ | |
for (var key in properties.__classvars__) { | |
var value = getOwnProperty(properties.__classvars__, key); | |
if (value !== undefined) | |
rv[key] = value; | |
} | |
/* copy prototype and constructor over, reattach $extend and | |
return the class */ | |
rv.prototype = prototype; | |
rv.constructor = rv; | |
rv.$extend = Class.$extend; | |
rv.$withData = Class.$withData; | |
return rv; | |
}; | |
/* instanciate with data functionality */ | |
Class.$withData = function(data) { | |
var rv = cheapNew(this); | |
for (var key in data) { | |
var value = getOwnProperty(data, key); | |
if (value !== undefined) | |
rv[key] = value; | |
} | |
return rv; | |
}; | |
/* export the class */ | |
return Class; | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var dropinRequire = function( moduleId, root ) { | |
if ( !root ) { | |
root = '' | |
} | |
// add the .js to module names | |
if ( !moduleId.match(/.js$/) ) { | |
moduleId += '.js'; | |
} | |
if( moduleId in dropinRequire.cache ) { | |
return dropinRequire.cache[moduleId]; | |
} | |
var req = new XMLHttpRequest(); | |
var modulePath = moduleId; | |
if ( modulePath.match(/^\.+\//) ) { | |
modulePath = root + modulePath; | |
} | |
req.open('GET', modulePath, false); | |
req.send(null); | |
if( req.status != 200 ) { | |
throw new Error(req); | |
} | |
var txt = [ | |
dropinRequire.prefix, | |
req.responseText, | |
dropinRequire.suffix(moduleId, root), | |
].join('\n'); | |
return dropinRequire.cache[moduleId] = eval(txt); | |
} | |
dropinRequire.cache = {}; | |
dropinRequire.prefix = [ | |
'(function(root){', | |
'var _module = { exports: {} };', | |
'var _require = function(moduleId){', | |
'return dropinRequire(moduleId, root)', | |
'};', | |
'(function(module, exports, require){', | |
].join('\n'), | |
// Here goes the javascript with commonjs modules | |
dropinRequire.suffix = function(moduleId, root){ | |
var parts; | |
if ( moduleId && (parts = moduleId.match( /\/([^\/]+)$/ )) ) { | |
root += moduleId.slice(0, moduleId.lastIndexOf( parts[0] ) + 1); | |
} | |
return [ | |
'})(_module, _module.exports, _require);', | |
'return _module.exports;', | |
'})("' + root + '");', | |
].join('\n'); | |
} | |
// to handle the replacement of "require" function | |
// - TODO do i need a global | |
dropinRequire.prevRequire = require; | |
/** | |
* dropinRequire.noConflict | |
* - attemps to make a jQuery-like noConflict | |
* - check and make it work | |
*/ | |
dropinRequire.noConflict = function(){ // no removeAll ? | |
require = dropinRequire.prevRequire; | |
return dropinRequire; | |
} | |
var require = dropinRequire; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(!(function(ns, Class, undefined){ | |
var JSONHttpRequest = require('../libs/jsonhttprequest.js'); | |
ns.FrappeClient = Class.$extend({ | |
__init__: function(url, user, pswd){ | |
if (!url) _throw.ValueRequired('url'); | |
this.url = url; | |
this._setup_client(); | |
if (user && pswd){ | |
this.login(user, pswd); | |
} | |
}, | |
_setup_client: function(cookie, cookie2){ | |
this.client = new JSONHttpRequest(); | |
if (this.cookies){ | |
if (this.cookies.cookie) { | |
this.client.setRequestHeader('Cookie', this.cookies.cookie); | |
} | |
if (this.cookies.cookie2) { | |
this.client.setRequestHeader('Cookie2', this.cookies.cookie2); | |
} | |
} | |
}, | |
_setup_cookies: function(request){ | |
this.cookies = { | |
cookie: request.getRequestHeader('Cookie')||false, | |
cookie2: rquest.getRequestHeader('Cookie2')||false | |
} | |
}, | |
_open: function(method, url, absolute){ | |
absolute = (!absolute) ? false : true; | |
url = (absolute===true) ? url : this.url + url; | |
this.request = this.client.open(method, this.url, false); | |
return this.request; | |
}, | |
_post: function(url, absolute){ | |
return this._open('POST', url, absolute); | |
}, | |
_get: function(url, absolute){ | |
this._open('GET', url, absolute); | |
}, | |
_put: function(url, absolute){ | |
this._open('PUT', url, absolute); | |
}, | |
_send: function(args){ | |
this.request.sendJSON(args||null); | |
return this.request.responseJSON; | |
}, | |
login: function(user, password){ | |
this._post(); | |
var res = this._send({ | |
'cmd': 'login', | |
'usr': user, | |
'pwd': password | |
}); | |
if (res.message!=='Logged In') throw new Error('AuthError'); | |
this._setup_cookies(res); | |
}, | |
logout: function(){ | |
this._get(); | |
this._send({'cmd': 'logout'}); | |
delete this.client; | |
}, | |
insert: function(doc){ | |
this._post('/api/resource/{doctype}/{docname}'.format(doc)); | |
var res = this._send({'data': JSON.stringfy(doc)}); | |
this.post_process(res); | |
}, | |
update: function(doc){ | |
this._put('/api/resource/{doctype}/{docname}'.format(doc)); | |
var res = this._send({'data': JSON.stringfy(doc)}); | |
this.post_process(res); | |
}, | |
bulk_update: function(docs){ | |
return this.post_request({ | |
'cmd': 'frappe.client.bulk_update', | |
'doc': JSON.stringfy(docs) | |
}); | |
}, | |
delete: function(doctype, name){ | |
return this.post_request({ | |
'cmd': 'frappe.model.delete_doc', | |
'doctype': doctype, | |
'docname': name | |
}); | |
}, | |
submit: function(doclist){ | |
return this.post_request({ | |
'cmd': 'frappe.client.submit', | |
'doclist': JSON.stringfy(doclist) | |
}); | |
}, | |
get_value: function(doctype, fieldname, filters){ | |
if (!fieldname) fieldname = 'name'; | |
return this.get_request({ | |
'cmd': 'frappe.client.get_value', | |
'doctype': doctype, | |
'fieldname': fieldname, | |
'filters': JSON.stringfy(filters||{}) | |
}); | |
}, | |
set_value: function(doctype, docname, fieldname, value){ | |
return this.post_request({ | |
'cmd': 'frappe.client.set_value', | |
'doctype': doctype, | |
'docname': docname, | |
'fieldname': fieldname, | |
'value': value | |
}); | |
}, | |
cancel: function(doctype, name){ | |
return this.post_request({ | |
'cmd': 'frappe.client.cancel', | |
'doctype': doctype, | |
'name': name | |
}); | |
}, | |
get_doc: function(doctype, name, filters){ | |
var params = JSON.strinfy(filters||{}); | |
self._get('/api/resource/{doctype}/{docname}'.format({ | |
doctype: doctype, | |
docname: docname | |
})); | |
var res = this._send(params); | |
return this.post_request(res); | |
}, | |
rename_doc: function(doctype, old_name, new_name){ | |
return this.post_request({ | |
'cmd': 'frappe.client.rename_doc', | |
'doctype': doctype, | |
'old_name': old_name, | |
'new_name': new_name | |
}); | |
}, | |
get_request: function(params){ | |
this._get(); | |
var res = this._send(this.preprocess(params)); | |
this.post_process(res); | |
return res; | |
}, | |
post_request: function(params){ | |
this._post(); | |
var res = this._send(this.preprocess(params)); | |
this.post_process(res); | |
return res; | |
}, | |
preprocess: function(params){ | |
return JSON.stringfy(params); | |
}, | |
post_process: function(request){ | |
var json = request.responseJSON; | |
if ('exc' in json && json.exc) throw new Error(json.exc); | |
if ('message' in json && json.message) return message; | |
if ('data' in json && json.data) return data; | |
return null; | |
} | |
}); | |
})(exports||window, Class)); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// JSONHttpRequest 0.3.0 | |
// | |
// Copyright 2011 Torben Schulz <http://pixelsvsbytes.com/> | |
// | |
// This program is free software: you can redistribute it and/or modify | |
// it under the terms of the GNU General Public License as published by | |
// the Free Software Foundation, either version 3 of the License, or | |
// (at your option) any later version. | |
// | |
// This program is distributed in the hope that it will be useful, | |
// but WITHOUT ANY WARRANTY; without even the implied warranty of | |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
// GNU General Public License for more details. | |
// | |
// You should have received a copy of the GNU General Public License | |
// along with _self program. If not, see <http://www.gnu.org/licenses/>. | |
// | |
/////////////////////////////////////////////////////////////////////// | |
function JSONHttpRequest() { | |
var _xmlHttpRequest = new XMLHttpRequest(); | |
var _responseJSON = null; | |
var _userContentType = false; | |
// INFO Defining 'this' as '_self' improves compression, since keywords won't be shortened, | |
// but variables will, and it delivers us from the need to reset the scope of the anonymous | |
// function in the for-loop via call() or apply(). | |
var _self = this; | |
var property = { | |
get: function() { | |
try { | |
_responseJSON = _xmlHttpRequest.responseText ? (!_responseJSON ? JSON.parse(_xmlHttpRequest.responseText) : _responseJSON) : null; | |
} | |
catch (e) { | |
if (_self.strictJSON) | |
throw e; | |
} | |
return _responseJSON; | |
}, | |
enumerable: true, | |
configurable: true | |
} | |
_self.strictJSON = true; | |
Object.defineProperty(_self, 'responseJSON', property); | |
_self.sendJSON = function(data) { | |
try { | |
data = JSON.stringify(data); | |
_responseJSON = null; | |
if (!_userContentType) | |
_xmlHttpRequest.setRequestHeader('Content-Type', 'application/json;charset=encoding'); | |
_userContentType = false; | |
} | |
catch (e) { | |
if (_self.strictJSON) | |
throw e; | |
} | |
_xmlHttpRequest.send(data); | |
} | |
// INFO proxy setup | |
function proxy(name) { | |
try { | |
if ((typeof _xmlHttpRequest[name]) == 'function') { | |
_self[name] = function() { | |
if (name == 'setRequestHeader') | |
_userContentType = arguments[0].toLowerCase() == 'content-type'; | |
return _xmlHttpRequest[name].apply(_xmlHttpRequest, Array.prototype.slice.apply(arguments)); | |
}; | |
} | |
else { | |
property.get = function() { return _xmlHttpRequest[name]; } | |
property.set = function(value) { _xmlHttpRequest[name] = value; } | |
Object.defineProperty(_self, name, property); | |
} | |
} | |
catch (e) { | |
// NOTE Swallow any exceptions, which may rise here. | |
} | |
} | |
// FIX onreadystatechange is not enumerable [Opera] | |
proxy('onreadystatechange'); | |
for (n in _xmlHttpRequest) | |
proxy(n); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
nice gist! :)