🚀 Socket Launch Week Day 5:Introducing Repository Access Permissions and Custom Roles.Learn more
Sign In

bikini

Package Overview
Dependencies
Maintainers
1
Versions
10
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

bikini - npm Package Compare versions

Comparing version
0.6.3
to
0.7.0
+12
.editorconfig
# http://editorconfig.org
root = true
[*]
indent_style = space
indent_size = 2
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
{
"requireCurlyBraces": [
"if",
"else",
"for",
"while",
"do",
"try",
"catch"
],
"requireOperatorBeforeLineBreak": true,
"validateIndentation": 2,
"disallowMultipleLineStrings": true,
"disallowMixedSpacesAndTabs": true,
"disallowTrailingWhitespace": true,
"requireSpaceAfterKeywords": [
"if",
"else",
"for",
"while",
"do",
"switch",
"return",
"try",
"catch",
"function"
],
"requireSpaceBeforeBinaryOperators": [
"=", "+=", "-=", "*=", "/=", "%=", "<<=", ">>=", ">>>=",
"&=", "|=", "^=", "+=",
"+", "-", "*", "/", "%", "<<", ">>", ">>>", "&",
"|", "^", "&&", "||", "===", "==", ">=",
"<=", "<", ">", "!=", "!=="
],
"requireSpaceAfterBinaryOperators": true,
"requireSpacesInConditionalExpression": true,
"requireSpaceBeforeBlockStatements": true,
"disallowSpaceAfterObjectKeys": true,
"requireLineFeedAtFileEnd": true,
"requireSpacesInFunctionExpression": {
"beforeOpeningCurlyBrace": true
},
"validateJSDoc": {
"checkParamNames": true,
"requireParamTypes": true
},
"disallowMultipleLineBreaks": true
}

Sorry, the diff of this file is not supported yet

+1
-1

@@ -10,3 +10,3 @@ {

"immed": true,
"indent": 4,
"indent": 2,
"latedef": true,

@@ -13,0 +13,0 @@ "newcap": true,

{
"name": "bikini",
"version": "0.6.3",
"version": "0.7.0",
"main": [
"./dist/*.js"
"./dist/bikini.js",
"./dist/bikangular.js"
],

@@ -7,0 +8,0 @@ "ignore": [

/*!
* Project: Bikini - Everything a model needs
* Copyright: (c) 2014 M-Way Solutions GmbH.
* Copyright: (c) 2015 M-Way Solutions GmbH.
* Version: 0.6.3
* Date: Wed Sep 24 2014 12:55:24
* Date: Tue May 12 2015 09:32:44
* License: https://raw.githubusercontent.com/mwaylabs/bikini/master/MIT-LICENSE.txt
*/
!function(a,b,c,d){var e=null;e="undefined"!=typeof exports?exports:a.Bikini={},e.Version=e.version="0.6.3",e.f=function(){},e.create=function(a){return new this(a)},e.design=function(a){var b=this.extend(a||{});return new b},e.extend=b.Model.extend,e.isCollection=function(a){return b.Collection.prototype.isPrototypeOf(a)},e.isModel=function(a){return b.Model.prototype.isPrototypeOf(a)},e.isEntity=function(a){return e.Entity.prototype.isPrototypeOf(a)},e.DATA={TYPE:{INTEGER:"integer",STRING:"string",TEXT:"text",DATE:"date",BOOLEAN:"boolean",FLOAT:"float",OBJECT:"object",ARRAY:"array",BINARY:"binary",OBJECTID:"objectid",NULL:"null"}},e.Object={_type:"Bikini.Object",_create:function(a){var b=function(){};return b.prototype=a,new b},include:function(a){for(var b in a){if(this.hasOwnProperty(b))throw e.Exception.RESERVED_WORD.getException();this[b]=a[b]}return this},design:function(a){var b=this._create(this);return b.include(this._normalize(a)),b},bindToCaller:function(a,b,c){return function(){if("function"!=typeof b||"object"!=typeof a)throw e.Exception.INVALID_INPUT_PARAMETER.getException();return Array.isArray(c)?b.apply(a,c):b.call(a,c)}},_normalize:function(a){return a=a&&"object"==typeof a?a:{}},handleCallback:function(a){var b=Array.prototype.slice.call(arguments,1);if(a){var c="object"==typeof a.target?a.target:this,d=a;if("function"==typeof a.action?d=a.action:"string"==typeof a.action&&(d=c[a.action]),"function"==typeof d)return this.bindToCaller(c,d,b)()}}},YES=!0,NO=!1,e.ObjectID=function(a){e.ObjectID.counter=e.ObjectID.counter||parseInt(Math.random()*Math.pow(16,6)),e.ObjectID.machineId=e.ObjectID.machineId||parseInt(Math.random()*Math.pow(16,6)),e.ObjectID.processId=e.ObjectID.processId||parseInt(Math.random()*Math.pow(16,4)),this._ObjectID(a)},e.ObjectID._looksLikeObjectID=function(a){return 24===a.length&&a.match(/^[0-9a-f]*$/)},c.extend(e.ObjectID.prototype,{_str:"",_ObjectID:function(a){if(a){if(a=a.toLowerCase(),!e.ObjectID._looksLikeObjectID(a))throw new Error("Invalid hexadecimal string for creating an ObjectID");this._str=a}else this._str=this._hexString(8,(new Date).getTime()/1e3)+this._hexString(6,e.ObjectID.machineId)+this._hexString(4,e.ObjectID.processId)+this._hexString(6,e.ObjectID.counter++);return this._str},_hexString:function(a,b){b=b||parseInt(Math.random()*Math.pow(16,a));for(var c=b.toString(16);c.length<a;)c="0"+c;return c.substr(0,a)},toString:function(){return"ObjectID('"+this._str+"')"},equals:function(a){return a instanceof this._ObjectID&&this.valueOf()===a.valueOf()},clone:function(){return new e.ObjectID(this._str)},typeName:function(){return"oid"},getTimestamp:function(){return 1e3*parseInt(this._str.substr(0,8),16)},getMachineId:function(){return parseInt(this._str.substr(8,6),16)},getProcessId:function(){return parseInt(this._str.substr(14,4),16)},getCounter:function(){return parseInt(this._str.substr(18,6),16)},valueOf:function(){return this._str},toJSON:function(){return this._str},toHexString:function(){return this._str},_selectorIsId:function(a){return"string"==typeof a||"number"==typeof a||a instanceof e.ObjectId},_selectorIsIdPerhapsAsObject:function(a){return this._selectorIsId(a)||a&&"object"==typeof a&&a._id&&this._selectorIsId(a._id)&&1===c.size(a)},_idsMatchedBySelector:function(a){if(this._selectorIsId(a))return[a];if(!a)return null;if(c.has(a,"_id"))return this._selectorIsId(a._id)?[a._id]:a._id&&a._id.$in&&c.isArray(a._id.$in)&&!c.isEmpty(a._id.$in)&&c.all(a._id.$in,this._selectorIsId)?a._id.$in:null;if(a.$and&&c.isArray(a.$and))for(var b=0;b<a.$and.length;++b){var d=this._idsMatchedBySelector(a.$and[b]);if(d)return d}return null}}),e.UniqueId=e.Object.design({uuid:function(a,b){var c="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".split(""),d=[];b=b||c.length;var e;if(a)for(e=0;a>e;e++)d[e]=c[0|Math.random()*b];else{var f;for(d[8]=d[13]=d[18]=d[23]="-",d[14]="4",e=0;36>e;e++)d[e]||(f=0|16*Math.random(),d[e]=c[19===e?3&f|8:f])}return d.join("")}}),e.Base64=e.Object.design({type:"Bikini.Base64",_keyStr:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",encodeBinary:function(a){for(var b,c="",d=new Array(4),e=0,f=0;e<a.length;){b=new Array(3);for(var g=0;g<b.length;g++)b[g]=e<a.length?255&a.charCodeAt(e++):0;switch(d[0]=b[0]>>2,d[1]=(3&b[0])<<4|b[1]>>4,d[2]=(15&b[1])<<2|b[2]>>6,d[3]=63&b[2],f=e-(a.length-1)){case 2:d[3]=64,d[2]=64;break;case 1:d[3]=64}for(g=0;g<d.length;g++)c+=this._keyStr.charAt(d[g])}return c},encode:function(a){var b,c,d,f,g,h,i,j="",k=0;for(a=e.Cypher.utf8Encode(a);k<a.length;)b=a.charCodeAt(k++),c=a.charCodeAt(k++),d=a.charCodeAt(k++),f=b>>2,g=(3&b)<<4|c>>4,h=(15&c)<<2|d>>6,i=63&d,isNaN(c)?h=i=64:isNaN(d)&&(i=64),j+=this._keyStr.charAt(f)+this._keyStr.charAt(g)+this._keyStr.charAt(h)+this._keyStr.charAt(i);return j},binaryEncode:function(a){for(var b,c,d,e,f,g,h,i="",j=0;j<a.length;)b=a.charCodeAt(j++),c=a.charCodeAt(j++),d=a.charCodeAt(j++),e=b>>2,f=(3&b)<<4|c>>4,g=(15&c)<<2|d>>6,h=63&d,isNaN(c)?g=h=64:isNaN(d)&&(h=64),i+=this._keyStr.charAt(e)+this._keyStr.charAt(f)+this._keyStr.charAt(g)+this._keyStr.charAt(h);return i},decode:function(a){var b,c,d,f,g,h,i,j="",k=0;for(a=a.replace(/[^A-Za-z0-9\+\/\=]/g,"");k<a.length;)f=this._keyStr.indexOf(a.charAt(k++)),g=this._keyStr.indexOf(a.charAt(k++)),h=this._keyStr.indexOf(a.charAt(k++)),i=this._keyStr.indexOf(a.charAt(k++)),b=f<<2|g>>4,c=(15&g)<<4|h>>2,d=(3&h)<<6|i,j+=String.fromCharCode(b),64!==h&&(j+=String.fromCharCode(c)),64!==i&&(j+=String.fromCharCode(d));return e.Cypher.utf8Decode(j)}}),e.SHA256=e.Object.design({type:"Bikini.SHA256",chrsz:8,hexcase:0,hash:function(a){return a=e.Cypher.utf8Encode(a),this.binb2hex(this.coreSha256(this.str2binb(a),a.length*this.chrsz))},safeAdd:function(a,b){var c=(65535&a)+(65535&b),d=(a>>16)+(b>>16)+(c>>16);return d<<16|65535&c},S:function(a,b){return a>>>b|a<<32-b},R:function(a,b){return a>>>b},Ch:function(a,b,c){return a&b^~a&c},Maj:function(a,b,c){return a&b^a&c^b&c},Sigma0256:function(a){return this.S(a,2)^this.S(a,13)^this.S(a,22)},Sigma1256:function(a){return this.S(a,6)^this.S(a,11)^this.S(a,25)},Gamma0256:function(a){return this.S(a,7)^this.S(a,18)^this.R(a,3)},Gamma1256:function(a){return this.S(a,17)^this.S(a,19)^this.R(a,10)},coreSha256:function(a,b){var c,d,e,f,g,h,i,j,k,l,m,n,o=new Array(1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298),p=new Array(1779033703,3144134277,1013904242,2773480762,1359893119,2600822924,528734635,1541459225),q=new Array(64);for(a[b>>5]|=128<<24-b%32,a[(b+64>>9<<4)+15]=b,k=0;k<a.length;k+=16){for(c=p[0],d=p[1],e=p[2],f=p[3],g=p[4],h=p[5],i=p[6],j=p[7],l=0;64>l;l++)q[l]=16>l?a[l+k]:this.safeAdd(this.safeAdd(this.safeAdd(this.Gamma1256(q[l-2]),q[l-7]),this.Gamma0256(q[l-15])),q[l-16]),m=this.safeAdd(this.safeAdd(this.safeAdd(this.safeAdd(j,this.Sigma1256(g)),this.Ch(g,h,i)),o[l]),q[l]),n=this.safeAdd(this.Sigma0256(c),this.Maj(c,d,e)),j=i,i=h,h=g,g=this.safeAdd(f,m),f=e,e=d,d=c,c=this.safeAdd(m,n);p[0]=this.safeAdd(c,p[0]),p[1]=this.safeAdd(d,p[1]),p[2]=this.safeAdd(e,p[2]),p[3]=this.safeAdd(f,p[3]),p[4]=this.safeAdd(g,p[4]),p[5]=this.safeAdd(h,p[5]),p[6]=this.safeAdd(i,p[6]),p[7]=this.safeAdd(j,p[7])}return p},str2binb:function(a){for(var b=[],c=(1<<this.chrsz)-1,d=0;d<a.length*this.chrsz;d+=this.chrsz)b[d>>5]|=(a.charCodeAt(d/this.chrsz)&c)<<24-d%32;return b},binb2hex:function(a){for(var b=this.hexcase?"0123456789ABCDEF":"0123456789abcdef",c="",d=0;d<4*a.length;d++)c+=b.charAt(a[d>>2]>>8*(3-d%4)+4&15)+b.charAt(a[d>>2]>>8*(3-d%4)&15);return c}}),e.Cypher=e.Object.design({type:"Bikini.Cypher",defaultDecoder:e.Base64,defaultEncoder:e.Base64,defaultHasher:e.SHA256,decode:function(a,b){return b&&b.decode?b.decode(a):this.defaultDecoder.decode(a)},encode:function(a,b){return b&&b.encode?b.encode(a):this.defaultEncoder.encode(a)},hash:function(a,b){return b&&b.hash?b.hash(a):this.defaultHasher.hash(a)},utf8Encode:function(a){a=a.replace(/\r\n/g,"\n");for(var b="",c=0;c<a.length;c++){var d=a.charCodeAt(c);128>d?b+=String.fromCharCode(d):d>127&&2048>d?(b+=String.fromCharCode(d>>6|192),b+=String.fromCharCode(63&d|128)):(b+=String.fromCharCode(d>>12|224),b+=String.fromCharCode(d>>6&63|128),b+=String.fromCharCode(63&d|128))}return b},utf8Decode:function(a){var b,c,d,e,f,g="";for(b=c=d=e=0;b<a.length;)c=a.charCodeAt(b),128>c?(g+=String.fromCharCode(c),b++):c>191&&224>c?(e=a.charCodeAt(b+1),g+=String.fromCharCode((31&c)<<6|63&e),b+=2):(e=a.charCodeAt(b+1),f=a.charCodeAt(b+2),g+=String.fromCharCode((15&c)<<12|(63&e)<<6|63&f),b+=3);return g}}),e.Date={create:function(){var a=moment.apply(this,arguments);return c.extend(a,this)}},e.Field=function(a){this.merge(a),this.initialize.apply(this,arguments)},e.Field.extend=e.extend,e.Field.create=e.create,e.Field.design=e.design,c.extend(e.Field.prototype,e.Object,{_type:"Bikini.Field",name:null,type:null,index:null,defaultValue:void 0,length:null,required:NO,persistent:YES,initialize:function(){},merge:function(a){a=c.isString(a)?{type:a}:a||{},this.name=c.isUndefined(a.name)?this.name:a.name,this.type=c.isUndefined(a.type)?this.type:a.type,this.index=c.isUndefined(a.index)?this.index:a.index,this.defaultValue=c.isUndefined(a.defaultValue)?this.defaultValue:a.defaultValue,this.length=c.isUndefined(a.length)?this.length:a.length,this.required=c.isUndefined(a.required)?this.required:a.required,this.persistent=c.isUndefined(a.persistent)?this.persistent:a.persistent},transform:function(a,b){b=b||this.type;try{if(c.isUndefined(a))return this.defaultValue;if(b===e.DATA.TYPE.STRING||b===e.DATA.TYPE.TEXT)return c.isObject(a)?JSON.stringify(a):c.isNull(a)?"null":a.toString();if(b===e.DATA.TYPE.INTEGER)return parseInt(a);if(b===e.DATA.TYPE.BOOLEAN)return a===!0||"true"===a;if(b===e.DATA.TYPE.FLOAT)return parseFloat(a);if(b===e.DATA.TYPE.OBJECT||b===e.DATA.TYPE.ARRAY){if(!c.isObject(a))return c.isString(a)?JSON.parse(a):null}else if(b===e.DATA.TYPE.DATE){if(!e.Date.isPrototypeOf(a)){var d=a?e.Date.create(a):null;return d&&d.isValid()?d:null}}else if(b===e.DATA.TYPE.OBJECTID&&!e.ObjectID.prototype.isPrototypeOf(a))return c.isString(a)?new e.ObjectID(a):null;return a}catch(f){console.error("Failed converting value! "+f.message)}},equals:function(a,b){var d=this.transform(a),e=this.transform(b);return this._equals(d,e,c.isArray(d))},isBinary:function(a){return"undefined"!=typeof Uint8Array&&a instanceof Uint8Array||a&&a.$Uint8ArrayPolyfill},detectType:function(a){return c.isNumber(a)?e.DATA.TYPE.FLOAT:c.isString(a)?e.DATA.TYPE.STRING:c.isBoolean(a)?e.DATA.TYPE.BOOLEAN:c.isArray(a)?e.DATA.TYPE.ARRAY:c.isNull(a)?e.DATA.TYPE.NULL:c.isDate(a)||e.Date.isPrototypeOf(a)?e.DATA.TYPE.DATE:e.ObjectID.prototype.isPrototypeOf(a)?e.DATA.TYPE.OBJECTID:this.isBinary(a)?e.DATA.TYPE.BINARY:e.DATA.TYPE.OBJECT},typeOrder:function(a){switch(a){case e.DATA.TYPE.NULL:return 0;case e.DATA.TYPE.FLOAT:return 1;case e.DATA.TYPE.STRING:return 2;case e.DATA.TYPE.OBJECT:return 3;case e.DATA.TYPE.ARRAY:return 4;case e.DATA.TYPE.BINARY:return 5;case e.DATA.TYPE.DATE:return 6}return-1},_equals:function(a,b,d){var e,f=this;if(a===b)return!0;if(!a||!b)return!1;if(!c.isObject(a)||!c.isObject(b))return!1;if(a instanceof Date&&b instanceof Date)return a.valueOf()===b.valueOf();if(this.isBinary(a)&&this.isBinary(b)){if(a.length!==b.length)return!1;for(e=0;e<a.length;e++)if(a[e]!==b[e])return!1;return!0}if(c.isFunction(a.equals))return a.equals(b);if(c.isArray(a)){if(!c.isArray(b))return!1;if(a.length!==b.length)return!1;for(e=0;e<a.length;e++)if(!f.equals(a[e],b[e],d))return!1;return!0}var g;if(d){var h=[];return c.each(b,function(a,b){h.push(b)}),e=0,g=c.all(a,function(a,c){return e>=h.length?!1:c!==h[e]?!1:f.equals(a,b[h[e]],d)?(e++,!0):!1}),g&&e===h.length}return e=0,g=c.all(a,function(a,g){return c.has(b,g)&&f.equals(a,b[g],d)?(e++,!0):!1}),g&&c.size(b)===e},_cmp:function(a,b){if(void 0===a)return void 0===b?0:-1;if(void 0===b)return 1;var c=0,d=this.detectType(a),f=this.detectType(b),g=this.typeOrder(d),h=this.typeOrder(f);if(g!==h)return h>g?-1:1;if(d!==f)throw new Error("Missing type coercion logic in _cmp");if(7===d&&(d=f=2,a=a.toHexString(),b=b.toHexString()),d===e.DATA.TYPE.DATE&&(d=f=1,a=a.getTime(),b=b.getTime()),d===e.DATA.TYPE.FLOAT)return a-b;if(f===e.DATA.TYPE.STRING)return b>a?-1:a===b?0:1;if(d===e.DATA.TYPE.OBJECT){var i=function(a){var b=[];for(var c in a)b.push(c),b.push(a[c]);return b};return this._cmp(i(a),i(b))}if(d===e.DATA.TYPE.ARRAY)for(c=0;;c++){if(c===a.length)return c===b.length?0:-1;if(c===b.length)return 1;var j=this._cmp(a[c],b[c]);if(0!==j)return j}if(d===e.DATA.TYPE.BINARY){if(a.length!==b.length)return a.length-b.length;for(c=0;c<a.length;c++){if(a[c]<b[c])return-1;if(a[c]>b[c])return 1}return 0}if(d===e.DATA.TYPE.BOOLEAN)return a?b?0:1:b?-1:0;if(d===e.DATA.TYPE.NULL)return 0;throw new Error("Unknown type to sort")}}),e.Entity=function(a){var b=this.fields;this.fields={},this._mergeFields(b),a=a||{},a.fields&&this._mergeFields(a.fields),this.typeMapping=a.typeMapping||this.typeMapping;var c=a.collection,d=a.model||(c?c.prototype.model:null);this.idAttribute=a.idAttribute||this.idAttribute||(d?d.prototype.idAttribute:""),this._updateFields(this.typeMapping),this.initialize.apply(this,arguments)},e.Entity.from=function(a,b){if(e.Entity.prototype.isPrototypeOf(a))b&&b.typeMapping&&a._updateFields(b.typeMapping);else if(c.isFunction(a)&&e.Entity.prototype.isPrototypeOf(a.prototype)){var d=a;a=new d(b)}else{"string"==typeof a&&(a={name:a});var f=e.Entity.extend(a);a=new f(b)}return a},e.Entity.extend=e.extend,e.Entity.create=e.create,e.Entity.design=e.design,c.extend(e.Entity.prototype,e.Object,{_type:"Bikini.Entity",name:"",idAttribute:"",fields:{},initialize:function(){},getFields:function(){return this.fields},getField:function(a){return this.fields[a]},getFieldName:function(a){var b=this.getField(a);return b&&b.name?b.name:a},getKey:function(){return this.idAttribute||e.Model.idAttribute},getKeys:function(){return this.splitKey(this.getKey())},splitKey:function(a){var b=[];return c.isString(a)&&c.each(a.split(","),function(a){var c=a.trim();c&&b.push(c)}),b},_mergeFields:function(a){c.isObject(this.fields)||(this.fields={});var b=this;c.isObject(a)&&c.each(a,function(a,c){b.fields[c]?b.fields[c].merge(a):b.fields[c]=new e.Field(a)})},_updateFields:function(a){var b=this;c.each(this.fields,function(c,d){c.persistent===NO?delete b.fields[d]:(c.name||(c.name=d),a&&a[c.type]&&(c.type=a[c.type]))})},toAttributes:function(a,b,d){if(d=d||this.fields,a&&!c.isEmpty(d)){var e,f={};return c.each(d,function(b,d){e=c.isFunction(a.get)?a.get(b.name):a[b.name],f[d]=e}),f}return a},fromAttributes:function(a,b){if(b=b||this.fields,a&&!c.isEmpty(b)){var d={};return c.each(b,function(b,e){var f=c.isFunction(a.get)?a.get(e):a[e];f=b.transform(f),c.isUndefined(f)||(d[b.name]=f)}),d}return a},setId:function(a,b){if(a&&b){var d=this.getKey()||a.idAttribute;d&&(c.isFunction(a.set)?a.set(d,b):a[d]=b)}return a},getId:function(a){if(a){var b=this.getKey()||a.idAttribute;if(b)return c.isFunction(a.get)?a.get(b):a[b]}}}),e.Security=e.Object.design({logon:function(a,b){var c=a?a.credentials:null;if(c)switch(c.type){case"basic":return this.logonBasicAuth(a,b)}return this.handleCallback(b)},logonBasicAuth:function(a,b){var c=a.credentials;return a.beforeSend=function(a){e.Security.setBasicAuth(a,c)},this.handleCallback(b)},setBasicAuth:function(a,b){if(b&&b.username&&a&&e.Base64){var c=e.Base64.encode(encodeURIComponent(b.username+":"+(b.password||"")));a.setRequestHeader("Authorization","Basic "+c)}}}),e.Model=b.Model.extend({constructor:function(a,c){this.url&&"string"==typeof this.url&&"/"!==this.url.charAt(this.url.length-1)&&(this.url+="/"),this.init(a,c),b.Model.apply(this,arguments)}}),e.Model.create=e.create,e.Model.design=e.design,c.extend(e.Model.prototype,e.Object,{_type:"Bikini.Model",isModel:YES,entity:null,defaults:{},changedSinceSync:{},logon:e.Security.logon,init:function(a,b){b=b||{},this.collection=b.collection||this.collection,this.idAttribute=b.idAttribute||this.idAttribute,this.store=this.store||(this.collection?this.collection.store:null)||b.store,this.store&&c.isFunction(this.store.initModel)&&this.store.initModel(this,b),this.entity=this.entity||(this.collection?this.collection.entity:null)||b.entity,this.entity&&(this.entity=e.Entity.from(this.entity,{model:this.constructor,typeMapping:b.typeMapping}),this.idAttribute=this.entity.idAttribute||this.idAttribute),this.credentials=this.credentials||(this.collection?this.collection.credentials:null)||b.credentials,this.on("change",this.onChange,this),this.on("sync",this.onSync,this)},sync:function(a,d,e){e=e||{},e.credentials=e.credentials||this.credentials;var f=(e.store?e.store:null)||this.store,g=this,h=arguments;return this.logon(e,function(){return f&&c.isFunction(f.sync)?f.sync.apply(g,h):b.sync.apply(g,h)})},onChange:function(a){var b=a.changedAttributes();if(c.isObject(b))for(var d in b)this.changedSinceSync[d]=b[d]},onSync:function(){this.changedSinceSync={}},getUrlRoot:function(){if(this.urlRoot)return c.isFunction(this.urlRoot)?this.urlRoot():this.urlRoot;if(this.collection)return this.collection.getUrlRoot();if(this.url){var a=c.isFunction(this.url)?this.url():this.url;return a&&this.id&&a.indexOf(this.id)>0?a.substr(0,a.indexOf(this.id)):a}},toJSON:function(a){a=a||{};var b=a.entity||this.entity;return e.isEntity(b)?b.fromAttributes(a.attrs||this.attributes):a.attrs||c.clone(this.attributes)},parse:function(a,b){b=b||{};var c=b.entity||this.entity;return e.isEntity(c)?c.toAttributes(a):a}}),e.Collection=b.Collection.extend({constructor:function(a){this.url&&"/"!==this.url.charAt(this.url.length-1)&&(this.url+="/"),this.init(a),b.Collection.apply(this,arguments)}}),e.Collection.create=e.create,e.Collection.design=e.design,c.extend(e.Collection.prototype,e.Object,{_type:"Bikini.Collection",isCollection:YES,model:e.Model,entity:null,options:null,logon:e.Security.logon,init:function(a){a=a||{},this.store=a.store||this.store||(this.model?this.model.prototype.store:null),this.entity=a.entity||this.entity||(this.model?this.model.prototype.entity:null),this.options=a.options||this.options;var b=this.entity||this.entityFromUrl(this.url);b&&(this.entity=e.Entity.from(b,{model:this.model,typeMapping:a.typeMapping})),this._updateUrl(),this.store&&c.isFunction(this.store.initCollection)&&this.store.initCollection(this,a)},entityFromUrl:function(a){if(a){var b=e.Request.getLocation(this.url).pathname.match(/([^\/]+)\/?$/);if(b&&b.length>1)return b[1]}},sort:function(a){c.isObject(a&&a.sort)&&(this.comparator=e.DataSelector.compileSort(a.sort)),b.Collection.prototype.sort.apply(this,arguments)},select:function(a){var b=a&&a.query?e.DataSelector.create(a.query):null,c=e.Collection.create(null,{model:this.model});return a&&a.sort&&(c.comparator=e.DataSelector.compileSort(a.sort)),this.each(function(a){(!b||b.matches(a.attributes))&&c.add(a)}),c},destroy:function(a){a=a||{};var b=a.success;if(this.length>0){a.success=function(){0===this.length&&b&&b()};for(var c;c=this.first();)this.sync("delete",c,a),this.remove(c)}else b&&b()},destroyLocal:function(){var a=this.endpoint.localStore;return this.entity.name&&a.drop(this.entity.name),localStorage.setItem("__"+this.channel+"last_msg_time",""),this.store.endpoints={},this.reset()},sync:function(a,d,e){e=e||{},e.credentials=e.credentials||this.credentials;var f=(e.store?e.store:null)||this.store,g=this,h=arguments;return this.logon(e,function(){return f&&c.isFunction(f.sync)?f.sync.apply(g,h):b.sync.apply(g,h)})},save:function(){this.each(function(a){a.save()})},getUrlParams:function(a){a=a||this.getUrl();var b=a.match(/\?([^#]*)/),d={};return b&&b.length>1&&c.each(b[1].split("&"),function(a){var b=a.split("=");d[b[0]]=b[1]}),d},getUrl:function(){return(c.isFunction(this.url)?this.url():this.url)||""},getUrlRoot:function(){var a=this.getUrl();return a?a.indexOf("?")>=0?a.substr(0,a.indexOf("?")):a:""},applyFilter:function(a){this.trigger("filter",this.filter(a))},_updateUrl:function(){var a=this.getUrlParams();if(this.options&&(this.url=this.getUrlRoot(),this.options.query&&(a.query=encodeURIComponent(JSON.stringify(this.options.query))),this.options.fields&&(a.fields=encodeURIComponent(JSON.stringify(this.options.fields))),this.options.sort&&(a.sort=encodeURIComponent(JSON.stringify(this.options.sort))),!c.isEmpty(a))){this.url+="?";var b=[];for(var d in a)b.push(d+(a[d]?"="+a[d]:""));this.url+=b.join("&")}}}),e.DataSelector=e.Object.design({_type:"Bikini.DataSelector",_selector:null,create:function(a){var b=this.design({_selector:null});return b.init(a),b},init:function(a){this._selector=this.compileSelector(a)},matches:function(a){return c.isFunction(this._selector)?this._selector(a):!1},hasOperators:function(a){var b;for(var c in a){var d="$"===c.substr(0,1);if(void 0===b)b=d;else if(b!==d)throw new Error("Inconsistent selector: "+a)}return!!b},compileSelector:function(a){if(c.isFunction(a))return function(b){return a.call(b)};if(this._selectorIsId(a))return function(b){var d=c.isFunction(b.getId)?b.getId():b._id||b.id;return e.Field.prototype.equals(d,a)};if(!a||"_id"in a&&!a._id)return function(){return!1};if(c.isBoolean(a)||c.isArray(a)||e.Field.prototype.isBinary(a))throw new Error("Invalid selector: "+a);return this.compileDocSelector(a)},compileDocSelector:function(a){var b=e.DataSelector,d=[];return c.each(a,function(a,e){if("$"===e.substr(0,1)){if(!c.has(b.LOGICAL_OPERATORS,e))throw new Error("Unrecognized logical operator: "+e);d.push(b.LOGICAL_OPERATORS[e](a))}else{var f=b._makeLookupFunction(e),g=b.compileValueSelector(a);d.push(function(a){var b=f(a);return c.any(b,g)})}}),function(a){var b=c.isFunction(a.getData)?a.getData():a;return c.all(d,function(a){return a(b)})}},compileValueSelector:function(a){var b=e.DataSelector;if(null===a)return function(a){return b._anyIfArray(a,function(a){return null===a})};if(!c.isObject(a))return function(c){return b._anyIfArray(c,function(b){return b===a})};if(c.isRegExp(a))return function(d){return c.isUndefined(d)?!1:b._anyIfArray(d,function(b){return a.test(b)})};if(c.isArray(a))return function(d){return c.isArray(d)?b._anyIfArrayPlus(d,function(c){return b._equal(a,c)}):!1};if(this.hasOperators(a)){var d=[];return c.each(a,function(e,f){if(!c.has(b.VALUE_OPERATORS,f))throw new Error("Unrecognized operator: "+f);d.push(b.VALUE_OPERATORS[f](e,a.$options))}),function(a){return c.all(d,function(b){return b(a)})}}return function(c){return b._anyIfArray(c,function(c){return b._equal(a,c)})}},_makeLookupFunction:function(a){var b,d,e,f=a.indexOf(".");if(-1===f)b=a;else{b=a.substr(0,f);var g=a.substr(f+1);d=this._makeLookupFunction(g),e=/^\d+(\.|$)/.test(g)}return function(a){if(null===a)return[void 0];var f=a[b];return d?c.isArray(f)&&0===f.length?[void 0]:((!c.isArray(f)||e)&&(f=[f]),Array.prototype.concat.apply([],c.map(f,d))):[f]}},_anyIfArray:function(a,b){return c.isArray(a)?c.any(a,b):b(a)},_anyIfArrayPlus:function(a,b){return b(a)?!0:c.isArray(a)&&c.any(a,b)},_selectorIsId:function(a){return c.isString(a)||c.isNumber(a)},_equal:function(a,b){return e.Field.prototype._equals(a,b,!0)},_cmp:function(a,b){return e.Field.prototype._cmp(a,b)},LOGICAL_OPERATORS:{$and:function(a){if(!c.isArray(a)||c.isEmpty(a))throw new Error("$and/$or/$nor must be nonempty array");var b=c.map(a,e.DataSelector.compileDocSelector);return function(a){return c.all(b,function(b){return b(a)})}},$or:function(a){if(!c.isArray(a)||c.isEmpty(a))throw new Error("$and/$or/$nor must be nonempty array");var b=c.map(a,e.DataSelector.compileDocSelector);return function(a){return c.any(b,function(b){return b(a)})}},$nor:function(a){if(!c.isArray(a)||c.isEmpty(a))throw new Error("$and/$or/$nor must be nonempty array");var b=c.map(a,e.DataSelector.compileDocSelector);return function(a){return c.all(b,function(b){return!b(a)})}},$where:function(a){if(!c.isFunction(a)){var b=a;a=function(){return b}}return function(b){return a.call(b)}}},VALUE_OPERATORS:{$in:function(a){if(!c.isArray(a))throw new Error("Argument to $in must be array");return function(b){return e.DataSelector._anyIfArrayPlus(b,function(b){return c.any(a,function(a){return e.DataSelector._equal(a,b)})})}},$all:function(a){if(!c.isArray(a))throw new Error("Argument to $all must be array");return function(b){return c.isArray(b)?c.all(a,function(a){return c.any(b,function(b){return e.DataSelector._equal(a,b)})}):!1}},$lt:function(a){return function(b){return e.DataSelector._anyIfArray(b,function(b){return e.DataSelector._cmp(b,a)<0})}},$lte:function(a){return function(b){return e.DataSelector._anyIfArray(b,function(b){return e.DataSelector._cmp(b,a)<=0})}},$gt:function(a){return function(b){return e.DataSelector._anyIfArray(b,function(b){return e.DataSelector._cmp(b,a)>0})}},$gte:function(a){return function(b){return e.DataSelector._anyIfArray(b,function(b){return e.DataSelector._cmp(b,a)>=0})}},$ne:function(a){return function(b){return!e.DataSelector._anyIfArrayPlus(b,function(b){return e.DataSelector._equal(b,a)})}},$nin:function(a){if(!c.isArray(a))throw new Error("Argument to $nin must be array");var b=this.VALUE_OPERATORS.$in(a);return function(a){return void 0===a?!0:!b(a)}},$exists:function(a){return function(b){return a===(void 0!==b)}},$mod:function(a){var b=a[0],c=a[1];return function(a){return e.DataSelector._anyIfArray(a,function(a){return a%b===c})}},$size:function(a){return function(b){return c.isArray(b)&&a===b.length}},$type:function(a){return function(b){return c.isUndefined(b)?!1:e.DataSelector._anyIfArray(b,function(b){return e.Field.prototype.detectType(b)===a})}},$regex:function(a,b){if(c.isUndefined(b)){if(/[^gim]/.test(b))throw new Error("Only the i, m, and g regexp options are supported");var d=c.isRegExp(a)?a.source:a;a=new RegExp(d,b)}else c.isRegExp(a)||(a=new RegExp(a));return function(b){return c.isUndefined(b)?!1:e.DataSelector._anyIfArray(b,function(b){return a.test(b)})}},$options:function(){return function(){return!0}},$elemMatch:function(a){var b=e.DataSelector.compileDocSelector(a);return function(a){return c.isArray(a)?c.any(a,function(a){return b(a)}):!1}},$not:function(a){var b=e.DataSelector.compileDocSelector(a);return function(a){return!b(a)}}},compileSort:function(a){var b=[];if(c.isArray(a))for(var d=0;d<a.length;d++)b.push("string"==typeof a[d]?{lookup:this._makeLookupFunction(a[d]),ascending:!0}:{lookup:this._makeLookupFunction(a[d][0]),ascending:"desc"!==a[d][1]});else{if("object"!=typeof a)throw new Error("Bad sort specification: ",JSON.stringify(a));for(var f in a)b.push({lookup:this._makeLookupFunction(f),ascending:a[f]>=0})}if(0===b.length)return function(){return 0};var g=function(a,b){var d,f=!0;return c.each(a,function(a){c.isArray(a)||(a=[a]),c.isArray(a)&&0===a.length&&(a=[void 0]),c.each(a,function(a){if(f)d=a,f=!1;else{var c=e.DataSelector._cmp(d,a);(b&&c>0||!b&&0>c)&&(d=a)}})}),d};return function(a,c){a=a.attributes?a.attributes:a,c=c.attributes?c.attributes:c;for(var d=0;d<b.length;++d){var f=b[d],h=g(f.lookup(a),f.ascending),i=g(f.lookup(c),f.ascending),j=e.DataSelector._cmp(h,i);if(0!==j)return f.ascending?j:-j}return 0}}}),e.SqlSelector=e.DataSelector.design({_type:"Bikini.SqlSelector",_selector:null,_query:null,_entity:null,create:function(a,b){var c=this.extend({_entity:b,_selector:null,_query:null});return c.init(a),c},init:function(a){this._selector=this.compileSelector(a),this._query=this.buildSqlQuery(a)},buildStatement:function(){return this._query},buildSqlQuery:function(a){if(a instanceof Function)return"";if(c.isString(a))return a;if(!a||"_id"in a&&!a._id)return"1=2";if(c.isBoolean(a)||c.isArray(a)||e.DataField.isBinary(a))throw new Error("Invalid selector: "+a);return this.buildSqlWhere(a)()},buildSqlWhere:function(a){var b=this,d=[];return c.each(a,function(a,e){if("$"===e.substr(0,1))d.push(b.buildLogicalOperator(e,a));else{var f=b.buildLookup(e),g=b.buildValueSelector(a);c.isFunction(g)&&d.push(function(){return g(f)})}}),function(){var a="";return c.each(d,function(d){c.isFunction(d)&&(a+=d.call(b))}),a}},buildValueSelector:function(a){var b=this;if(null===a)return function(a){return a+" IS NULL"};if(!c.isObject(a))return function(c){return c+" = "+b.buildValue(a)};if(c.isRegExp(a)){var d=a.toString(),e=d.match(/\/[\^]?([^^.*$'+()]*)[\$]?\//);if(e&&e.length>1){var f=d.indexOf("/^")<0?"%":"",g=d.indexOf("$/")<0?"%":"";return function(a){return a+' LIKE "'+f+e[1]+g+'"'}}return null}if(c.isArray(a))return null;if(this.hasOperators(a)){var h=[];return c.each(a,function(a,d){if(!c.has(b.VALUE_OPERATORS,d))throw new Error("Unrecognized operator: "+d);h.push(b.VALUE_OPERATORS[d](a,b))}),function(a){return b.LOGICAL_OPERATORS.$and(h,a)}}return function(c){return c+" = "+b.buildValue(a)}},buildLookup:function(a){var b=this._entity?this._entity.getField(a):null;return a=b&&b.name?b.name:a,'"'+a+'"'},buildValue:function(a){return c.isString(a)?'"'+a.replace(/"/g,'""')+'"':a},buildLogicalOperator:function(a,b){if(c.has(this.LOGICAL_OPERATORS,a)){if(!c.isArray(b)||c.isEmpty(b))throw new Error("$and/$or/$nor must be nonempty array");var d=c.map(b,this.buildSqlWhere,this),e=this;return function(b){return e.LOGICAL_OPERATORS[a](d,b)}}throw new Error("Unrecognized logical operator: "+a)},LOGICAL_OPERATORS:{$and:function(a,b){var d="",e=0;return c.each(a,function(a){var c=null!==a?a(b):"";c&&(e++,d+=d?" AND "+c:c)}),e>1?"( "+d+" )":d},$or:function(a,b){var d="",e=!1;return c.each(a,function(a){var c=null!==a?a(b):"";e|=!c,d+=d&&c?" OR "+c:c}),e?"":"( "+d+" )"},$nor:function(a,b){var d="",e=!1;return c.each(a,function(a){var c=null!==a?a(b):"";e|=!c,d+=d&&c?" OR "+c:c}),e?"":"NOT ( "+d+" )"}},VALUE_OPERATORS:{$in:function(){return null},$all:function(){return null},$lt:function(a,b){return function(c){return c+" < "+b.buildValue(a)}},$lte:function(a,b){return function(c){return c+" <= "+b.buildValue(a)}},$gt:function(a,b){return function(c){return c+" > "+b.buildValue(a)}},$gte:function(a,b){return function(c){return c+"">""+b.buildValue(a)}},$ne:function(a,b){return function(c){return c+" <> "+b.buildValue(a)}},$nin:function(){return null},$exists:function(){return function(a){return a+" IS NOT NULL"}},$mod:function(){return null},$size:function(){return null},$type:function(){return null},$regex:function(){return null},$options:function(){return null},$elemMatch:function(){return null},$not:function(a,b){var c=b.buildSqlWhere(a);return function(a){return"NOT ("+c(a)+")"}}}}),e.Store=function(){this.initialize.apply(this,arguments)},e.Store.extend=e.extend,e.Store.create=e.create,e.Store.design=e.design,c.extend(e.Store.prototype,b.Events,e.Object,{_type:"Bikini.Store",entities:null,options:null,name:"",typeMapping:function(){var a={};return a[e.DATA.TYPE.OBJECTID]=e.DATA.TYPE.STRING,a[e.DATA.TYPE.DATE]=e.DATA.TYPE.STRING,a[e.DATA.TYPE.BINARY]=e.DATA.TYPE.TEXT,a}(),initialize:function(a){a=a||{},this.options=this.options||{},this.options.name=this.name,this.options.typeMapping=this.typeMapping,this.options.entities=this.entities,c.extend(this.options,a||{}),this._setEntities(a.entities||{})},_setEntities:function(a){this.entities={};for(var b in a){var c=e.Entity.from(a[b],{store:this,typeMapping:this.options.typeMapping});c.name=c.name||b;var d=c.collection||e.Collection.extend({model:e.Model.extend({})}),f=d.prototype.model;d.prototype.entity=f.prototype.entity=b,d.prototype.store=f.prototype.store=this,c.idAttribute=c.idAttribute||f.prototype.idAttribute,this.entities[b]=c
}},getEntity:function(a){if(a){var b=a.entity||a,d=c.isString(b)?b:b.name;if(d)return this.entities[d]||(b&&b.name?b:{name:d})}},getCollection:function(a){return c.isString(a)&&(a=this.entities[a]),a&&a.collection?e.Collection.prototype.isPrototypeOf(a.collection)?a.collection:new a.collection:void 0},createModel:function(a,b){if(c.isString(a)&&(a=this.entities[a]),a&&a.collection){var d=a.collection.model||a.collection.prototype.model;if(d)return new d(b)}},getArray:function(a){return c.isArray(a)?a:e.isCollection(a)?a.models:c.isObject(a)?[a]:[]},getDataArray:function(a){var d=[];if(c.isArray(a)||b.Collection.prototype.isPrototypeOf(a))c.each(a,function(a){var b=this.getAttributes(a);b&&d.push(b)});else{var e=this.getAttributes(a);e&&d.push(this.getAttributes(e))}return d},getAttributes:function(a){return b.Model.prototype.isPrototypeOf(a)?a.attributes:c.isObject(a)?a:null},initModel:function(){},initCollection:function(){},initEntity:function(){},sync:function(){},fetch:function(a,b){if(!a||a.models||a.attributes||b||(b=a),a&&(a.models||a.attributes)||!b||!b.entity||(a=this.getCollection(b.entity)),a&&a.fetch){var d=c.extend({},b||{},{store:this});return a.fetch(d)}},create:function(a,b,d){if(!a||a.models||d||(b=a,d=b),a&&a.models||!d||!d.entity||(a=this.getCollection(d.entity)),a&&a.create){var e=c.extend({},d||{},{store:this});a.create(b,e)}},save:function(a,b,d){if(!a||a.attributes||d||(b=a,d=b),a&&a.attributes||!d||!d.entity||(a=this.createModel(d.entity)),a&&a.save){var e=c.extend({},d||{},{store:this});a.save(b,e)}},destroy:function(a,b){if(a&&a.destroy){var d=c.extend({},b||{},{store:this});a.destroy(d)}},_checkEntity:function(a,b){if(!e.isEntity(b)){var c=e.Store.CONST.ERROR_NO_ENTITY;return console.error(c),this.handleCallback(a.error,c),this.handleCallback(a.finish,c),!1}return!0},_checkData:function(a,b){if(!(c.isArray(b)&&0!==b.length||c.isObject(b))){var d=e.Store.CONST.ERROR_NO_DATA;return console.error(d),this.handleCallback(a.error,d),this.handleCallback(a.finish,d),!1}return!0},handleSuccess:function(a){var b=Array.prototype.slice.call(arguments,1);a.success&&this.handleCallback.apply(this,[a.success].concat(b)),a.finish&&this.handleCallback.apply(this,[a.finish].concat(b))},handleError:function(a){var b=Array.prototype.slice.call(arguments,1);a.error&&this.handleCallback.apply(this,[a.error].concat(b)),a.finish&&this.handleCallback.apply(this,[a.finish].concat(b))},CONST:{ERROR_NO_ENTITY:"No valid entity specified. ",ERROR_NO_DATA:"No data passed. ",ERROR_LOAD_DATA:"Error while loading data from store. ",ERROR_SAVE_DATA:"Error while saving data to the store. ",ERROR_LOAD_IDS:"Error while loading ids from store. ",ERROR_SAVE_IDS:"Error while saving ids to the store. "}}),e.LocalStorageStore=e.Store.extend({_type:"Bikini.LocalStorageStore",ids:{},sync:function(a,b,d){d=d||{};var f,g=d.store||this.store,h=g.getEntity(b.entity||d.entity||this.entity);if(g&&h&&b){var i=b.id||("create"===a?(new e.ObjectID).toHexString():null);switch(f=d.attrs||b.toJSON(d),a){case"patch":case"update":case"create":"create"!==a&&(f=c.extend(g._getItem(h,i)||{},f)),b.id!==i&&b.idAttribute&&(f[b.idAttribute]=i),g._setItem(h,i,f);break;case"delete":g._removeItem(h,i);break;case"read":if(i)f=g._getItem(h,i);else{f=[];var j=g._getItemIds(h);for(i in j){var k=g._getItem(h,i);k&&f.push(k)}}break;default:return}}f?g.handleSuccess(d,f):g.handleError(d,e.Store.CONST.ERROR_NO_ENTITY)},drop:function(a){var b=this.getEntity(a);if(b&&b.name){for(var c=this._findAllKeys(b),d=0;d<c.length;d++)localStorage.removeItem(c[d]);localStorage.removeItem("__ids__"+b.name),this.handleSuccess(a)}else this.handleError(a,e.Store.CONST.ERROR_NO_ENTITY)},_getKey:function(a,b){return"_"+a.name+"_"+b},_getItem:function(a,b){var c;if(a&&b)try{c=JSON.parse(localStorage.getItem(this._getKey(a,b))),c?a.setId(c,b):this._delItemId(b)}catch(d){console.error(e.Store.CONST.ERROR_LOAD_DATA+d.message)}return c},_setItem:function(a,b,c){if(a&&b&&c)try{localStorage.setItem(this._getKey(a,b),JSON.stringify(c)),this._addItemId(a,b)}catch(d){console.error(e.Store.CONST.ERROR_SAVE_DATA+d.message)}},_removeItem:function(a,b){a&&b&&(localStorage.removeItem(this._getKey(a,b)),this._delItemId(a,b))},_addItemId:function(a,b){var c=this._getItemIds(a);b in c||(c[b]="",this._saveItemIds(a,c))},_delItemId:function(a,b){var c=this._getItemIds(a);b in c&&(delete c[b],this._saveItemIds(a,c))},_findAllKeys:function(a){var b=[],c=this._getKey(a,"");if(c)for(var d,e=localStorage.length,f=0;e>f;f++)d=localStorage.key(f),d&&d===c&&b.push(d);return b},_getItemIds:function(a){try{var b="__ids__"+a.name;return this.ids[a.name]||(this.ids[a.name]=JSON.parse(localStorage.getItem(b))||{}),this.ids[a.name]}catch(c){console.error(e.Store.CONST.ERROR_LOAD_IDS+c.message)}},_saveItemIds:function(a,b){try{var c="__ids__"+a.name;localStorage.setItem(c,JSON.stringify(b))}catch(d){console.error(e.Store.CONST.ERROR_SAVE_IDS+d.message)}}}),e.WebSqlStore=e.Store.extend({_type:"Bikini.WebSqlStore",_selector:null,options:null,name:"bikini",size:1048576,version:"1.0",db:null,dataField:{name:"data",type:"text",required:!0},idField:{name:"id",type:"string",required:!0},typeMapping:function(){var a={};return a[e.DATA.TYPE.OBJECTID]=e.DATA.TYPE.STRING,a[e.DATA.TYPE.DATE]=e.DATA.TYPE.STRING,a[e.DATA.TYPE.OBJECT]=e.DATA.TYPE.TEXT,a[e.DATA.TYPE.ARRAY]=e.DATA.TYPE.TEXT,a[e.DATA.TYPE.BINARY]=e.DATA.TYPE.TEXT,a}(),sqlTypeMapping:function(){var a={};return a[e.DATA.TYPE.STRING]="varchar(255)",a[e.DATA.TYPE.TEXT]="text",a[e.DATA.TYPE.OBJECT]="text",a[e.DATA.TYPE.ARRAY]="text",a[e.DATA.TYPE.FLOAT]="float",a[e.DATA.TYPE.INTEGER]="integer",a[e.DATA.TYPE.DATE]="varchar(255)",a[e.DATA.TYPE.BOOLEAN]="boolean",a}(),initialize:function(a){e.Store.prototype.initialize.apply(this,arguments),this.options=this.options||{},this.options.name=this.name,this.options.size=this.size,this.options.version=this.version,this.options.typeMapping=this.typeMapping,this.options.sqlTypeMapping=this.sqlTypeMapping,c.extend(this.options,a||{}),this._openDb({error:function(a){console.error(a)}})},sync:function(a,b,c){var f=c.store||this.store,g=e.isCollection(b)?b.models:[b],h=new d.Deferred,i=c.success;switch(c.success&&(c.success=function(a){h.resolve(a),i(a)}),c.entity=c.entity||this.entity,a){case"create":f._checkTable(c,function(){f._insertOrReplace(g,c)});break;case"update":case"patch":f._checkTable(c,function(){f._insertOrReplace(g,c)});break;case"delete":f._delete(g,c);break;case"read":f._select(this,c)}return h.promise()},select:function(a){this._select(null,a)},drop:function(a){this._dropTable(a)},createTable:function(a){this._createTable(a)},execute:function(a){this._executeSql(a)},_openDb:function(a){var b,c;if(!this.db)try{if(window.openDatabase){if(this.db=window.openDatabase(this.options.name,"","",this.options.size),this.entities)for(var d in this.entities)this._createTable({entity:this.entities[d]})}else b="Your browser does not support WebSQL databases."}catch(e){c=e}this.db?this.options.version&&this.db.version!==this.options.version?this._updateDb(a):this.handleSuccess(a,this.db):2===c||"2"===c?this._updateDb(a):(!b&&c&&(b=c),this.handleSuccess(a,b))},_updateDb:function(a){var b,d,e=this;try{var f=window.openDatabase(this.options.name,"","",this.options.size);try{var g=this._sqlUpdateDatabase(f.version,this.options.version);f.changeVersion(f.version,this.options.version,function(a){c.each(g,function(b){console.log("sql statement: "+b),d=b,a.executeSql(b)})},function(b){e.handleError(a,b,d)},function(){e.handleSuccess(a)})}catch(h){b=h.message,console.error("webSql change version failed, DB-Version: "+f.version)}}catch(h){b=h.message}b&&this.handleError(a,b)},_sqlUpdateDatabase:function(){var a=[];if(this.entities)for(var b in this.entities){var c=this.entities[b];a.push(this._sqlDropTable(c.name)),a.push(this._sqlCreateTable(c))}return a},_sqlDropTable:function(a){return"DROP TABLE IF EXISTS '"+a+"'"},_isAutoincrementKey:function(a,b){if(a&&b){var c=this.getField(a,b);return c&&c.type===e.DATA.TYPE.INTEGER}},_sqlPrimaryKey:function(a,b){return b&&1===b.length?this._isAutoincrementKey(a,b[0])?b[0]+" INTEGER PRIMARY KEY ASC AUTOINCREMENT UNIQUE":b[0]+" PRIMARY KEY ASC UNIQUE":""},_sqlConstraint:function(a,b){return b&&b.length>1?"PRIMARY KEY ("+b.join(",")+") ON CONFLICT REPLACE":""},_sqlCreateTable:function(a){var b=this,d=a.getKeys(),e=1===d.length?this._sqlPrimaryKey(a,d):"",f=d.length>1?this._sqlConstraint(a,d):a.constraint||"",g="",h=this.getFields(a);c.each(h,function(a){if(!e||a.name!==d[0]){var c=b._dbAttribute(a);c&&(g+=(g?", ":"")+c)}}),g||(g=this._dbAttribute(this.dataField));var i="CREATE TABLE IF NOT EXISTS '"+a.name+"' (";return i+=e?e+", ":"",i+=g,i+=f?", "+f:"",i+=");"},_sqlDelete:function(a,b){var c="DELETE FROM '"+b.name+"'",d=this._sqlWhere(a,b)||this._sqlWhereFromData(a,b);return d&&(c+=" WHERE "+d),c+=a.and?" AND "+a.and:""},_sqlWhere:function(a,b){this._selector=null;var d="";return c.isString(a.where)?d=a.where:c.isObject(a.where)&&(this._selector=e.SqlSelector.create(a.where,b),d=this._selector.buildStatement()),d},_sqlWhereFromData:function(a,b){var d=this,e=[];if(a&&a.models&&b&&b.idAttribute){var f,g=b.idAttribute,h=this.getField(b,g);if(c.each(a.models,function(a){f=a.id,c.isUndefined(f)||e.push(d._sqlValue(f,h))}),e.length>0)return g+" IN ("+e.join(",")+")"}return""},_sqlSelect:function(a,b){var c="SELECT ";a.fields?a.fields.length>1?c+=a.fields.join(", "):1===a.fields.length&&(c+=a.fields[0]):c+="*",c+=" FROM '"+b.name+"'",a.join&&(c+=" JOIN "+a.join),a.leftJoin&&(c+=" LEFT JOIN "+a.leftJoin);var d=this._sqlWhere(a,b)||this._sqlWhereFromData(a,b);return d&&(c+=" WHERE "+d),a.order&&(c+=" ORDER BY "+a.order),a.limit&&(c+=" LIMIT "+a.limit),a.offset&&(c+=" OFFSET "+a.offset),c},_sqlValue:function(a,b){var c=b&&b.type?b.type:e.Field.prototype.detectType(a);return c===e.DATA.TYPE.INTEGER||c===e.DATA.TYPE.FLOAT?a:c===e.DATA.TYPE.BOOLEAN?a?"1":"0":c===e.DATA.TYPE.NULL?"NULL":(a=e.Field.prototype.transform(a,e.DATA.TYPE.STRING),a=a.replace(/"/g,'""'),'"'+a+'"')},_dbAttribute:function(a){if(a&&a.name){var b=this.options.sqlTypeMapping[a.type],c=a.required?" NOT NULL":"";if(b)return a.name+" "+b.toUpperCase()+c}},_dropTable:function(a){var b=this.getEntity(a);if(b.db=null,this._checkDb(a)&&b){var c=this._sqlDropTable(b.name);this._executeTransaction(a,[c])}},_createTable:function(a){var b=this.getEntity(a);if(b.db=this.db,this._checkDb(a)&&this._checkEntity(a,b)){var c=this._sqlCreateTable(b);this._executeTransaction(a,[c])}},_checkTable:function(a,b){var c=this.getEntity(a);c&&!c.db?this._createTable({success:function(){b()},error:function(b){this.handleError(a,b)},entity:c}):b()},_insertOrReplace:function(a,b){var d=this.getEntity(b);if(this._checkDb(b)&&this._checkEntity(b,d)&&this._checkData(b,a)){for(var f=this._isAutoincrementKey(d,d.getKey()),g=[],h="INSERT OR REPLACE INTO '"+d.name+"' (",i=0;i<a.length;i++){var j=a[i],k="";f||j.id||!j.idAttribute||j.set(j.idAttribute,(new e.ObjectID).toHexString());var l,m,n=b.attrs||j.toJSON();if(c.isEmpty(d.fields)?(l=[j.id,JSON.stringify(n)],m=["id","data"]):(l=c.values(n),m=c.keys(n)),l.length>0){var o=new Array(l.length).join("?,")+"?",p="'"+m.join("','")+"'";k+=h+p+") VALUES ("+o+");",g.push({statement:k,arguments:l})}}this._executeTransaction(b,g)}},_select:function(a,b){var d=this.getEntity(b);if(this._checkDb(b)&&this._checkEntity(b,d)){var f,g=e.isCollection(a);g?a=[]:b.models=[a];var h=this._sqlSelect(b,d),i=this;this.db.readTransaction(function(b){var e=h.statement||h,j=h.arguments;f=e,console.log("sql statement: "+e),j&&console.log(" arguments: "+JSON.stringify(j)),b.executeSql(e,j,function(b,e){for(var f=e.rows.length,h=0;f>h;h++){var j,k=e.rows.item(h);if(c.isEmpty(d.fields)&&i._hasDefaultFields(k))try{j=JSON.parse(k.data)}catch(l){}else j=k;if(j&&(!i._selector||i._selector.matches(j))){if(!g){a=j;break}a.push(j)}}},function(a,b){console.error("webSql error: "+b.message)})},function(a){console.error("WebSql Syntax Error: "+a.message),i.handleError(b,a.message,f)},function(){i.handleSuccess(b,a)})}},_delete:function(a,b){var c=this.getEntity(b);if(this._checkDb(b)&&this._checkEntity(b,c)){b.models=a;var d=this._sqlDelete(b,c);this._executeTransaction(b,[d])}},_executeSql:function(a){a.sql&&this._executeTransaction(a,[a.sql])},_executeTransaction:function(a,b){var d,e;if(this._checkDb(a)){var f=this;try{this.db.transaction(function(a){c.each(b,function(b){var c=b.statement||b,d=b.arguments;e=c,console.log("sql statement: "+c),d&&console.log(" arguments: "+JSON.stringify(d)),a.executeSql(c,d)})},function(b){console.error(b.message),f.handleError(a,b.message,e)},function(){f.handleSuccess(a)})}catch(g){console.error(g.message)}}d&&this.handleCallback(a.error,d,e)},_hasDefaultFields:function(a){return c.every(c.keys(a),function(a){return a===this.idField.name||a===this.dataField.name},this)},_checkDb:function(a){if(!this.db){var b="db handler not initialized.";return console.error(b),this.handleError(a,b),!1}return!0},getFields:function(a){if(c.isEmpty(a.fields)){var b={};b.data=this.dataField;var d=a.idAttribute||"id";return b[d]=this.idField,b}return a.fields},getField:function(a,b){return this.getFields(a)[b]}}),e.BikiniStore=e.Store.extend({_type:"Bikini.BikiniStore",_selector:null,endpoints:{},options:null,localStore:e.WebSqlStore,useLocalStore:YES,useSocketNotify:YES,useOfflineChanges:YES,isConnected:NO,typeMapping:{binary:"text",date:"string"},initialize:function(a){e.Store.prototype.initialize.apply(this,arguments),this.options=this.options||{},this.options.useLocalStore=this.useLocalStore,this.options.useSocketNotify=this.useSocketNotify,this.options.useOfflineChanges=this.useOfflineChanges,this.options.socketPath=this.socketPath,this.options.localStore=this.localStore,this.options.typeMapping=this.typeMapping,this.options.useSocketNotify&&"object"!=typeof io&&(console.log("Socket.IO not present !!"),this.options.useSocketNotify=NO),c.extend(this.options,a||{})},initModel:function(){},initCollection:function(a){var b=a.getUrlRoot();"/"!==b.charAt(b.length-1)&&(b+="/");var c=this.getEntity(a.entity);if(b&&c){var d=c.name,e=this._locationBasedHashCode(b),f=c.credentials||a.credentials,g=f&&f.username?f.username:"",h=d+g+e;a.channel=h;var i=this,j=this.endpoints[e];if(!j){var k=this.getLocation(b);j={},j.baseUrl=b,j.readUrl=a.getUrl(),j.host=k.protocol+"//"+k.host,j.path=k.pathname,j.entity=c,j.channel=h,j.credentials=f,j.socketPath=this.options.socketPath,j.localStore=this.createLocalStore(j),j.messages=this.createMsgCollection(j),j.socket=this.createSocket(j),j.info=this.fetchServerInfo(j),i.endpoints[e]=j}a.endpoint=j,a.listenTo(this,j.channel,this.onMessage,a)}},getEndpoint:function(a){if(a){var b=this._locationBasedHashCode(a);return this.endpoints[b]}},createLocalStore:function(a,b){if(this.options.useLocalStore&&a){var c={};return c[a.entity.name]={name:a.channel,idAttribute:b},this.options.localStore.create({entities:c})}},createMsgCollection:function(a){if(this.options.useOfflineChanges&&a){var b=e.Collection.design({url:a.url,entity:"msg-"+a.channel,store:this.options.localStore.create()}),c=this;return b.fetch({success:function(){c.sendMessages(a)}}),b}},createSocket:function(a,b){if(this.options.useSocketNotify&&a&&a.socketPath){var c=this,d=a.host,e=a.path,f=this.getLocation(d);""===f.port&&("https:"===f.protocol?d+=":443":"http:"===f.protocol&&(d+=":80")),e=a.socketPath;var g=e&&0===e.indexOf("/")?e.substr(1):e;return a.socket=io.connect(d,{resource:g}),a.socket.on("connect",function(){c._bindChannel(a,b),c.onConnect(a)}),a.socket.on("disconnect",function(){console.log("socket.io: disconnect"),c.onDisconnect(a)}),a.socket}},_bindChannel:function(a,b){var c=this;if(a&&a.socket){var d=a.channel,e=a.socket,f=this.getLastMessageTime(d);b=b||a.entity.name,e.on(d,function(a){a&&(c.trigger(d,a),c.options.useLocalStore&&c.setLastMessageTime(d,a.time))}),e.emit("bind",{entity:b,channel:d,time:f})}},getLastMessageTime:function(a){return localStorage.getItem("__"+a+"last_msg_time")||0},setLastMessageTime:function(a,b){b&&localStorage.setItem("__"+a+"last_msg_time",b)},_hashCode:function(a){var b,c=0;if(0===a.length)return c;for(var d=0,e=a.length;e>d;d++)b=a.charCodeAt(d),c=(c<<5)-c+b,c|=0;return c},_locationBasedHashCode:function(a){return this._hashCode(this._getLocationUrl(a))},_getLocationUrl:function(a){return this.getLocation(a).toString()},_getLocation:function(a){var b=document.createElement("a");return b.href=a||this.url,""===b.host&&(b.href=b.href),b},onConnect:function(a){this.isConnected=YES,this.fetchChanges(a),this.sendMessages(a)},onDisconnect:function(a){this.isConnected=NO,a.socket&&a.socket.socket&&a.socket.socket.onDisconnect()},onMessage:function(a){if(a){var b=this.endpoint?this.endpoint.localStore:null,c=null,d=null,e=null,f={store:b,entity:this.entity,merge:YES,fromMessage:YES,parse:YES};switch(a.id&&a.method?(c=a.data||{},d=a.method,e=a.id):a.attributes&&(c=a.attributes.data,d=a.attributes.method,e=a.attributes.id),d){case"patch":case"update":case"create":f.patch="patch"===d;var g=e?this.get(e):null;g?g.save(c,f):this.create(c,f);break;case"delete":if(e)if("all"===e){for(;g=this.first();)b&&b.sync.apply(this,["delete",g,{store:b,fromMessage:YES}]),this.remove(g);this.store.setLastMessageTime(this.endpoint.channel,"")}else{var h=this.get(e);h&&h.destroy(f)}}}},sync:function(a,b,c){var d=c.store||this.store;if(c.fromMessage)return d.handleCallback(c.success);var f=d.getEndpoint(this.getUrlRoot()),g=null;if(d&&f){var h=this.channel;e.isModel(b)&&!b.id&&b.set(b.idAttribute,(new e.ObjectID).toHexString());var i=d.getLastMessageTime(h);return"read"===a&&f.localStore&&i?"read"===a&&(g=d.fetchChanges(f)):g=d.addMessage(a,b,f.localStore?{}:c,f),f.localStore&&(c.store=f.localStore,f.localStore.sync.apply(this,arguments)),g}},addMessage:function(a,b,d,e){var f=this;if(a&&b){var g=b.changedSinceSync,h=null,i=YES;switch(a){case"update":case"create":h=d.attrs||b.toJSON();break;case"patch":if(c.isEmpty(g))return;h=b.toJSON({attrs:g});break;case"delete":break;default:i=NO}var j={_id:b.id,id:b.id,method:a,data:h},k=function(a,c){return f.emitMessage(a,c,d,b)};return i?this.storeMessage(e,j,k):k(e,j)}},emitMessage:function(a,b,d,f){var g=a.channel,h=this,i=e.isModel(f)||"read"!==b.method?a.baseUrl:a.readUrl;return b.id&&"create"!==b.method&&(i+=("/"===i.charAt(i.length-1)?"":"/")+b.id),f.sync.apply(f,[b.method,f,{url:i,error:function(c,e){!c.responseText&&h.options.useOfflineChanges?(h.onDisconnect(a),h.handleCallback(d.success,b.data)):h.removeMessage(a,b,function(){h.handleCallback(d.error,e)})},success:function(e){h.isConnected||h.onConnect(a),h.removeMessage(a,b,function(a,b){if(d.success){var f=e;h.handleCallback(d.success,f)}else if("read"===b.method)for(var i=c.isArray(e)?e:[e],j=0;j<i.length;j++)e=i[j],e&&h.trigger(g,{id:e._id,method:"update",data:e});else h.trigger(g,b)})},store:{}}])},fetchChanges:function(a){var b=this,c=a?a.channel:"",d=b.getLastMessageTime(c);if(a&&a.baseUrl&&c&&d){var f=new e.Collection({});return f.fetch({url:a.baseUrl+"changes/"+d,success:function(a,d,e){return f.each(function(a){a.get("time")&&a.get("method")&&(b.options.useLocalStore&&b.setLastMessageTime(c,a.get("time")),b.trigger(c,a))}),e.xhr},credentials:a.credentials})}},fetchServerInfo:function(a){var b=this;if(a&&a.baseUrl){var c=new e.Model,d=b.getLastMessageTime(a.channel),f=a.baseUrl;return"/"!==f.charAt(f.length-1)&&(f+="/"),c.fetch({url:f+"info",success:function(e,f,g){if(!d&&c.get("time")&&b.setLastMessageTime(a.channel,c.get("time")),!a.socketPath&&c.get("socketPath")){a.socketPath=c.get("socketPath");var h=c.get("entity")||a.entity.name;b.options.useSocketNotify&&b.createSocket(a,h)}return g.xhr},credentials:a.credentials})}},sendMessages:function(a){if(a&&a.messages){var b=this;a.messages.each(function(c){var d;try{d=JSON.parse(c.get("msg"))}catch(e){}var f=c.get("channel");if(d&&f){var g=b.createModel({collection:a.messages},d.data);b.emitMessage(a,d,{},g)}else c.destroy()})}},mergeMessages:function(a){return a},storeMessage:function(a,b,d){if(a&&a.messages&&b){var e=a.channel,f=a.messages.get(b._id);if(f){var g=JSON.parse(f.get("msg"));f.save({msg:JSON.stringify(c.extend(g,b))})}else a.messages.create({_id:b._id,id:b.id,msg:JSON.stringify(b),channel:e})}return d(a,b)},removeMessage:function(a,b,c){if(a&&a.messages){var d=a.messages.get(b._id);d&&d.destroy()}return c(a,b)},clear:function(a){if(a){var b=this.getEndpoint(a.getUrlRoot());b&&(b.messages&&b.messages.destroy(),a.reset(),this.setLastMessageTime(b.channel,""))}},getLocation:function(a){var b=document.createElement("a");return b.href=a||this.url,""===b.host&&(b.href=b.href),b}})}(this,Backbone,_,$);
!function(a,b,c,d){var e=null;e="undefined"!=typeof exports?exports:a.Bikini={},e.Version=e.version="0.6.3",e.f=function(){},e.create=function(a){return new this(a)},e.design=function(a){var b=this.extend(a||{});return new b},e.extend=b.Model.extend,e.isCollection=function(a){return b.Collection.prototype.isPrototypeOf(a)},e.isModel=function(a){return b.Model.prototype.isPrototypeOf(a)},e.isEntity=function(a){return e.Entity.prototype.isPrototypeOf(a)},e.DATA={TYPE:{INTEGER:"integer",STRING:"string",TEXT:"text",DATE:"date",BOOLEAN:"boolean",FLOAT:"float",OBJECT:"object",ARRAY:"array",BINARY:"binary",OBJECTID:"objectid",NULL:"null"}},e.Object={_type:"Bikini.Object",_create:function(a){var b=function(){};return b.prototype=a,new b},include:function(a){for(var b in a){if(this.hasOwnProperty(b))throw e.Exception.RESERVED_WORD.getException();this[b]=a[b]}return this},design:function(a){var b=this._create(this);return b.include(this._normalize(a)),b},bindToCaller:function(a,b,c){return function(){if("function"!=typeof b||"object"!=typeof a)throw e.Exception.INVALID_INPUT_PARAMETER.getException();return Array.isArray(c)?b.apply(a,c):b.call(a,c)}},_normalize:function(a){return a=a&&"object"==typeof a?a:{}},handleCallback:function(a){var b=Array.prototype.slice.call(arguments,1);if(a){var c="object"==typeof a.target?a.target:this,d=a;if("function"==typeof a.action?d=a.action:"string"==typeof a.action&&(d=c[a.action]),"function"==typeof d)return this.bindToCaller(c,d,b)()}}},YES=!0,NO=!1,e.ObjectID=function(a){e.ObjectID.counter=e.ObjectID.counter||parseInt(Math.random()*Math.pow(16,6)),e.ObjectID.machineId=e.ObjectID.machineId||parseInt(Math.random()*Math.pow(16,6)),e.ObjectID.processId=e.ObjectID.processId||parseInt(Math.random()*Math.pow(16,4)),this._ObjectID(a)},e.ObjectID._looksLikeObjectID=function(a){return 24===a.length&&a.match(/^[0-9a-f]*$/)},c.extend(e.ObjectID.prototype,{_str:"",_ObjectID:function(a){if(a){if(a=a.toLowerCase(),!e.ObjectID._looksLikeObjectID(a))throw new Error("Invalid hexadecimal string for creating an ObjectID");this._str=a}else this._str=this._hexString(8,(new Date).getTime()/1e3)+this._hexString(6,e.ObjectID.machineId)+this._hexString(4,e.ObjectID.processId)+this._hexString(6,e.ObjectID.counter++);return this._str},_hexString:function(a,b){b=b||parseInt(Math.random()*Math.pow(16,a));for(var c=b.toString(16);c.length<a;)c="0"+c;return c.substr(0,a)},toString:function(){return"ObjectID('"+this._str+"')"},equals:function(a){return a instanceof this._ObjectID&&this.valueOf()===a.valueOf()},clone:function(){return new e.ObjectID(this._str)},typeName:function(){return"oid"},getTimestamp:function(){return 1e3*parseInt(this._str.substr(0,8),16)},getMachineId:function(){return parseInt(this._str.substr(8,6),16)},getProcessId:function(){return parseInt(this._str.substr(14,4),16)},getCounter:function(){return parseInt(this._str.substr(18,6),16)},valueOf:function(){return this._str},toJSON:function(){return this._str},toHexString:function(){return this._str},_selectorIsId:function(a){return"string"==typeof a||"number"==typeof a||a instanceof e.ObjectId},_selectorIsIdPerhapsAsObject:function(a){return this._selectorIsId(a)||a&&"object"==typeof a&&a._id&&this._selectorIsId(a._id)&&1===c.size(a)},_idsMatchedBySelector:function(a){if(this._selectorIsId(a))return[a];if(!a)return null;if(c.has(a,"_id"))return this._selectorIsId(a._id)?[a._id]:a._id&&a._id.$in&&c.isArray(a._id.$in)&&!c.isEmpty(a._id.$in)&&c.all(a._id.$in,this._selectorIsId)?a._id.$in:null;if(a.$and&&c.isArray(a.$and))for(var b=0;b<a.$and.length;++b){var d=this._idsMatchedBySelector(a.$and[b]);if(d)return d}return null}}),e.UniqueId=e.Object.design({uuid:function(a,b){var c="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".split(""),d=[];b=b||c.length;var e;if(a)for(e=0;a>e;e++)d[e]=c[0|Math.random()*b];else{var f;for(d[8]=d[13]=d[18]=d[23]="-",d[14]="4",e=0;36>e;e++)d[e]||(f=0|16*Math.random(),d[e]=c[19===e?3&f|8:f])}return d.join("")}}),e.Base64=e.Object.design({type:"Bikini.Base64",_keyStr:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",encodeBinary:function(a){for(var b,c="",d=new Array(4),e=0,f=0;e<a.length;){b=new Array(3);for(var g=0;g<b.length;g++)b[g]=e<a.length?255&a.charCodeAt(e++):0;switch(d[0]=b[0]>>2,d[1]=(3&b[0])<<4|b[1]>>4,d[2]=(15&b[1])<<2|b[2]>>6,d[3]=63&b[2],f=e-(a.length-1)){case 2:d[3]=64,d[2]=64;break;case 1:d[3]=64}for(g=0;g<d.length;g++)c+=this._keyStr.charAt(d[g])}return c},encode:function(a){var b,c,d,f,g,h,i,j="",k=0;for(a=e.Cypher.utf8Encode(a);k<a.length;)b=a.charCodeAt(k++),c=a.charCodeAt(k++),d=a.charCodeAt(k++),f=b>>2,g=(3&b)<<4|c>>4,h=(15&c)<<2|d>>6,i=63&d,isNaN(c)?h=i=64:isNaN(d)&&(i=64),j+=this._keyStr.charAt(f)+this._keyStr.charAt(g)+this._keyStr.charAt(h)+this._keyStr.charAt(i);return j},binaryEncode:function(a){for(var b,c,d,e,f,g,h,i="",j=0;j<a.length;)b=a.charCodeAt(j++),c=a.charCodeAt(j++),d=a.charCodeAt(j++),e=b>>2,f=(3&b)<<4|c>>4,g=(15&c)<<2|d>>6,h=63&d,isNaN(c)?g=h=64:isNaN(d)&&(h=64),i+=this._keyStr.charAt(e)+this._keyStr.charAt(f)+this._keyStr.charAt(g)+this._keyStr.charAt(h);return i},decode:function(a){var b,c,d,f,g,h,i,j="",k=0;for(a=a.replace(/[^A-Za-z0-9\+\/\=]/g,"");k<a.length;)f=this._keyStr.indexOf(a.charAt(k++)),g=this._keyStr.indexOf(a.charAt(k++)),h=this._keyStr.indexOf(a.charAt(k++)),i=this._keyStr.indexOf(a.charAt(k++)),b=f<<2|g>>4,c=(15&g)<<4|h>>2,d=(3&h)<<6|i,j+=String.fromCharCode(b),64!==h&&(j+=String.fromCharCode(c)),64!==i&&(j+=String.fromCharCode(d));return e.Cypher.utf8Decode(j)}}),e.SHA256=e.Object.design({type:"Bikini.SHA256",chrsz:8,hexcase:0,hash:function(a){return a=e.Cypher.utf8Encode(a),this.binb2hex(this.coreSha256(this.str2binb(a),a.length*this.chrsz))},safeAdd:function(a,b){var c=(65535&a)+(65535&b),d=(a>>16)+(b>>16)+(c>>16);return d<<16|65535&c},S:function(a,b){return a>>>b|a<<32-b},R:function(a,b){return a>>>b},Ch:function(a,b,c){return a&b^~a&c},Maj:function(a,b,c){return a&b^a&c^b&c},Sigma0256:function(a){return this.S(a,2)^this.S(a,13)^this.S(a,22)},Sigma1256:function(a){return this.S(a,6)^this.S(a,11)^this.S(a,25)},Gamma0256:function(a){return this.S(a,7)^this.S(a,18)^this.R(a,3)},Gamma1256:function(a){return this.S(a,17)^this.S(a,19)^this.R(a,10)},coreSha256:function(a,b){var c,d,e,f,g,h,i,j,k,l,m,n,o=new Array(1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298),p=new Array(1779033703,3144134277,1013904242,2773480762,1359893119,2600822924,528734635,1541459225),q=new Array(64);for(a[b>>5]|=128<<24-b%32,a[(b+64>>9<<4)+15]=b,k=0;k<a.length;k+=16){for(c=p[0],d=p[1],e=p[2],f=p[3],g=p[4],h=p[5],i=p[6],j=p[7],l=0;64>l;l++)q[l]=16>l?a[l+k]:this.safeAdd(this.safeAdd(this.safeAdd(this.Gamma1256(q[l-2]),q[l-7]),this.Gamma0256(q[l-15])),q[l-16]),m=this.safeAdd(this.safeAdd(this.safeAdd(this.safeAdd(j,this.Sigma1256(g)),this.Ch(g,h,i)),o[l]),q[l]),n=this.safeAdd(this.Sigma0256(c),this.Maj(c,d,e)),j=i,i=h,h=g,g=this.safeAdd(f,m),f=e,e=d,d=c,c=this.safeAdd(m,n);p[0]=this.safeAdd(c,p[0]),p[1]=this.safeAdd(d,p[1]),p[2]=this.safeAdd(e,p[2]),p[3]=this.safeAdd(f,p[3]),p[4]=this.safeAdd(g,p[4]),p[5]=this.safeAdd(h,p[5]),p[6]=this.safeAdd(i,p[6]),p[7]=this.safeAdd(j,p[7])}return p},str2binb:function(a){for(var b=[],c=(1<<this.chrsz)-1,d=0;d<a.length*this.chrsz;d+=this.chrsz)b[d>>5]|=(a.charCodeAt(d/this.chrsz)&c)<<24-d%32;return b},binb2hex:function(a){for(var b=this.hexcase?"0123456789ABCDEF":"0123456789abcdef",c="",d=0;d<4*a.length;d++)c+=b.charAt(a[d>>2]>>8*(3-d%4)+4&15)+b.charAt(a[d>>2]>>8*(3-d%4)&15);return c}}),e.Cypher=e.Object.design({type:"Bikini.Cypher",defaultDecoder:e.Base64,defaultEncoder:e.Base64,defaultHasher:e.SHA256,decode:function(a,b){return b&&b.decode?b.decode(a):this.defaultDecoder.decode(a)},encode:function(a,b){return b&&b.encode?b.encode(a):this.defaultEncoder.encode(a)},hash:function(a,b){return b&&b.hash?b.hash(a):this.defaultHasher.hash(a)},utf8Encode:function(a){a=a.replace(/\r\n/g,"\n");for(var b="",c=0;c<a.length;c++){var d=a.charCodeAt(c);128>d?b+=String.fromCharCode(d):d>127&&2048>d?(b+=String.fromCharCode(d>>6|192),b+=String.fromCharCode(63&d|128)):(b+=String.fromCharCode(d>>12|224),b+=String.fromCharCode(d>>6&63|128),b+=String.fromCharCode(63&d|128))}return b},utf8Decode:function(a){var b,c,d,e,f,g="";for(b=c=d=e=0;b<a.length;)c=a.charCodeAt(b),128>c?(g+=String.fromCharCode(c),b++):c>191&&224>c?(e=a.charCodeAt(b+1),g+=String.fromCharCode((31&c)<<6|63&e),b+=2):(e=a.charCodeAt(b+1),f=a.charCodeAt(b+2),g+=String.fromCharCode((15&c)<<12|(63&e)<<6|63&f),b+=3);return g}}),e.Date={create:function(){var a=moment.apply(this,arguments);return c.extend(a,this)}},e.Field=function(a){this.merge(a),this.initialize.apply(this,arguments)},e.Field.extend=e.extend,e.Field.create=e.create,e.Field.design=e.design,c.extend(e.Field.prototype,e.Object,{_type:"Bikini.Field",name:null,type:null,index:null,defaultValue:void 0,length:null,required:NO,persistent:YES,initialize:function(){},merge:function(a){a=c.isString(a)?{type:a}:a||{},this.name=c.isUndefined(a.name)?this.name:a.name,this.type=c.isUndefined(a.type)?this.type:a.type,this.index=c.isUndefined(a.index)?this.index:a.index,this.defaultValue=c.isUndefined(a.defaultValue)?this.defaultValue:a.defaultValue,this.length=c.isUndefined(a.length)?this.length:a.length,this.required=c.isUndefined(a.required)?this.required:a.required,this.persistent=c.isUndefined(a.persistent)?this.persistent:a.persistent},transform:function(a,b){b=b||this.type;try{if(c.isUndefined(a))return this.defaultValue;if(b===e.DATA.TYPE.STRING||b===e.DATA.TYPE.TEXT)return c.isObject(a)?JSON.stringify(a):c.isNull(a)?"null":a.toString();if(b===e.DATA.TYPE.INTEGER)return parseInt(a);if(b===e.DATA.TYPE.BOOLEAN)return a===!0||"true"===a;if(b===e.DATA.TYPE.FLOAT)return parseFloat(a);if(b===e.DATA.TYPE.OBJECT||b===e.DATA.TYPE.ARRAY){if(!c.isObject(a))return c.isString(a)?JSON.parse(a):null}else if(b===e.DATA.TYPE.DATE){if(!e.Date.isPrototypeOf(a)){var d=a?e.Date.create(a):null;return d&&d.isValid()?d:null}}else if(b===e.DATA.TYPE.OBJECTID&&!e.ObjectID.prototype.isPrototypeOf(a))return c.isString(a)?new e.ObjectID(a):null;return a}catch(f){console.error("Failed converting value! "+f.message)}},equals:function(a,b){var d=this.transform(a),e=this.transform(b);return this._equals(d,e,c.isArray(d))},isBinary:function(a){return"undefined"!=typeof Uint8Array&&a instanceof Uint8Array||a&&a.$Uint8ArrayPolyfill},detectType:function(a){return c.isNumber(a)?e.DATA.TYPE.FLOAT:c.isString(a)?e.DATA.TYPE.STRING:c.isBoolean(a)?e.DATA.TYPE.BOOLEAN:c.isArray(a)?e.DATA.TYPE.ARRAY:c.isNull(a)?e.DATA.TYPE.NULL:c.isDate(a)||e.Date.isPrototypeOf(a)?e.DATA.TYPE.DATE:e.ObjectID.prototype.isPrototypeOf(a)?e.DATA.TYPE.OBJECTID:this.isBinary(a)?e.DATA.TYPE.BINARY:e.DATA.TYPE.OBJECT},typeOrder:function(a){switch(a){case e.DATA.TYPE.NULL:return 0;case e.DATA.TYPE.FLOAT:return 1;case e.DATA.TYPE.STRING:return 2;case e.DATA.TYPE.OBJECT:return 3;case e.DATA.TYPE.ARRAY:return 4;case e.DATA.TYPE.BINARY:return 5;case e.DATA.TYPE.DATE:return 6}return-1},_equals:function(a,b,d){var e,f=this;if(a===b)return!0;if(!a||!b)return!1;if(!c.isObject(a)||!c.isObject(b))return!1;if(a instanceof Date&&b instanceof Date)return a.valueOf()===b.valueOf();if(this.isBinary(a)&&this.isBinary(b)){if(a.length!==b.length)return!1;for(e=0;e<a.length;e++)if(a[e]!==b[e])return!1;return!0}if(c.isFunction(a.equals))return a.equals(b);if(c.isArray(a)){if(!c.isArray(b))return!1;if(a.length!==b.length)return!1;for(e=0;e<a.length;e++)if(!f.equals(a[e],b[e],d))return!1;return!0}var g;if(d){var h=[];return c.each(b,function(a,b){h.push(b)}),e=0,g=c.all(a,function(a,c){return e>=h.length?!1:c!==h[e]?!1:f.equals(a,b[h[e]],d)?(e++,!0):!1}),g&&e===h.length}return e=0,g=c.all(a,function(a,g){return c.has(b,g)&&f.equals(a,b[g],d)?(e++,!0):!1}),g&&c.size(b)===e},_cmp:function(a,b){if(void 0===a)return void 0===b?0:-1;if(void 0===b)return 1;var c=0,d=this.detectType(a),f=this.detectType(b),g=this.typeOrder(d),h=this.typeOrder(f);if(g!==h)return h>g?-1:1;if(d!==f)throw new Error("Missing type coercion logic in _cmp");if(7===d&&(d=f=2,a=a.toHexString(),b=b.toHexString()),d===e.DATA.TYPE.DATE&&(d=f=1,a=a.getTime(),b=b.getTime()),d===e.DATA.TYPE.FLOAT)return a-b;if(f===e.DATA.TYPE.STRING)return b>a?-1:a===b?0:1;if(d===e.DATA.TYPE.OBJECT){var i=function(a){var b=[];for(var c in a)b.push(c),b.push(a[c]);return b};return this._cmp(i(a),i(b))}if(d===e.DATA.TYPE.ARRAY)for(c=0;;c++){if(c===a.length)return c===b.length?0:-1;if(c===b.length)return 1;var j=this._cmp(a[c],b[c]);if(0!==j)return j}if(d===e.DATA.TYPE.BINARY){if(a.length!==b.length)return a.length-b.length;for(c=0;c<a.length;c++){if(a[c]<b[c])return-1;if(a[c]>b[c])return 1}return 0}if(d===e.DATA.TYPE.BOOLEAN)return a?b?0:1:b?-1:0;if(d===e.DATA.TYPE.NULL)return 0;throw new Error("Unknown type to sort")}}),e.Entity=function(a){var b=this.fields;this.fields={},this._mergeFields(b),a=a||{},a.fields&&this._mergeFields(a.fields),this.typeMapping=a.typeMapping||this.typeMapping;var c=a.collection,d=a.model||(c?c.prototype.model:null);this.idAttribute=a.idAttribute||this.idAttribute||(d?d.prototype.idAttribute:""),this._updateFields(this.typeMapping),this.initialize.apply(this,arguments)},e.Entity.from=function(a,b){if(e.Entity.prototype.isPrototypeOf(a))b&&b.typeMapping&&a._updateFields(b.typeMapping);else if(c.isFunction(a)&&e.Entity.prototype.isPrototypeOf(a.prototype)){var d=a;a=new d(b)}else{"string"==typeof a&&(a={name:a});var f=e.Entity.extend(a);a=new f(b)}return a},e.Entity.extend=e.extend,e.Entity.create=e.create,e.Entity.design=e.design,c.extend(e.Entity.prototype,e.Object,{_type:"Bikini.Entity",name:"",idAttribute:"",fields:{},initialize:function(){},getFields:function(){return this.fields},getField:function(a){return this.fields[a]},getFieldName:function(a){var b=this.getField(a);return b&&b.name?b.name:a},getKey:function(){return this.idAttribute||e.Model.idAttribute},getKeys:function(){return this.splitKey(this.getKey())},splitKey:function(a){var b=[];return c.isString(a)&&c.each(a.split(","),function(a){var c=a.trim();c&&b.push(c)}),b},_mergeFields:function(a){c.isObject(this.fields)||(this.fields={});var b=this;c.isObject(a)&&c.each(a,function(a,c){b.fields[c]?b.fields[c].merge(a):b.fields[c]=new e.Field(a)})},_updateFields:function(a){var b=this;c.each(this.fields,function(c,d){c.persistent===NO?delete b.fields[d]:(c.name||(c.name=d),a&&a[c.type]&&(c.type=a[c.type]))})},toAttributes:function(a,b,d){if(d=d||this.fields,a&&!c.isEmpty(d)){var e,f={};return c.each(d,function(b,d){e=c.isFunction(a.get)?a.get(b.name):a[b.name],f[d]=e}),f}return a},fromAttributes:function(a,b){if(b=b||this.fields,a&&!c.isEmpty(b)){var d={};return c.each(b,function(b,e){var f=c.isFunction(a.get)?a.get(e):a[e];f=b.transform(f),c.isUndefined(f)||(d[b.name]=f)}),d}return a},setId:function(a,b){if(a&&b){var d=this.getKey()||a.idAttribute;d&&(c.isFunction(a.set)?a.set(d,b):a[d]=b)}return a},getId:function(a){if(a){var b=this.getKey()||a.idAttribute;if(b)return c.isFunction(a.get)?a.get(b):a[b]}}}),e.Security=e.Object.design({logon:function(a,b){var c=a?a.credentials:null;if(c)switch(c.type){case"basic":return this.logonBasicAuth(a,b)}return this.handleCallback(b)},logonBasicAuth:function(a,b){var c=a.credentials;return a.beforeSend=function(a){e.Security.setBasicAuth(a,c)},this.handleCallback(b)},setBasicAuth:function(a,b){if(b&&b.username&&a&&e.Base64){var c=e.Base64.encode(encodeURIComponent(b.username+":"+(b.password||"")));a.setRequestHeader("Authorization","Basic "+c)}}}),e.Model=b.Model.extend({constructor:function(a,c){this.url&&"string"==typeof this.url&&"/"!==this.url.charAt(this.url.length-1)&&(this.url+="/"),this.init(a,c),b.Model.apply(this,arguments)}}),e.Model.create=e.create,e.Model.design=e.design,c.extend(e.Model.prototype,e.Object,{_type:"Bikini.Model",isModel:YES,entity:null,defaults:{},changedSinceSync:{},logon:e.Security.logon,init:function(a,b){b=b||{},this.collection=b.collection||this.collection,this.idAttribute=b.idAttribute||this.idAttribute,this.store=this.store||(this.collection?this.collection.store:null)||b.store,this.store&&c.isFunction(this.store.initModel)&&this.store.initModel(this,b),this.entity=this.entity||(this.collection?this.collection.entity:null)||b.entity,this.entity&&(this.entity=e.Entity.from(this.entity,{model:this.constructor,typeMapping:b.typeMapping}),this.idAttribute=this.entity.idAttribute||this.idAttribute),this.credentials=this.credentials||(this.collection?this.collection.credentials:null)||b.credentials,this.on("change",this.onChange,this),this.on("sync",this.onSync,this)},sync:function(a,d,e){e=e||{},e.credentials=e.credentials||this.credentials;var f=(e.store?e.store:null)||this.store,g=this,h=arguments;return this.logon(e,function(a){return f&&c.isFunction(f.sync)?f.sync.apply(g,h):b.sync.apply(g,h)})},onChange:function(a,b){var d=a.changedAttributes();if(c.isObject(d))for(var e in d)this.changedSinceSync[e]=d[e]},onSync:function(a,b){this.changedSinceSync={}},getUrlRoot:function(){if(this.urlRoot)return c.isFunction(this.urlRoot)?this.urlRoot():this.urlRoot;if(this.collection)return this.collection.getUrlRoot();if(this.url){var a=c.isFunction(this.url)?this.url():this.url;return a&&this.id&&a.indexOf(this.id)>0?a.substr(0,a.indexOf(this.id)):a}},toJSON:function(a){a=a||{};var b=a.entity||this.entity;return e.isEntity(b)?b.fromAttributes(a.attrs||this.attributes):a.attrs||c.clone(this.attributes)},parse:function(a,b){b=b||{};var c=b.entity||this.entity;return e.isEntity(c)?c.toAttributes(a):a}}),e.Collection=b.Collection.extend({constructor:function(a){this.url&&"/"!==this.url.charAt(this.url.length-1)&&(this.url+="/"),this.init(a),b.Collection.apply(this,arguments)}}),e.Collection.create=e.create,e.Collection.design=e.design,c.extend(e.Collection.prototype,e.Object,{_type:"Bikini.Collection",isCollection:YES,model:e.Model,entity:null,options:null,logon:e.Security.logon,init:function(a){a=a||{},this.store=a.store||this.store||(this.model?this.model.prototype.store:null),this.entity=a.entity||this.entity||(this.model?this.model.prototype.entity:null),this.options=a.options||this.options;var b=this.entity||this.entityFromUrl(this.url);b&&(this.entity=e.Entity.from(b,{model:this.model,typeMapping:a.typeMapping})),this._updateUrl(),this.store&&c.isFunction(this.store.initCollection)&&this.store.initCollection(this,a)},entityFromUrl:function(a){if(a){var b=document.createElement("a");b.href=a||this.url,""===b.host&&(b.href=b.href);var c=b.pathname.match(/([^\/]+)\/?$/);if(c&&c.length>1)return c[-1]}},sort:function(a){c.isObject(a&&a.sort)&&(this.comparator=e.DataSelector.compileSort(a.sort)),b.Collection.prototype.sort.apply(this,arguments)},select:function(a){var b=a&&a.query?e.DataSelector.create(a.query):null,c=e.Collection.create(null,{model:this.model});return a&&a.sort&&(c.comparator=e.DataSelector.compileSort(a.sort)),this.each(function(a){(!b||b.matches(a.attributes))&&c.add(a)}),c},destroy:function(a){a=a||{};var b=a.success;if(this.length>0){a.success=function(){0===this.length&&b&&b()};for(var c;c=this.first();)this.sync("delete",c,a),this.remove(c)}else b&&b()},destroyLocal:function(){var a=this.endpoint.localStore;return this.entity.name&&a.drop(this.entity.name),localStorage.setItem("__"+this.channel+"last_msg_time",""),this.store.endpoints={},this.reset()},sync:function(a,d,e){e=e||{},e.credentials=e.credentials||this.credentials;var f=(e.store?e.store:null)||this.store,g=this,h=arguments;return this.logon(e,function(a){return f&&c.isFunction(f.sync)?f.sync.apply(g,h):b.sync.apply(g,h)})},save:function(){this.each(function(a){a.save()})},getUrlParams:function(a){a=a||this.getUrl();var b=a.match(/\?([^#]*)/),d={};return b&&b.length>1&&c.each(b[1].split("&"),function(a){var b=a.split("=");d[b[0]]=b[1]}),d},getUrl:function(a){return(c.isFunction(this.url)?this.url():this.url)||""},getUrlRoot:function(){var a=this.getUrl();return a?a.indexOf("?")>=0?a.substr(0,a.indexOf("?")):a:""},applyFilter:function(a){this.trigger("filter",this.filter(a))},_updateUrl:function(){var a=this.getUrlParams();if(this.options&&(this.url=this.getUrlRoot(),this.options.query&&(a.query=encodeURIComponent(JSON.stringify(this.options.query))),this.options.fields&&(a.fields=encodeURIComponent(JSON.stringify(this.options.fields))),this.options.sort&&(a.sort=encodeURIComponent(JSON.stringify(this.options.sort))),!c.isEmpty(a))){this.url+="?";var b=[];for(var d in a)b.push(d+(a[d]?"="+a[d]:""));this.url+=b.join("&")}}}),e.DataSelector=e.Object.design({_type:"Bikini.DataSelector",_selector:null,create:function(a){var b=this.design({_selector:null});return b.init(a),b},init:function(a){this._selector=this.compileSelector(a)},matches:function(a){return c.isFunction(this._selector)?this._selector(a):!1},hasOperators:function(a){var b;for(var c in a){var d="$"===c.substr(0,1);if(void 0===b)b=d;else if(b!==d)throw new Error("Inconsistent selector: "+a)}return!!b},compileSelector:function(a){if(c.isFunction(a))return function(b){return a.call(b)};if(this._selectorIsId(a))return function(b){var d=c.isFunction(b.getId)?b.getId():b._id||b.id;return e.Field.prototype.equals(d,a)};if(!a||"_id"in a&&!a._id)return function(a){return!1};if(c.isBoolean(a)||c.isArray(a)||e.Field.prototype.isBinary(a))throw new Error("Invalid selector: "+a);return this.compileDocSelector(a)},compileDocSelector:function(a){var b=e.DataSelector,d=[];return c.each(a,function(a,e){if("$"===e.substr(0,1)){if(!c.has(b.LOGICAL_OPERATORS,e))throw new Error("Unrecognized logical operator: "+e);d.push(b.LOGICAL_OPERATORS[e](a))}else{var f=b._makeLookupFunction(e),g=b.compileValueSelector(a);d.push(function(a){var b=f(a);return c.any(b,g)})}}),function(a){var b=c.isFunction(a.getData)?a.getData():a;return c.all(d,function(a){return a(b)})}},compileValueSelector:function(a){var b=e.DataSelector;if(null===a)return function(a){return b._anyIfArray(a,function(a){return null===a})};if(!c.isObject(a))return function(c){return b._anyIfArray(c,function(b){return b===a})};if(c.isRegExp(a))return function(d){return c.isUndefined(d)?!1:b._anyIfArray(d,function(b){return a.test(b)})};if(c.isArray(a))return function(d){return c.isArray(d)?b._anyIfArrayPlus(d,function(c){return b._equal(a,c)}):!1};if(this.hasOperators(a)){var d=[];return c.each(a,function(e,f){if(!c.has(b.VALUE_OPERATORS,f))throw new Error("Unrecognized operator: "+f);d.push(b.VALUE_OPERATORS[f](e,a.$options))}),function(a){return c.all(d,function(b){return b(a)})}}return function(c){return b._anyIfArray(c,function(c){return b._equal(a,c)})}},_makeLookupFunction:function(a){var b,d,e,f=a.indexOf(".");if(-1===f)b=a;else{b=a.substr(0,f);var g=a.substr(f+1);d=this._makeLookupFunction(g),e=/^\d+(\.|$)/.test(g)}return function(a){if(null===a)return[void 0];var f=a[b];return d?c.isArray(f)&&0===f.length?[void 0]:((!c.isArray(f)||e)&&(f=[f]),Array.prototype.concat.apply([],c.map(f,d))):[f]}},_anyIfArray:function(a,b){return c.isArray(a)?c.any(a,b):b(a)},_anyIfArrayPlus:function(a,b){return b(a)?!0:c.isArray(a)&&c.any(a,b)},_selectorIsId:function(a){return c.isString(a)||c.isNumber(a)},_equal:function(a,b){return e.Field.prototype._equals(a,b,!0)},_cmp:function(a,b){return e.Field.prototype._cmp(a,b)},LOGICAL_OPERATORS:{$and:function(a){if(!c.isArray(a)||c.isEmpty(a))throw new Error("$and/$or/$nor must be nonempty array");var b=c.map(a,e.DataSelector.compileDocSelector);return function(a){return c.all(b,function(b){return b(a)})}},$or:function(a){if(!c.isArray(a)||c.isEmpty(a))throw new Error("$and/$or/$nor must be nonempty array");var b=c.map(a,e.DataSelector.compileDocSelector);return function(a){return c.any(b,function(b){return b(a)})}},$nor:function(a){if(!c.isArray(a)||c.isEmpty(a))throw new Error("$and/$or/$nor must be nonempty array");var b=c.map(a,e.DataSelector.compileDocSelector);return function(a){return c.all(b,function(b){return!b(a)})}},$where:function(a){if(!c.isFunction(a)){var b=a;a=function(){return b}}return function(b){return a.call(b)}}},VALUE_OPERATORS:{$in:function(a){if(!c.isArray(a))throw new Error("Argument to $in must be array");return function(b){return e.DataSelector._anyIfArrayPlus(b,function(b){return c.any(a,function(a){return e.DataSelector._equal(a,b)})})}},$all:function(a){if(!c.isArray(a))throw new Error("Argument to $all must be array");return function(b){return c.isArray(b)?c.all(a,function(a){return c.any(b,function(b){return e.DataSelector._equal(a,b)})}):!1}},$lt:function(a){return function(b){return e.DataSelector._anyIfArray(b,function(b){return e.DataSelector._cmp(b,a)<0})}},$lte:function(a){return function(b){return e.DataSelector._anyIfArray(b,function(b){return e.DataSelector._cmp(b,a)<=0})}},$gt:function(a){return function(b){return e.DataSelector._anyIfArray(b,function(b){return e.DataSelector._cmp(b,a)>0})}},$gte:function(a){return function(b){return e.DataSelector._anyIfArray(b,function(b){return e.DataSelector._cmp(b,a)>=0})}},$ne:function(a){return function(b){return!e.DataSelector._anyIfArrayPlus(b,function(b){return e.DataSelector._equal(b,a)})}},$nin:function(a){if(!c.isArray(a))throw new Error("Argument to $nin must be array");var b=this.VALUE_OPERATORS.$in(a);return function(a){return void 0===a?!0:!b(a)}},$exists:function(a){return function(b){return a===(void 0!==b)}},$mod:function(a){var b=a[0],c=a[1];return function(a){return e.DataSelector._anyIfArray(a,function(a){return a%b===c})}},$size:function(a){return function(b){return c.isArray(b)&&a===b.length}},$type:function(a){return function(b){return c.isUndefined(b)?!1:e.DataSelector._anyIfArray(b,function(b){return e.Field.prototype.detectType(b)===a})}},$regex:function(a,b){if(c.isUndefined(b)){if(/[^gim]/.test(b))throw new Error("Only the i, m, and g regexp options are supported");var d=c.isRegExp(a)?a.source:a;a=new RegExp(d,b)}else c.isRegExp(a)||(a=new RegExp(a));return function(b){return c.isUndefined(b)?!1:e.DataSelector._anyIfArray(b,function(b){return a.test(b)})}},$options:function(a){return function(a){return!0}},$elemMatch:function(a){var b=e.DataSelector.compileDocSelector(a);return function(a){return c.isArray(a)?c.any(a,function(a){return b(a)}):!1}},$not:function(a){var b=e.DataSelector.compileDocSelector(a);return function(a){return!b(a)}}},compileSort:function(a){var b=[];if(c.isArray(a))for(var d=0;d<a.length;d++)b.push("string"==typeof a[d]?{lookup:this._makeLookupFunction(a[d]),ascending:!0}:{lookup:this._makeLookupFunction(a[d][0]),ascending:"desc"!==a[d][1]});else{if("object"!=typeof a)throw new Error("Bad sort specification: ",JSON.stringify(a));for(var f in a)b.push({lookup:this._makeLookupFunction(f),ascending:a[f]>=0})}if(0===b.length)return function(){return 0};var g=function(a,b){var d,f=!0;return c.each(a,function(a){c.isArray(a)||(a=[a]),c.isArray(a)&&0===a.length&&(a=[void 0]),c.each(a,function(a){if(f)d=a,f=!1;else{var c=e.DataSelector._cmp(d,a);(b&&c>0||!b&&0>c)&&(d=a)}})}),d};return function(a,c){a=a.attributes?a.attributes:a,c=c.attributes?c.attributes:c;for(var d=0;d<b.length;++d){var f=b[d],h=g(f.lookup(a),f.ascending),i=g(f.lookup(c),f.ascending),j=e.DataSelector._cmp(h,i);if(0!==j)return f.ascending?j:-j}return 0}}}),e.SqlSelector=e.DataSelector.design({_type:"Bikini.SqlSelector",_selector:null,_query:null,_entity:null,create:function(a,b){var c=this.extend({_entity:b,_selector:null,_query:null});return c.init(a),c},init:function(a){this._selector=this.compileSelector(a),this._query=this.buildSqlQuery(a)},buildStatement:function(a){return this._query},buildSqlQuery:function(a,b){if(a instanceof Function)return"";if(c.isString(a))return a;if(!a||"_id"in a&&!a._id)return"1=2";if(c.isBoolean(a)||c.isArray(a)||e.DataField.isBinary(a))throw new Error("Invalid selector: "+a);return this.buildSqlWhere(a)()},buildSqlWhere:function(a){var b=this,d=[];return c.each(a,function(a,e){if("$"===e.substr(0,1))d.push(b.buildLogicalOperator(e,a));else{var f=b.buildLookup(e),g=b.buildValueSelector(a);c.isFunction(g)&&d.push(function(){return g(f)})}}),function(){var a="";return c.each(d,function(d){c.isFunction(d)&&(a+=d.call(b))}),a}},buildValueSelector:function(a){var b=this;if(null===a)return function(a){return a+" IS NULL"};if(!c.isObject(a))return function(c){return c+" = "+b.buildValue(a)};if(c.isRegExp(a)){var d=a.toString(),e=d.match(/\/[\^]?([^^.*$'+()]*)[\$]?\//);if(e&&e.length>1){var f=d.indexOf("/^")<0?"%":"",g=d.indexOf("$/")<0?"%":"";return function(a){return a+' LIKE "'+f+e[1]+g+'"'}}return null}if(c.isArray(a))return null;if(this.hasOperators(a)){var h=[];return c.each(a,function(a,d){if(!c.has(b.VALUE_OPERATORS,d))throw new Error("Unrecognized operator: "+d);h.push(b.VALUE_OPERATORS[d](a,b))}),function(a){return b.LOGICAL_OPERATORS.$and(h,a)}}return function(c){return c+" = "+b.buildValue(a)}},buildLookup:function(a){var b=this._entity?this._entity.getField(a):null;return a=b&&b.name?b.name:a,'"'+a+'"'},buildValue:function(a){return c.isString(a)?'"'+a.replace(/"/g,'""')+'"':a},buildLogicalOperator:function(a,b){if(c.has(this.LOGICAL_OPERATORS,a)){if(!c.isArray(b)||c.isEmpty(b))throw new Error("$and/$or/$nor must be nonempty array");var d=c.map(b,this.buildSqlWhere,this),e=this;return function(b){return e.LOGICAL_OPERATORS[a](d,b)}}throw new Error("Unrecognized logical operator: "+a)},LOGICAL_OPERATORS:{$and:function(a,b){var d="",e=0;return c.each(a,function(a){var c=null!==a?a(b):"";c&&(e++,d+=d?" AND "+c:c)}),e>1?"( "+d+" )":d},$or:function(a,b){var d="",e=!1;return c.each(a,function(a){var c=null!==a?a(b):"";e|=!c,d+=d&&c?" OR "+c:c}),e?"":"( "+d+" )"},$nor:function(a,b){var d="",e=!1;return c.each(a,function(a){var c=null!==a?a(b):"";e|=!c,d+=d&&c?" OR "+c:c}),e?"":"NOT ( "+d+" )"}},VALUE_OPERATORS:{$in:function(a){return null},$all:function(a){return null},$lt:function(a,b){return function(c){return c+" < "+b.buildValue(a)}},$lte:function(a,b){return function(c){return c+" <= "+b.buildValue(a)}},$gt:function(a,b){return function(c){return c+" > "+b.buildValue(a)}},$gte:function(a,b){return function(c){return c+"">""+b.buildValue(a)}},$ne:function(a,b){return function(c){return c+" <> "+b.buildValue(a)}},$nin:function(a){return null},$exists:function(a,b){return function(a){return a+" IS NOT NULL"}},$mod:function(a){return null},$size:function(a){return null},$type:function(a){return null},$regex:function(a,b){return null},$options:function(a){return null},$elemMatch:function(a){return null},$not:function(a,b){var c=b.buildSqlWhere(a);return function(a){return"NOT ("+c(a)+")"}}}}),e.Store=function(){this.initialize.apply(this,arguments)},e.Store.extend=e.extend,e.Store.create=e.create,e.Store.design=e.design,c.extend(e.Store.prototype,b.Events,e.Object,{_type:"Bikini.Store",entities:null,options:null,name:"",typeMapping:function(){var a={};return a[e.DATA.TYPE.OBJECTID]=e.DATA.TYPE.STRING,a[e.DATA.TYPE.DATE]=e.DATA.TYPE.STRING,a[e.DATA.TYPE.BINARY]=e.DATA.TYPE.TEXT,a}(),initialize:function(a){a=a||{},this.options=this.options||{},this.options.name=this.name,this.options.typeMapping=this.typeMapping,this.options.entities=this.entities,c.extend(this.options,a||{}),this._setEntities(a.entities||{})},_setEntities:function(a){this.entities={};for(var b in a){var c=e.Entity.from(a[b],{store:this,typeMapping:this.options.typeMapping});c.name=c.name||b;var d=c.collection||e.Collection.extend({model:e.Model.extend({})}),f=d.prototype.model;
d.prototype.entity=f.prototype.entity=b,d.prototype.store=f.prototype.store=this,c.idAttribute=c.idAttribute||f.prototype.idAttribute,this.entities[b]=c}},getEntity:function(a){if(a){var b=a.entity||a,d=c.isString(b)?b:b.name;if(d)return this.entities[d]||(b&&b.name?b:{name:d})}},getCollection:function(a){return c.isString(a)&&(a=this.entities[a]),a&&a.collection?e.Collection.prototype.isPrototypeOf(a.collection)?a.collection:new a.collection:void 0},createModel:function(a,b){if(c.isString(a)&&(a=this.entities[a]),a&&a.collection){var d=a.collection.model||a.collection.prototype.model;if(d)return new d(b)}},getArray:function(a){return c.isArray(a)?a:e.isCollection(a)?a.models:c.isObject(a)?[a]:[]},getDataArray:function(a){var d=[];if(c.isArray(a)||b.Collection.prototype.isPrototypeOf(a))c.each(a,function(a){var b=this.getAttributes(a);b&&d.push(b)});else{var e=this.getAttributes(a);e&&d.push(this.getAttributes(e))}return d},getAttributes:function(a){return b.Model.prototype.isPrototypeOf(a)?a.attributes:c.isObject(a)?a:null},initModel:function(a){},initCollection:function(a){},initEntity:function(a){},sync:function(a,b,c){},fetch:function(a,b){if(!a||a.models||a.attributes||b||(b=a),a&&(a.models||a.attributes)||!b||!b.entity||(a=this.getCollection(b.entity)),a&&a.fetch){var d=c.extend({},b||{},{store:this});return a.fetch(d)}},create:function(a,b,d){if(!a||a.models||d||(b=a,d=b),a&&a.models||!d||!d.entity||(a=this.getCollection(d.entity)),a&&a.create){var e=c.extend({},d||{},{store:this});a.create(b,e)}},save:function(a,b,d){if(!a||a.attributes||d||(b=a,d=b),a&&a.attributes||!d||!d.entity||(a=this.createModel(d.entity)),a&&a.save){var e=c.extend({},d||{},{store:this});a.save(b,e)}},destroy:function(a,b){if(a&&a.destroy){var d=c.extend({},b||{},{store:this});a.destroy(d)}},_checkEntity:function(a,b){if(!e.isEntity(b)){var c=e.Store.CONST.ERROR_NO_ENTITY;return console.error(c),this.handleCallback(a.error,c),this.handleCallback(a.finish,c),!1}return!0},_checkData:function(a,b){if(!(c.isArray(b)&&0!==b.length||c.isObject(b))){var d=e.Store.CONST.ERROR_NO_DATA;return console.error(d),this.handleCallback(a.error,d),this.handleCallback(a.finish,d),!1}return!0},handleSuccess:function(a){var b=Array.prototype.slice.call(arguments,1);a.success&&this.handleCallback.apply(this,[a.success].concat(b)),a.finish&&this.handleCallback.apply(this,[a.finish].concat(b))},handleError:function(a){var b=Array.prototype.slice.call(arguments,1);a.error&&this.handleCallback.apply(this,[a.error].concat(b)),a.finish&&this.handleCallback.apply(this,[a.finish].concat(b))},CONST:{ERROR_NO_ENTITY:"No valid entity specified. ",ERROR_NO_DATA:"No data passed. ",ERROR_LOAD_DATA:"Error while loading data from store. ",ERROR_SAVE_DATA:"Error while saving data to the store. ",ERROR_LOAD_IDS:"Error while loading ids from store. ",ERROR_SAVE_IDS:"Error while saving ids to the store. "}}),e.LocalStorageStore=e.Store.extend({_type:"Bikini.LocalStorageStore",ids:{},sync:function(a,b,d){d=d||{};var f,g=d.store||this.store,h=g.getEntity(b.entity||d.entity||this.entity);if(g&&h&&b){var i=b.id||("create"===a?(new e.ObjectID).toHexString():null);switch(f=d.attrs||b.toJSON(d),a){case"patch":case"update":case"create":"create"!==a&&(f=c.extend(g._getItem(h,i)||{},f)),b.id!==i&&b.idAttribute&&(f[b.idAttribute]=i),g._setItem(h,i,f);break;case"delete":g._removeItem(h,i);break;case"read":if(i)f=g._getItem(h,i);else{f=[];var j=g._getItemIds(h);for(i in j){var k=g._getItem(h,i);k&&f.push(k)}}break;default:return}}f?g.handleSuccess(d,f):g.handleError(d,e.Store.CONST.ERROR_NO_ENTITY)},drop:function(a){var b=this.getEntity(a);if(b&&b.name){for(var c=this._findAllKeys(b),d=0;d<c.length;d++)localStorage.removeItem(c[d]);localStorage.removeItem("__ids__"+b.name),this.handleSuccess(a)}else this.handleError(a,e.Store.CONST.ERROR_NO_ENTITY)},_getKey:function(a,b){return"_"+a.name+"_"+b},_getItem:function(a,b){var c;if(a&&b)try{c=JSON.parse(localStorage.getItem(this._getKey(a,b))),c?a.setId(c,b):this._delItemId(b)}catch(d){console.error(e.Store.CONST.ERROR_LOAD_DATA+d.message)}return c},_setItem:function(a,b,c){if(a&&b&&c)try{localStorage.setItem(this._getKey(a,b),JSON.stringify(c)),this._addItemId(a,b)}catch(d){console.error(e.Store.CONST.ERROR_SAVE_DATA+d.message)}},_removeItem:function(a,b){a&&b&&(localStorage.removeItem(this._getKey(a,b)),this._delItemId(a,b))},_addItemId:function(a,b){var c=this._getItemIds(a);b in c||(c[b]="",this._saveItemIds(a,c))},_delItemId:function(a,b){var c=this._getItemIds(a);b in c&&(delete c[b],this._saveItemIds(a,c))},_findAllKeys:function(a){var b=[],c=this._getKey(a,"");if(c)for(var d,e=localStorage.length,f=0;e>f;f++)d=localStorage.key(f),d&&d===c&&b.push(d);return b},_getItemIds:function(a){try{var b="__ids__"+a.name;return this.ids[a.name]||(this.ids[a.name]=JSON.parse(localStorage.getItem(b))||{}),this.ids[a.name]}catch(c){console.error(e.Store.CONST.ERROR_LOAD_IDS+c.message)}},_saveItemIds:function(a,b){try{var c="__ids__"+a.name;localStorage.setItem(c,JSON.stringify(b))}catch(d){console.error(e.Store.CONST.ERROR_SAVE_IDS+d.message)}}}),e.WebSqlStore=e.Store.extend({_type:"Bikini.WebSqlStore",_selector:null,options:null,name:"bikini",size:1048576,version:"1.0",db:null,dataField:{name:"data",type:"text",required:!0},idField:{name:"id",type:"string",required:!0},typeMapping:function(){var a={};return a[e.DATA.TYPE.OBJECTID]=e.DATA.TYPE.STRING,a[e.DATA.TYPE.DATE]=e.DATA.TYPE.STRING,a[e.DATA.TYPE.OBJECT]=e.DATA.TYPE.TEXT,a[e.DATA.TYPE.ARRAY]=e.DATA.TYPE.TEXT,a[e.DATA.TYPE.BINARY]=e.DATA.TYPE.TEXT,a}(),sqlTypeMapping:function(){var a={};return a[e.DATA.TYPE.STRING]="varchar(255)",a[e.DATA.TYPE.TEXT]="text",a[e.DATA.TYPE.OBJECT]="text",a[e.DATA.TYPE.ARRAY]="text",a[e.DATA.TYPE.FLOAT]="float",a[e.DATA.TYPE.INTEGER]="integer",a[e.DATA.TYPE.DATE]="varchar(255)",a[e.DATA.TYPE.BOOLEAN]="boolean",a}(),initialize:function(a){e.Store.prototype.initialize.apply(this,arguments),this.options=this.options||{},this.options.name=this.name,this.options.size=this.size,this.options.version=this.version,this.options.typeMapping=this.typeMapping,this.options.sqlTypeMapping=this.sqlTypeMapping,c.extend(this.options,a||{}),this._openDb({error:function(a){console.error(a)}})},sync:function(a,b,c){var f=c.store||this.store,g=e.isCollection(b)?b.models:[b],h=new d.Deferred,i=c.success;switch(c.success&&(c.success=function(a){h.resolve(a),i(a)}),c.entity=c.entity||this.entity,a){case"create":f._checkTable(c,function(){f._insertOrReplace(g,c)});break;case"update":case"patch":f._checkTable(c,function(){f._insertOrReplace(g,c)});break;case"delete":f._delete(g,c);break;case"read":var j=this;f._checkTable(c,function(){f._select(j,c)})}return h.promise()},select:function(a){this._select(null,a)},drop:function(a){this._dropTable(a)},createTable:function(a){this._createTable(a)},execute:function(a){this._executeSql(a)},_openDb:function(a){var b,c;if(!this.db)try{if(window.openDatabase){if(this.db=window.openDatabase(this.options.name,"","",this.options.size),this.entities)for(var d in this.entities)this._createTable({entity:this.entities[d]})}else b="Your browser does not support WebSQL databases."}catch(e){c=e}this.db?this.options.version&&this.db.version!==this.options.version?this._updateDb(a):this.handleSuccess(a,this.db):2===c||"2"===c?this._updateDb(a):(!b&&c&&(b=c),this.handleSuccess(a,b))},_updateDb:function(a){var b,d,e=this;try{var f=window.openDatabase(this.options.name,"","",this.options.size);try{var g=this._sqlUpdateDatabase(f.version,this.options.version);f.changeVersion(f.version,this.options.version,function(a){c.each(g,function(b){console.log("sql statement: "+b),d=b,a.executeSql(b)})},function(b){e.handleError(a,b,d)},function(){e.handleSuccess(a)})}catch(h){b=h.message,console.error("webSql change version failed, DB-Version: "+f.version)}}catch(h){b=h.message}b&&this.handleError(a,b)},_sqlUpdateDatabase:function(a,b){var c=[];if(this.entities)for(var d in this.entities){var e=this.entities[d];c.push(this._sqlDropTable(e.name)),c.push(this._sqlCreateTable(e))}return c},_sqlDropTable:function(a){return"DROP TABLE IF EXISTS '"+a+"'"},_isAutoincrementKey:function(a,b){if(a&&b){var c=this.getField(a,b);return c&&c.type===e.DATA.TYPE.INTEGER}},_sqlPrimaryKey:function(a,b){return b&&1===b.length?this._isAutoincrementKey(a,b[0])?b[0]+" INTEGER PRIMARY KEY ASC AUTOINCREMENT UNIQUE":b[0]+" PRIMARY KEY ASC UNIQUE":""},_sqlConstraint:function(a,b){return b&&b.length>1?"PRIMARY KEY ("+b.join(",")+") ON CONFLICT REPLACE":""},_sqlCreateTable:function(a){var b=this,d=a.getKeys(),e=1===d.length?this._sqlPrimaryKey(a,d):"",f=d.length>1?this._sqlConstraint(a,d):a.constraint||"",g="",h=this.getFields(a);c.each(h,function(a){if(!e||a.name!==d[0]){var c=b._dbAttribute(a);c&&(g+=(g?", ":"")+c)}}),g||(g=this._dbAttribute(this.dataField));var i="CREATE TABLE IF NOT EXISTS '"+a.name+"' (";return i+=e?e+", ":"",i+=g,i+=f?", "+f:"",i+=");"},_sqlDelete:function(a,b){var c="DELETE FROM '"+b.name+"'",d=this._sqlWhere(a,b)||this._sqlWhereFromData(a,b);return d&&(c+=" WHERE "+d),c+=a.and?" AND "+a.and:""},_sqlWhere:function(a,b){this._selector=null;var d="";return c.isString(a.where)?d=a.where:c.isObject(a.where)&&(this._selector=e.SqlSelector.create(a.where,b),d=this._selector.buildStatement()),d},_sqlWhereFromData:function(a,b){var d=this,e=[];if(a&&a.models&&b&&b.idAttribute){var f,g=b.idAttribute,h=this.getField(b,g);if(c.each(a.models,function(a){f=a.id,c.isUndefined(f)||e.push(d._sqlValue(f,h))}),e.length>0)return g+" IN ("+e.join(",")+")"}return""},_sqlSelect:function(a,b){var c="SELECT ";a.fields?a.fields.length>1?c+=a.fields.join(", "):1===a.fields.length&&(c+=a.fields[0]):c+="*",c+=" FROM '"+b.name+"'",a.join&&(c+=" JOIN "+a.join),a.leftJoin&&(c+=" LEFT JOIN "+a.leftJoin);var d=this._sqlWhere(a,b)||this._sqlWhereFromData(a,b);return d&&(c+=" WHERE "+d),a.order&&(c+=" ORDER BY "+a.order),a.limit&&(c+=" LIMIT "+a.limit),a.offset&&(c+=" OFFSET "+a.offset),c},_sqlValue:function(a,b){var c=b&&b.type?b.type:e.Field.prototype.detectType(a);return c===e.DATA.TYPE.INTEGER||c===e.DATA.TYPE.FLOAT?a:c===e.DATA.TYPE.BOOLEAN?a?"1":"0":c===e.DATA.TYPE.NULL?"NULL":(a=e.Field.prototype.transform(a,e.DATA.TYPE.STRING),a=a.replace(/"/g,'""'),'"'+a+'"')},_dbAttribute:function(a){if(a&&a.name){var b=this.options.sqlTypeMapping[a.type],c=a.required?" NOT NULL":"";if(b)return a.name+" "+b.toUpperCase()+c}},_dropTable:function(a){var b=this.getEntity(a);if(b.db=null,this._checkDb(a)&&b){var c=this._sqlDropTable(b.name);this._executeTransaction(a,[c])}},_createTable:function(a){var b=this.getEntity(a);if(b.db=this.db,this._checkDb(a)&&this._checkEntity(a,b)){var c=this._sqlCreateTable(b);this._executeTransaction(a,[c])}},_checkTable:function(a,b){var c=this.getEntity(a);c&&!c.db?this._createTable({success:function(){b()},error:function(b){this.handleError(a,b)},entity:c}):b()},_insertOrReplace:function(a,b){var d=this.getEntity(b);if(this._checkDb(b)&&this._checkEntity(b,d)&&this._checkData(b,a)){for(var f=this._isAutoincrementKey(d,d.getKey()),g=[],h="INSERT OR REPLACE INTO '"+d.name+"' (",i=0;i<a.length;i++){var j=a[i],k="";f||j.id||!j.idAttribute||j.set(j.idAttribute,(new e.ObjectID).toHexString());var l,m,n=b.attrs||j.toJSON();if(c.isEmpty(d.fields)?(l=[j.id,JSON.stringify(n)],m=["id","data"]):(l=c.values(n),m=c.keys(n)),l.length>0){var o=new Array(l.length).join("?,")+"?",p="'"+m.join("','")+"'";k+=h+p+") VALUES ("+o+");",g.push({statement:k,arguments:l})}}this._executeTransaction(b,g)}},_select:function(a,b){var d=this.getEntity(b);if(this._checkDb(b)&&this._checkEntity(b,d)){var f,g=e.isCollection(a);g?a=[]:b.models=[a];var h=this._sqlSelect(b,d),i=this;this.db.readTransaction(function(b){var e=h.statement||h,j=h.arguments;f=e,console.log("sql statement: "+e),j&&console.log(" arguments: "+JSON.stringify(j)),b.executeSql(e,j,function(b,e){for(var f=e.rows.length,h=0;f>h;h++){var j,k=e.rows.item(h);if(c.isEmpty(d.fields)&&i._hasDefaultFields(k))try{j=JSON.parse(k.data)}catch(l){}else j=k;if(j&&(!i._selector||i._selector.matches(j))){if(!g){a=j;break}a.push(j)}}},function(a,b){console.error("webSql error: "+b.message)})},function(a){console.error("WebSql Syntax Error: "+a.message),i.handleError(b,a.message,f)},function(){i.handleSuccess(b,a)})}},_delete:function(a,b){var c=this.getEntity(b);if(this._checkDb(b)&&this._checkEntity(b,c)){b.models=a;var d=this._sqlDelete(b,c);this._executeTransaction(b,[d])}},_executeSql:function(a){a.sql&&this._executeTransaction(a,[a.sql])},_executeTransaction:function(a,b){var d,e;if(this._checkDb(a)){var f=this;try{this.db.transaction(function(a){c.each(b,function(b){var c=b.statement||b,d=b.arguments;e=c,console.log("sql statement: "+c),d&&console.log(" arguments: "+JSON.stringify(d)),a.executeSql(c,d)})},function(b){console.error(b.message),f.handleError(a,b.message,e)},function(){f.handleSuccess(a)})}catch(g){console.error(g.message)}}d&&this.handleCallback(a.error,d,e)},_hasDefaultFields:function(a){return c.every(c.keys(a),function(a){return a===this.idField.name||a===this.dataField.name},this)},_checkDb:function(a){if(!this.db){var b="db handler not initialized.";return console.error(b),this.handleError(a,b),!1}return!0},getFields:function(a){if(c.isEmpty(a.fields)){var b={};b.data=this.dataField;var d=a.idAttribute||"id";return b[d]=this.idField,b}return a.fields},getField:function(a,b){return this.getFields(a)[b]}}),e.BikiniStore=e.Store.extend({_type:"Bikini.BikiniStore",_selector:null,endpoints:{},options:null,localStore:e.WebSqlStore,useLocalStore:!0,useSocketNotify:!0,useOfflineChanges:!0,isConnected:!1,typeMapping:{binary:"text",date:"string"},initialize:function(a){console.log("Bikini.BikiniStore.initialize"),e.Store.prototype.initialize.apply(this,arguments),this.options=this.options||{},this.options.useLocalStore=this.useLocalStore,this.options.useSocketNotify=this.useSocketNotify,this.options.useOfflineChanges=this.useOfflineChanges,this.options.query=a.query||!1,this.options.socketPath=this.socketPath,this.options.localStore=this.localStore,this.options.typeMapping=this.typeMapping,this.options.useSocketNotify&&"object"!=typeof io&&(console.log("Socket.IO not present !!"),this.options.useSocketNotify=!1),c.extend(this.options,a||{})},initCollection:function(a){console.log("Bikini.BikiniStore.initCollection");var b=a.getUrlRoot();"/"!==b.charAt(b.length-1)&&(b+="/");var c=this.getEntity(a.entity);if(b&&c){var d=c.name,e=this._locationBasedHashCode(b),f=c.credentials||a.credentials,g=f&&f.username?f.username:"",h=d+g+e;a.channel=h;var i=this,j=this.endpoints[e];if(!j){var k=this.getLocation(b);j={},j.baseUrl=b,j.readUrl=a.getUrl(),j.host=k.protocol+"//"+k.host,j.path=k.pathname,j.entity=c,j.channel=h,j.credentials=f,j.socketPath=this.options.socketPath,j.localStore=this.createLocalStore(j),j.messages=this.createMsgCollection(j),j.socket=this.createSocket(j),j.info=this.fetchServerInfo(j),i.endpoints[e]=j}a.endpoint=j,a.listenTo(this,j.channel,this.onMessage,a)}},getEndpoint:function(a){if(console.log("Bikini.BikiniStore.getEndpoint"),a){var b=this._locationBasedHashCode(a);return this.endpoints[b]}},createLocalStore:function(a,b){if(console.log("Bikini.BikiniStore.createLocalStore"),this.options.useLocalStore&&a){var c={};return c[a.entity.name]={name:a.channel,idAttribute:b},this.options.localStore.create({entities:c})}},createMsgCollection:function(a){if(this.options.useOfflineChanges&&a){var b="msg-"+a.channel,c={};c[b]={name:b,idAttribute:"id"};var d=e.Collection.design({url:a.url,entity:b,store:this.options.localStore.create({entities:c})}),f=this;return d.fetch({success:function(){f.sendMessages(a)}}),d}},createSocket:function(a,b){if(console.log("Bikini.BikiniStore.createSocket"),this.options.useSocketNotify&&a&&a.socketPath){var c=this,d=a.host,e=a.path,f=this.getLocation(d);""===f.port&&("https:"===f.protocol?d+=":443":"http:"===f.protocol&&(d+=":80")),e=a.socketPath;var g=e&&0===e.indexOf("/")?e.substr(1):e,h={resource:g};return this.options.socketQuery&&(h.query=this.options.socketQuery),a.socket=io.connect(d,h),a.socket.on("connect",function(){c._bindChannel(a,b),c.onConnect(a)}),a.socket.on("disconnect",function(){console.log("socket.io: disconnect"),c.onDisconnect(a)}),a.socket}},_bindChannel:function(a,b){console.log("Bikini.BikiniStore._bindChannel");var c=this;if(a&&a.socket){var d=a.channel,e=a.socket,f=this.getLastMessageTime(d);b=b||a.entity.name,e.on(d,function(a){a&&(c.trigger(d,a),c.options.useLocalStore&&c.setLastMessageTime(d,a.time))}),e.emit("bind",{entity:b,channel:d,time:f})}},getLastMessageTime:function(a){return void 0!==this.lastMesgTime?this.lastMesgTime:(console.log("Bikini.BikiniStore.getLastMessageTime"),this.lastMesgTime=localStorage.getItem("__"+a+"lastMesgTime")||0,this.lastMesgTime)},setLastMessageTime:function(a,b){b&&b>this.getLastMessageTime()&&(console.log("Bikini.BikiniStore.setLastMessageTime"),localStorage.setItem("__"+a+"lastMesgTime",b),this.lastMesgTime=b)},_hashCode:function(a){console.log("Bikini.BikiniStore._hashCode");var b,c=0;if(0===a.length)return c;for(var d=0,e=a.length;e>d;d++)b=a.charCodeAt(d),c=(c<<5)-c+b,c|=0;return c},_locationBasedHashCode:function(a){return console.log("Bikini.BikiniStore._locationBasedHashCode"),this._hashCode(this._getLocationUrl(a))},_getLocationUrl:function(a){return console.log("Bikini.BikiniStore._getLocationUrl"),this.getLocation(a).toString()},_getLocation:function(a){console.log("Bikini.BikiniStore._getLocation");var b=document.createElement("a");return b.href=a||this.url,""===b.host&&(b.href=b.href),b},onConnect:function(a){console.log("Bikini.BikiniStore.onConnect"),this.isConnected=!0,this.fetchChanges(a),this.sendMessages(a)},onDisconnect:function(a){console.log("Bikini.BikiniStore.onDisconnect"),this.isConnected=!1,a.socket&&a.socket.socket&&a.socket.socket.onDisconnect()},onMessage:function(a){if(console.log("Bikini.BikiniStore.onMessage"),a){var b=this.endpoint?this.endpoint.localStore:null,c=null,d=null,e=null,f={store:b,entity:this.entity,merge:!0,fromMessage:!0,parse:!0};switch(a.id&&a.method?(c=a.data||{},d=a.method,e=a.id):a.attributes&&(c=a.attributes.data,d=a.attributes.method,e=a.attributes.id),d){case"patch":case"update":case"create":f.patch="patch"===d;var g=e?this.get(e):null;g?g.save(c,f):this.create(c,f);break;case"delete":if(e)if("all"===e){for(;g=this.first();)b&&b.sync.apply(this,["delete",g,{store:b,fromMessage:!0}]),this.remove(g);this.store.setLastMessageTime(this.endpoint.channel,"")}else{var h=this.get(e);h&&h.destroy(f)}}}},sync:function(a,b,c){console.log("Bikini.BikiniStore.sync");var d=c.store||this.store;if(c.fromMessage)return d.handleCallback(c.success);var f=d.getEndpoint(this.getUrlRoot()),g=null;if(d&&f){var h=this.channel;e.isModel(b)&&!b.id&&b.set(b.idAttribute,(new e.ObjectID).toHexString());var i=d.getLastMessageTime(h);return"read"===a&&f.localStore&&i?"read"===a&&(g=d.fetchChanges(f)):g=d.addMessage(a,b,f.localStore?{}:c,f),f.localStore&&(c.store=f.localStore,f.localStore.sync.apply(this,arguments)),g}},addMessage:function(a,b,d,e){var f=this;if(a&&b){var g=b.changedSinceSync,h=null,i=!0;switch(a){case"update":case"create":h=d.attrs||b.toJSON();break;case"patch":if(c.isEmpty(g))return;h=b.toJSON({attrs:g});break;case"delete":break;default:i=!1}var j={_id:b.id,id:b.id,method:a,data:h},k=function(a,c){return f.emitMessage(a,c,d,b)};return i?this.storeMessage(e,j,k):k(e,j)}},emitMessage:function(a,b,d,f){var g=a.channel,h=this,i=e.isModel(f)||"read"!==b.method?a.baseUrl:a.readUrl;return b.id&&"create"!==b.method&&(i+=("/"===i.charAt(i.length-1)?"":"/")+b.id),f.sync.apply(f,[b.method,f,{url:i,error:function(c,e){!c.responseText&&h.options.useOfflineChanges?(h.onDisconnect(a),h.handleCallback(d.success,b.data)):h.removeMessage(a,b,function(a,b){h.handleCallback(d.error,e)})},success:function(e){h.isConnected||h.onConnect(a),h.removeMessage(a,b,function(a,b){if(d.success){var f=e;h.handleCallback(d.success,f)}else if("read"===b.method)for(var i=c.isArray(e)?e:[e],j=0;j<i.length;j++)e=i[j],e&&h.trigger(g,{id:e[a.entity.idAttribute]||e._id,method:"update",data:e});else h.trigger(g,b)})},store:{}}])},fetchChanges:function(a){var b=this,c=a?a.channel:"",d=b.getLastMessageTime(c);if(a&&a.baseUrl&&c&&d){var f=new e.Collection({});return f.fetch({url:a.baseUrl+"changes/"+d,success:function(a,d,e){return f.each(function(a){a.get("time")&&a.get("method")&&(b.options.useLocalStore&&b.setLastMessageTime(c,a.get("time")),b.trigger(c,a))}),e.xhr},credentials:a.credentials})}},fetchServerInfo:function(a){var b=this;if(a&&a.baseUrl){var c=new e.Model,d=b.getLastMessageTime(a.channel),f=a.baseUrl;return"/"!==f.charAt(f.length-1)&&(f+="/"),c.fetch({url:f+"info",success:function(e,f,g){if(!d&&c.get("time")&&b.setLastMessageTime(a.channel,c.get("time")),!a.socketPath&&c.get("socketPath")){a.socketPath=c.get("socketPath");var h=c.get("entity")||a.entity.name;b.options.useSocketNotify&&(a.socket=b.createSocket(a,h))}return g.xhr},credentials:a.credentials})}},sendMessages:function(a){if(a&&a.messages){var b=this;a.messages.each(function(c){var d;try{d=JSON.parse(c.get("msg"))}catch(e){}var f=c.get("channel");if(d&&f){var g=b.createModel({collection:a.messages},d.data);b.emitMessage(a,d,{},g)}else c.destroy()})}},mergeMessages:function(a,b){return a},storeMessage:function(a,b,d){if(a&&a.messages&&b){var e=a.channel,f=a.messages.get(b._id);if(f){var g=JSON.parse(f.get("msg"));f.save({msg:JSON.stringify(c.extend(g,b))})}else a.messages.create({_id:b._id,id:b.id,msg:JSON.stringify(b),channel:e})}return d(a,b)},removeMessage:function(a,b,c){if(a&&a.messages){var d=a.messages.get(b._id);d&&d.destroy()}return c(a,b)},clear:function(a){if(a){var b=this.getEndpoint(a.getUrlRoot());b&&(b.messages&&b.messages.destroy(),a.reset(),this.setLastMessageTime(b.channel,""))}},getLocation:function(a){var b=document.createElement("a");return b.href=a||this.url,""===b.host&&(b.href=b.href),b}})}(this,Backbone,_,$);
//# sourceMappingURL=bikini.map

@@ -66,2 +66,5 @@ /**

core: {
compress: {
drop_console: true
},
src: 'dist/bikini.js',

@@ -79,3 +82,3 @@ dest: 'dist/bikini.min.js',

files: ['src/**/*'],
tasks: ['build-js'],
tasks: ['preprocess:dist'],
options: {

@@ -82,0 +85,0 @@ spawn: false

{
"name": "bikini",
"version": "0.6.3",
"version": "0.7.0",
"description": "",

@@ -5,0 +5,0 @@ "keywords": [

@@ -5,23 +5,23 @@ // @echo BANNER

// @include ./core/bikini.js
// @include ./core/bikini.js
// @include ./utility/objectid.js
// @include ./utility/uuid.js
// @include ./utility/base64.js
// @include ./utility/sha256.js
// @include ./utility/cypher.js
// @include ./utility/date.js
// @include ./utility/objectid.js
// @include ./utility/uuid.js
// @include ./utility/base64.js
// @include ./utility/sha256.js
// @include ./utility/cypher.js
// @include ./utility/date.js
// @include ./data/field.js
// @include ./data/entity.js
// @include ./data/security.js
// @include ./data/model.js
// @include ./data/collection.js
// @include ./data/data_selector.js
// @include ./data/sql_selector.js
// @include ./data/stores/store.js
// @include ./data/stores/local_storage.js
// @include ./data/stores/web_sql.js
// @include ./data/stores/bikini_store.js
// @include ./data/field.js
// @include ./data/entity.js
// @include ./data/security.js
// @include ./data/model.js
// @include ./data/collection.js
// @include ./data/data_selector.js
// @include ./data/sql_selector.js
// @include ./data/stores/store.js
// @include ./data/stores/local_storage.js
// @include ./data/stores/web_sql.js
// @include ./data/stores/bikini_store.js
})(this, Backbone, _, $);

@@ -10,6 +10,6 @@ // Copyright (c) 2013 M-Way Solutions GmbH

var Bikini = null;
if( typeof exports !== 'undefined' ) {
Bikini = exports;
if (typeof exports !== 'undefined') {
Bikini = exports;
} else {
Bikini = global.Bikini = {};
Bikini = global.Bikini = {};
}

@@ -29,12 +29,12 @@

*/
Bikini.f = function() {
Bikini.f = function () {
};
Bikini.create = function( args ) {
return new this(args);
Bikini.create = function (args) {
return new this(args);
};
Bikini.design = function( obj ) {
var O = this.extend(obj || {});
return new O();
Bikini.design = function (obj) {
var O = this.extend(obj || {});
return new O();
};

@@ -44,12 +44,12 @@

Bikini.isCollection = function( collection ) {
return Backbone.Collection.prototype.isPrototypeOf(collection);
Bikini.isCollection = function (collection) {
return Backbone.Collection.prototype.isPrototypeOf(collection);
};
Bikini.isModel = function( model ) {
return Backbone.Model.prototype.isPrototypeOf(model);
Bikini.isModel = function (model) {
return Backbone.Model.prototype.isPrototypeOf(model);
};
Bikini.isEntity = function( entity ) {
return Bikini.Entity.prototype.isPrototypeOf(entity);
Bikini.isEntity = function (entity) {
return Bikini.Entity.prototype.isPrototypeOf(entity);
};

@@ -61,136 +61,136 @@

Bikini.DATA = {
TYPE: {
INTEGER: 'integer',
TYPE: {
INTEGER: 'integer',
STRING: 'string',
STRING: 'string',
TEXT: 'text',
TEXT: 'text',
DATE: 'date',
DATE: 'date',
BOOLEAN: 'boolean',
BOOLEAN: 'boolean',
FLOAT: 'float',
FLOAT: 'float',
OBJECT: 'object',
OBJECT: 'object',
ARRAY: 'array',
ARRAY: 'array',
BINARY: 'binary',
BINARY: 'binary',
OBJECTID: 'objectid',
OBJECTID: 'objectid',
NULL: 'null'
}
NULL: 'null'
}
};
Bikini.Object = {
/**
* The type of this object.
*
* @type String
*/
_type: 'Bikini.Object',
/**
* The type of this object.
*
* @type String
*/
_type: 'Bikini.Object',
/**
* Creates an object based on a passed prototype.
*
* @param {Object} proto The prototype of the new object.
*/
_create: function( proto ) {
var F = function() {
};
F.prototype = proto;
return new F();
},
/**
* Creates an object based on a passed prototype.
*
* @param {Object} proto The prototype of the new object.
*/
_create: function (proto) {
var F = function () {
};
F.prototype = proto;
return new F();
},
/**
* Includes passed properties into a given object.
*
* @param {Object} properties The properties to be included into the given object.
*/
include: function( properties ) {
for( var prop in properties ) {
if( this.hasOwnProperty(prop) ) {
throw Bikini.Exception.RESERVED_WORD.getException();
}
this[prop] = properties[prop];
}
/**
* Includes passed properties into a given object.
*
* @param {Object} properties The properties to be included into the given object.
*/
include: function (properties) {
for (var prop in properties) {
if (this.hasOwnProperty(prop)) {
throw Bikini.Exception.RESERVED_WORD.getException();
}
this[prop] = properties[prop];
}
return this;
},
return this;
},
/**
* Creates a new class and extends it with all functions of the defined super class
* The function takes multiple input arguments. Each argument serves as additional
* super classes - see mixins.
*
* @param {Object} properties The properties to be included into the given object.
*/
design: function( properties ) {
/* create the new object */
// var obj = Bikini.Object._create(this);
var obj = this._create(this);
/**
* Creates a new class and extends it with all functions of the defined super class
* The function takes multiple input arguments. Each argument serves as additional
* super classes - see mixins.
*
* @param {Object} properties The properties to be included into the given object.
*/
design: function (properties) {
/* create the new object */
// var obj = Bikini.Object._create(this);
var obj = this._create(this);
/* assign the properties passed with the arguments array */
obj.include(this._normalize(properties));
/* assign the properties passed with the arguments array */
obj.include(this._normalize(properties));
/* return the new object */
return obj;
},
/* return the new object */
return obj;
},
/**
* Binds a method to its caller, so it is always executed within the right scope.
*
* @param {Object} caller The scope of the method that should be bound.
* @param {Function} method The method to be bound.
* @param {Object} arg One or more arguments. If more, then apply is used instead of call.
*/
bindToCaller: function( caller, method, arg ) {
return function() {
if( typeof method !== 'function' || typeof caller !== 'object' ) {
throw Bikini.Exception.INVALID_INPUT_PARAMETER.getException();
}
if( Array.isArray(arg) ) {
return method.apply(caller, arg);
}
return method.call(caller, arg);
};
},
/**
* Binds a method to its caller, so it is always executed within the right scope.
*
* @param {Object} caller The scope of the method that should be bound.
* @param {Function} method The method to be bound.
* @param {Object} arg One or more arguments. If more, then apply is used instead of call.
*/
bindToCaller: function (caller, method, arg) {
return function () {
if (typeof method !== 'function' || typeof caller !== 'object') {
throw Bikini.Exception.INVALID_INPUT_PARAMETER.getException();
}
if (Array.isArray(arg)) {
return method.apply(caller, arg);
}
return method.call(caller, arg);
};
},
/**
* This method is used internally to normalize the properties object that is used
* for extending a given object.
*
* @param obj
* @returns {Object}
* @private
*/
_normalize: function( obj ) {
obj = obj && typeof obj === 'object' ? obj : {};
/**
* This method is used internally to normalize the properties object that is used
* for extending a given object.
*
* @param obj
* @returns {Object}
* @private
*/
_normalize: function (obj) {
obj = obj && typeof obj === 'object' ? obj : {};
return obj;
},
return obj;
},
/**
* Calls a method defined by a handler
*
* @param {Object} handler A function, or an object including target and action to use with bindToCaller.
*/
handleCallback: function( handler ) {
var args = Array.prototype.slice.call(arguments, 1);
if( handler ) {
var target = typeof handler.target === 'object' ? handler.target : this;
var action = handler;
if( typeof handler.action === 'function' ) {
action = handler.action;
} else if( typeof handler.action === 'string' ) {
action = target[handler.action];
}
if( typeof action === 'function' ) {
return this.bindToCaller(target, action, args)();
}
}
/**
* Calls a method defined by a handler
*
* @param {Object} handler A function, or an object including target and action to use with bindToCaller.
*/
handleCallback: function (handler) {
var args = Array.prototype.slice.call(arguments, 1);
if (handler) {
var target = typeof handler.target === 'object' ? handler.target : this;
var action = handler;
if (typeof handler.action === 'function') {
action = handler.action;
} else if (typeof handler.action === 'string') {
action = target[handler.action];
}
if (typeof action === 'function') {
return this.bindToCaller(target, action, args)();
}
}
}

@@ -197,0 +197,0 @@ };

@@ -20,9 +20,9 @@ // Copyright (c) 2013 M-Way Solutions GmbH

constructor: function (options) {
if(this.url && this.url.charAt(this.url.length - 1) !== '/') {
this.url += '/';
}
this.init(options);
Backbone.Collection.apply(this, arguments);
constructor: function (options) {
if (this.url && this.url.charAt(this.url.length - 1) !== '/') {
this.url += '/';
}
this.init(options);
Backbone.Collection.apply(this, arguments);
}
});

@@ -35,171 +35,180 @@

_type: 'Bikini.Collection',
_type: 'Bikini.Collection',
isCollection: YES,
isCollection: YES,
model: Bikini.Model,
model: Bikini.Model,
entity: null,
entity: null,
options: null,
options: null,
logon: Bikini.Security.logon,
logon: Bikini.Security.logon,
init: function (options) {
options = options || {};
this.store = options.store || this.store || (this.model ? this.model.prototype.store : null);
this.entity = options.entity || this.entity || (this.model ? this.model.prototype.entity : null);
this.options = options.options || this.options;
init: function (options) {
options = options || {};
this.store = options.store || this.store || (this.model ? this.model.prototype.store : null);
this.entity = options.entity || this.entity || (this.model ? this.model.prototype.entity : null);
this.options = options.options || this.options;
var entity = this.entity || this.entityFromUrl(this.url);
if (entity) {
this.entity = Bikini.Entity.from(entity, { model: this.model, typeMapping: options.typeMapping });
}
this._updateUrl();
var entity = this.entity || this.entityFromUrl(this.url);
if (entity) {
this.entity = Bikini.Entity.from(entity, {model: this.model, typeMapping: options.typeMapping});
}
this._updateUrl();
if (this.store && _.isFunction(this.store.initCollection)) {
this.store.initCollection(this, options);
}
},
if (this.store && _.isFunction(this.store.initCollection)) {
this.store.initCollection(this, options);
}
},
entityFromUrl: function (url) {
if (url) {
// extract last path part as entity name
var parts = Bikini.Request.getLocation(this.url).pathname.match(/([^\/]+)\/?$/);
if (parts && parts.length > 1) {
return parts[1];
}
}
},
entityFromUrl: function (url) {
if (url) {
var location = document.createElement('a');
location.href = url || this.url;
// IE doesn't populate all link properties when setting .href with a relative URL,
// however .href will return an absolute URL which then can be used on itself
// to populate these additional fields.
if (location.host === '') {
location.href = location.href;
}
sort: function (options) {
if (_.isObject(options && options.sort)) {
this.comparator = Bikini.DataSelector.compileSort(options.sort);
}
Backbone.Collection.prototype.sort.apply(this, arguments);
},
// extract last path part as entity name
var parts = location.pathname.match(/([^\/]+)\/?$/);
if (parts && parts.length > 1) {
return parts[-1];
}
}
},
select: function (options) {
var selector = options && options.query ? Bikini.DataSelector.create(options.query) : null;
var collection = Bikini.Collection.create(null, { model: this.model });
sort: function (options) {
if (_.isObject(options && options.sort)) {
this.comparator = Bikini.DataSelector.compileSort(options.sort);
}
Backbone.Collection.prototype.sort.apply(this, arguments);
},
if (options && options.sort) {
collection.comparator = Bikini.DataSelector.compileSort(options.sort);
}
select: function (options) {
var selector = options && options.query ? Bikini.DataSelector.create(options.query) : null;
var collection = Bikini.Collection.create(null, {model: this.model});
this.each(function (model) {
if (!selector || selector.matches(model.attributes)) {
collection.add(model);
}
});
return collection;
},
if (options && options.sort) {
collection.comparator = Bikini.DataSelector.compileSort(options.sort);
}
destroy: function (options) {
options = options || {};
var success = options.success;
if (this.length > 0) {
options.success = function () {
if (this.length === 0 && success) {
success();
}
};
var model;
while ((model = this.first())) {
this.sync('delete', model, options);
this.remove(model);
}
} else if (success) {
success();
}
},
this.each(function (model) {
if (!selector || selector.matches(model.attributes)) {
collection.add(model);
}
});
return collection;
},
destroyLocal: function(){
var store = this.endpoint.localStore;
var that = this;
// DROP TABLE
if(this.entity.name) {
store.drop(this.entity.name);
destroy: function (options) {
options = options || {};
var success = options.success;
if (this.length > 0) {
options.success = function () {
if (this.length === 0 && success) {
success();
}
// RESET localStorage-entry
localStorage.setItem('__' + this.channel + 'last_msg_time', '');
this.store.endpoints = {};
return this.reset();
},
};
var model;
while ((model = this.first())) {
this.sync('delete', model, options);
this.remove(model);
}
} else if (success) {
success();
}
},
sync: function (method, model, options) {
options = options || {};
options.credentials = options.credentials || this.credentials;
var store = (options.store ? options.store : null) || this.store;
var that = this;
var args = arguments;
destroyLocal: function () {
var store = this.endpoint.localStore;
var that = this;
// DROP TABLE
if (this.entity.name) {
store.drop(this.entity.name);
}
// RESET localStorage-entry
localStorage.setItem('__' + this.channel + 'last_msg_time', '');
this.store.endpoints = {};
return this.reset();
},
return this.logon(options, function (result) {
if (store && _.isFunction(store.sync)) {
return store.sync.apply(that, args);
} else {
return Backbone.sync.apply(that, args);
}
});
},
sync: function (method, model, options) {
options = options || {};
options.credentials = options.credentials || this.credentials;
var store = (options.store ? options.store : null) || this.store;
var that = this;
var args = arguments;
/**
* save all containing models
*/
save: function() {
this.each(function(model) {
model.save();
});
},
return this.logon(options, function (result) {
if (store && _.isFunction(store.sync)) {
return store.sync.apply(that, args);
} else {
return Backbone.sync.apply(that, args);
}
});
},
getUrlParams: function (url) {
url = url || this.getUrl();
var m = url.match(/\?([^#]*)/);
var params = {};
if (m && m.length > 1) {
_.each(m[1].split('&'), function (p) {
var a = p.split('=');
params[a[0]] = a[1];
});
}
return params;
},
/**
* save all containing models
*/
save: function () {
this.each(function (model) {
model.save();
});
},
getUrl: function (collection) {
return (_.isFunction(this.url) ? this.url() : this.url) || '';
},
getUrlParams: function (url) {
url = url || this.getUrl();
var m = url.match(/\?([^#]*)/);
var params = {};
if (m && m.length > 1) {
_.each(m[1].split('&'), function (p) {
var a = p.split('=');
params[a[0]] = a[1];
});
}
return params;
},
getUrlRoot: function () {
var url = this.getUrl();
return url ? ( url.indexOf('?') >= 0 ? url.substr(0, url.indexOf('?')) : url) : '';
},
getUrl: function (collection) {
return (_.isFunction(this.url) ? this.url() : this.url) || '';
},
applyFilter: function (callback) {
this.trigger('filter', this.filter(callback));
},
getUrlRoot: function () {
var url = this.getUrl();
return url ? ( url.indexOf('?') >= 0 ? url.substr(0, url.indexOf('?')) : url) : '';
},
_updateUrl: function () {
var params = this.getUrlParams();
if (this.options) {
this.url = this.getUrlRoot();
if (this.options.query) {
params.query = encodeURIComponent(JSON.stringify(this.options.query));
}
if (this.options.fields) {
params.fields = encodeURIComponent(JSON.stringify(this.options.fields));
}
if (this.options.sort) {
params.sort = encodeURIComponent(JSON.stringify(this.options.sort));
}
if (!_.isEmpty(params)) {
this.url += '?';
var a = [];
for (var k in params) {
a.push(k + (params[k] ? '=' + params[k] : ''));
}
this.url += a.join('&');
}
applyFilter: function (callback) {
this.trigger('filter', this.filter(callback));
},
_updateUrl: function () {
var params = this.getUrlParams();
if (this.options) {
this.url = this.getUrlRoot();
if (this.options.query) {
params.query = encodeURIComponent(JSON.stringify(this.options.query));
}
if (this.options.fields) {
params.fields = encodeURIComponent(JSON.stringify(this.options.fields));
}
if (this.options.sort) {
params.sort = encodeURIComponent(JSON.stringify(this.options.sort));
}
if (!_.isEmpty(params)) {
this.url += '?';
var a = [];
for (var k in params) {
a.push(k + (params[k] ? '=' + params[k] : ''));
}
this.url += a.join('&');
}
}
}
});

@@ -18,588 +18,590 @@ // Copyright (c) 2013 M-Way Solutions GmbH

_type: 'Bikini.DataSelector',
_type: 'Bikini.DataSelector',
_selector: null,
_selector: null,
create: function (docSelector) {
var selector = this.design({
_selector: null
});
selector.init(docSelector);
return selector;
},
create: function (docSelector) {
var selector = this.design({
_selector: null
});
selector.init(docSelector);
return selector;
},
init: function (docSelector) {
this._selector = this.compileSelector(docSelector);
},
init: function (docSelector) {
this._selector = this.compileSelector(docSelector);
},
matches: function (value) {
if (_.isFunction(this._selector)) {
return this._selector(value);
}
return false;
},
matches: function (value) {
if (_.isFunction(this._selector)) {
return this._selector(value);
}
return false;
},
hasOperators: function (valueSelector) {
var theseAreOperators;
for (var selKey in valueSelector) {
var thisIsOperator = selKey.substr(0, 1) === '$';
if (theseAreOperators === undefined) {
theseAreOperators = thisIsOperator;
} else if (theseAreOperators !== thisIsOperator) {
throw new Error('Inconsistent selector: ' + valueSelector);
}
}
return !!theseAreOperators; // {} has no operators
},
hasOperators: function (valueSelector) {
var theseAreOperators;
for (var selKey in valueSelector) {
var thisIsOperator = selKey.substr(0, 1) === '$';
if (theseAreOperators === undefined) {
theseAreOperators = thisIsOperator;
} else if (theseAreOperators !== thisIsOperator) {
throw new Error('Inconsistent selector: ' + valueSelector);
}
}
return !!theseAreOperators; // {} has no operators
},
// Given a selector, return a function that takes one argument, a
// document, and returns true if the document matches the selector,
// else false.
compileSelector: function (selector) {
// you can pass a literal function instead of a selector
if ( _.isFunction(selector)) {
return function (doc) {
return selector.call(doc);
};
}
// Given a selector, return a function that takes one argument, a
// document, and returns true if the document matches the selector,
// else false.
compileSelector: function (selector) {
// you can pass a literal function instead of a selector
if (_.isFunction(selector)) {
return function (doc) {
return selector.call(doc);
};
}
// shorthand -- scalars match _id
if (this._selectorIsId(selector)) {
return function (record) {
var id = _.isFunction(record.getId) ? record.getId() : (record._id || record.id);
return Bikini.Field.prototype.equals(id, selector);
};
}
// shorthand -- scalars match _id
if (this._selectorIsId(selector)) {
return function (record) {
var id = _.isFunction(record.getId) ? record.getId() : (record._id || record.id);
return Bikini.Field.prototype.equals(id, selector);
};
}
// protect against dangerous selectors. falsey and {_id: falsey} are both
// likely programmer error, and not what you want, particularly for
// destructive operations.
if (!selector || (('_id' in selector) && !selector._id)) {
return function (doc) {
return false;
};
}
// protect against dangerous selectors. falsey and {_id: falsey} are both
// likely programmer error, and not what you want, particularly for
// destructive operations.
if (!selector || (('_id' in selector) && !selector._id)) {
return function (doc) {
return false;
};
}
// Top level can't be an array or true or binary.
if (_.isBoolean(selector) || _.isArray(selector) || Bikini.Field.prototype.isBinary(selector)) {
throw new Error('Invalid selector: ' + selector);
// Top level can't be an array or true or binary.
if (_.isBoolean(selector) || _.isArray(selector) || Bikini.Field.prototype.isBinary(selector)) {
throw new Error('Invalid selector: ' + selector);
}
return this.compileDocSelector(selector);
},
// The main compilation function for a given selector.
compileDocSelector: function (docSelector) {
var that = Bikini.DataSelector;
var perKeySelectors = [];
_.each(docSelector, function (subSelector, key) {
if (key.substr(0, 1) === '$') {
// Outer operators are either logical operators (they recurse back into
// this function), or $where.
if (!_.has(that.LOGICAL_OPERATORS, key)) {
throw new Error('Unrecognized logical operator: ' + key);
}
perKeySelectors.push(that.LOGICAL_OPERATORS[key](subSelector));
} else {
var lookUpByIndex = that._makeLookupFunction(key);
var valueSelectorFunc = that.compileValueSelector(subSelector);
perKeySelectors.push(function (doc) {
var branchValues = lookUpByIndex(doc);
// We apply the selector to each 'branched' value and return true if any
// match. This isn't 100% consistent with MongoDB; eg, see:
// https://jira.mongodb.org/browse/SERVER-8585
return _.any(branchValues, valueSelectorFunc);
});
}
});
return this.compileDocSelector(selector);
},
return function (record) {
var doc = _.isFunction(record.getData) ? record.getData() : record;
return _.all(perKeySelectors, function (f) {
return f(doc);
});
};
},
// The main compilation function for a given selector.
compileDocSelector: function (docSelector) {
var that = Bikini.DataSelector;
var perKeySelectors = [];
_.each(docSelector, function (subSelector, key) {
if (key.substr(0, 1) === '$') {
// Outer operators are either logical operators (they recurse back into
// this function), or $where.
if (!_.has(that.LOGICAL_OPERATORS, key)) {
throw new Error('Unrecognized logical operator: ' + key);
}
perKeySelectors.push(that.LOGICAL_OPERATORS[key](subSelector));
} else {
var lookUpByIndex = that._makeLookupFunction(key);
var valueSelectorFunc = that.compileValueSelector(subSelector);
perKeySelectors.push(function (doc) {
var branchValues = lookUpByIndex(doc);
// We apply the selector to each 'branched' value and return true if any
// match. This isn't 100% consistent with MongoDB; eg, see:
// https://jira.mongodb.org/browse/SERVER-8585
return _.any(branchValues, valueSelectorFunc);
});
}
compileValueSelector: function (valueSelector) {
var that = Bikini.DataSelector;
if (valueSelector === null) { // undefined or null
return function (value) {
return that._anyIfArray(value, function (x) {
return x === null; // undefined or null
});
};
}
return function (record) {
var doc = _.isFunction(record.getData) ? record.getData() : record;
return _.all(perKeySelectors, function (f) {
return f(doc);
});
};
},
// Selector is a non-null primitive (and not an array or RegExp either).
if (!_.isObject(valueSelector)) {
return function (value) {
return that._anyIfArray(value, function (x) {
return x === valueSelector;
});
};
}
compileValueSelector: function (valueSelector) {
var that = Bikini.DataSelector;
if (valueSelector === null) { // undefined or null
return function (value) {
return that._anyIfArray(value, function (x) {
return x === null; // undefined or null
});
};
if (_.isRegExp(valueSelector)) {
return function (value) {
if (_.isUndefined(value)) {
return false;
}
return that._anyIfArray(value, function (x) {
return valueSelector.test(x);
});
};
}
// Selector is a non-null primitive (and not an array or RegExp either).
if (!_.isObject(valueSelector)) {
return function (value) {
return that._anyIfArray(value, function (x) {
return x === valueSelector;
});
};
// Arrays match either identical arrays or arrays that contain it as a value.
if (_.isArray(valueSelector)) {
return function (value) {
if (!_.isArray(value)) {
return false;
}
return that._anyIfArrayPlus(value, function (x) {
return that._equal(valueSelector, x);
});
};
}
if (_.isRegExp(valueSelector)) {
return function (value) {
if (_.isUndefined(value)) {
return false;
}
return that._anyIfArray(value, function (x) {
return valueSelector.test(x);
});
};
// It's an object, but not an array or regexp.
if (this.hasOperators(valueSelector)) {
var operatorFunctions = [];
_.each(valueSelector, function (operand, operator) {
if (!_.has(that.VALUE_OPERATORS, operator)) {
throw new Error('Unrecognized operator: ' + operator);
}
operatorFunctions.push(that.VALUE_OPERATORS[operator](operand, valueSelector.$options));
});
return function (value) {
return _.all(operatorFunctions, function (f) {
return f(value);
});
};
}
// Arrays match either identical arrays or arrays that contain it as a value.
if (_.isArray(valueSelector)) {
return function (value) {
if (!_.isArray(value)) {
return false;
}
return that._anyIfArrayPlus(value, function (x) {
return that._equal(valueSelector, x);
});
};
}
// It's a literal; compare value (or element of value array) directly to the
// selector.
return function (value) {
return that._anyIfArray(value, function (x) {
return that._equal(valueSelector, x);
});
};
},
// It's an object, but not an array or regexp.
if (this.hasOperators(valueSelector)) {
var operatorFunctions = [];
_.each(valueSelector, function (operand, operator) {
if (!_.has(that.VALUE_OPERATORS, operator)) {
throw new Error('Unrecognized operator: ' + operator);
}
operatorFunctions.push(that.VALUE_OPERATORS[operator](operand, valueSelector.$options));
});
return function (value) {
return _.all(operatorFunctions, function (f) {
return f(value);
});
};
}
// _makeLookupFunction(key) returns a lookup function.
//
// A lookup function takes in a document and returns an array of matching
// values. This array has more than one element if any segment of the key other
// than the last one is an array. ie, any arrays found when doing non-final
// lookups result in this function 'branching'; each element in the returned
// array represents the value found at this branch. If any branch doesn't have a
// final value for the full key, its element in the returned list will be
// undefined. It always returns a non-empty array.
//
// _makeLookupFunction('a.x')({a: {x: 1}}) returns [1]
// _makeLookupFunction('a.x')({a: {x: [1]}}) returns [[1]]
// _makeLookupFunction('a.x')({a: 5}) returns [undefined]
// _makeLookupFunction('a.x')({a: [{x: 1},
// {x: [2]},
// {y: 3}]})
// returns [1, [2], undefined]
_makeLookupFunction: function (key) {
var dotLocation = key.indexOf('.');
var first, lookupRest, nextIsNumeric;
if (dotLocation === -1) {
first = key;
} else {
first = key.substr(0, dotLocation);
var rest = key.substr(dotLocation + 1);
lookupRest = this._makeLookupFunction(rest);
// Is the next (perhaps final) piece numeric (ie, an array lookup?)
nextIsNumeric = /^\d+(\.|$)/.test(rest);
}
// It's a literal; compare value (or element of value array) directly to the
// selector.
return function (value) {
return that._anyIfArray(value, function (x) {
return that._equal(valueSelector, x);
});
};
},
return function (doc) {
if (doc === null) { // null or undefined
return [undefined];
}
var firstLevel = doc[first];
// _makeLookupFunction(key) returns a lookup function.
//
// A lookup function takes in a document and returns an array of matching
// values. This array has more than one element if any segment of the key other
// than the last one is an array. ie, any arrays found when doing non-final
// lookups result in this function 'branching'; each element in the returned
// array represents the value found at this branch. If any branch doesn't have a
// final value for the full key, its element in the returned list will be
// undefined. It always returns a non-empty array.
//
// _makeLookupFunction('a.x')({a: {x: 1}}) returns [1]
// _makeLookupFunction('a.x')({a: {x: [1]}}) returns [[1]]
// _makeLookupFunction('a.x')({a: 5}) returns [undefined]
// _makeLookupFunction('a.x')({a: [{x: 1},
// {x: [2]},
// {y: 3}]})
// returns [1, [2], undefined]
_makeLookupFunction: function (key) {
var dotLocation = key.indexOf('.');
var first, lookupRest, nextIsNumeric;
if (dotLocation === -1) {
first = key;
} else {
first = key.substr(0, dotLocation);
var rest = key.substr(dotLocation + 1);
lookupRest = this._makeLookupFunction(rest);
// Is the next (perhaps final) piece numeric (ie, an array lookup?)
nextIsNumeric = /^\d+(\.|$)/.test(rest);
}
// We don't 'branch' at the final level.
if (!lookupRest) {
return [firstLevel];
}
return function (doc) {
if (doc === null) { // null or undefined
return [undefined];
}
var firstLevel = doc[first];
// It's an empty array, and we're not done: we won't find anything.
if (_.isArray(firstLevel) && firstLevel.length === 0) {
return [undefined];
}
// We don't 'branch' at the final level.
if (!lookupRest) {
return [firstLevel];
}
// For each result at this level, finish the lookup on the rest of the key,
// and return everything we find. Also, if the next result is a number,
// don't branch here.
//
// Technically, in MongoDB, we should be able to handle the case where
// objects have numeric keys, but Mongo doesn't actually handle this
// consistently yet itself, see eg
// https://jira.mongodb.org/browse/SERVER-2898
// https://github.com/mongodb/mongo/blob/master/jstests/array_match2.js
if (!_.isArray(firstLevel) || nextIsNumeric) {
firstLevel = [firstLevel];
}
return Array.prototype.concat.apply([], _.map(firstLevel, lookupRest));
};
},
// It's an empty array, and we're not done: we won't find anything.
if (_.isArray(firstLevel) && firstLevel.length === 0) {
return [undefined];
}
_anyIfArray: function (x, f) {
if (_.isArray(x)) {
return _.any(x, f);
}
return f(x);
},
// For each result at this level, finish the lookup on the rest of the key,
// and return everything we find. Also, if the next result is a number,
// don't branch here.
//
// Technically, in MongoDB, we should be able to handle the case where
// objects have numeric keys, but Mongo doesn't actually handle this
// consistently yet itself, see eg
// https://jira.mongodb.org/browse/SERVER-2898
// https://github.com/mongodb/mongo/blob/master/jstests/array_match2.js
if (!_.isArray(firstLevel) || nextIsNumeric) {
firstLevel = [firstLevel];
}
return Array.prototype.concat.apply([], _.map(firstLevel, lookupRest));
};
},
_anyIfArrayPlus: function (x, f) {
if (f(x)) {
return true;
}
return _.isArray(x) && _.any(x, f);
},
_anyIfArray: function (x, f) {
if (_.isArray(x)) {
return _.any(x, f);
}
return f(x);
},
// Is this selector just shorthand for lookup by _id?
_selectorIsId: function (selector) {
return _.isString(selector) || _.isNumber(selector);
},
_anyIfArrayPlus: function (x, f) {
if (f(x)) {
return true;
}
return _.isArray(x) && _.any(x, f);
},
// deep equality test: use for literal document and array matches
_equal: function (a, b) {
return Bikini.Field.prototype._equals(a, b, true);
},
// Is this selector just shorthand for lookup by _id?
_selectorIsId: function (selector) {
return _.isString(selector) || _.isNumber(selector);
_cmp: function (a, b) {
return Bikini.Field.prototype._cmp(a, b);
},
LOGICAL_OPERATORS: {
'$and': function (subSelector) {
if (!_.isArray(subSelector) || _.isEmpty(subSelector)) {
throw new Error('$and/$or/$nor must be nonempty array');
}
var subSelectorFunctions = _.map(subSelector, Bikini.DataSelector.compileDocSelector);
return function (doc) {
return _.all(subSelectorFunctions, function (f) {
return f(doc);
});
};
},
// deep equality test: use for literal document and array matches
_equal: function (a, b) {
return Bikini.Field.prototype._equals(a, b, true);
'$or': function (subSelector) {
if (!_.isArray(subSelector) || _.isEmpty(subSelector)) {
throw new Error('$and/$or/$nor must be nonempty array');
}
var subSelectorFunctions = _.map(subSelector, Bikini.DataSelector.compileDocSelector);
return function (doc) {
return _.any(subSelectorFunctions, function (f) {
return f(doc);
});
};
},
_cmp: function (a, b) {
return Bikini.Field.prototype._cmp(a, b);
'$nor': function (subSelector) {
if (!_.isArray(subSelector) || _.isEmpty(subSelector)) {
throw new Error('$and/$or/$nor must be nonempty array');
}
var subSelectorFunctions = _.map(subSelector, Bikini.DataSelector.compileDocSelector);
return function (doc) {
return _.all(subSelectorFunctions, function (f) {
return !f(doc);
});
};
},
LOGICAL_OPERATORS: {
'$and': function (subSelector) {
if (!_.isArray(subSelector) || _.isEmpty(subSelector)) {
throw new Error('$and/$or/$nor must be nonempty array');
}
var subSelectorFunctions = _.map(subSelector, Bikini.DataSelector.compileDocSelector);
return function (doc) {
return _.all(subSelectorFunctions, function (f) {
return f(doc);
});
};
},
'$where': function (selectorValue) {
if (!_.isFunction(selectorValue)) {
var value = selectorValue;
selectorValue = function () {
return value;
};
}
return function (doc) {
return selectorValue.call(doc);
};
}
},
'$or': function (subSelector) {
if (!_.isArray(subSelector) || _.isEmpty(subSelector)) {
throw new Error('$and/$or/$nor must be nonempty array');
}
var subSelectorFunctions = _.map(subSelector, Bikini.DataSelector.compileDocSelector);
return function (doc) {
return _.any(subSelectorFunctions, function (f) {
return f(doc);
});
};
},
VALUE_OPERATORS: {
'$in': function (operand) {
if (!_.isArray(operand)) {
throw new Error('Argument to $in must be array');
}
return function (value) {
return Bikini.DataSelector._anyIfArrayPlus(value, function (x) {
return _.any(operand, function (operandElt) {
return Bikini.DataSelector._equal(operandElt, x);
});
});
};
},
'$nor': function (subSelector) {
if (!_.isArray(subSelector) || _.isEmpty(subSelector)) {
throw new Error('$and/$or/$nor must be nonempty array');
}
var subSelectorFunctions = _.map(subSelector, Bikini.DataSelector.compileDocSelector);
return function (doc) {
return _.all(subSelectorFunctions, function (f) {
return !f(doc);
});
};
},
'$where': function (selectorValue) {
if (!_.isFunction(selectorValue)) {
var value = selectorValue;
selectorValue = function() { return value; };
}
return function (doc) {
return selectorValue.call(doc);
};
'$all': function (operand) {
if (!_.isArray(operand)) {
throw new Error('Argument to $all must be array');
}
return function (value) {
if (!_.isArray(value)) {
return false;
}
return _.all(operand, function (operandElt) {
return _.any(value, function (valueElt) {
return Bikini.DataSelector._equal(operandElt, valueElt);
});
});
};
},
VALUE_OPERATORS: {
'$in': function (operand) {
if (!_.isArray(operand)) {
throw new Error('Argument to $in must be array');
}
return function (value) {
return Bikini.DataSelector._anyIfArrayPlus(value, function (x) {
return _.any(operand, function (operandElt) {
return Bikini.DataSelector._equal(operandElt, x);
});
});
};
},
'$lt': function (operand) {
return function (value) {
return Bikini.DataSelector._anyIfArray(value, function (x) {
return Bikini.DataSelector._cmp(x, operand) < 0;
});
};
},
'$all': function (operand) {
if (!_.isArray(operand)) {
throw new Error('Argument to $all must be array');
}
return function (value) {
if (!_.isArray(value)) {
return false;
}
return _.all(operand, function (operandElt) {
return _.any(value, function (valueElt) {
return Bikini.DataSelector._equal(operandElt, valueElt);
});
});
};
},
'$lte': function (operand) {
return function (value) {
return Bikini.DataSelector._anyIfArray(value, function (x) {
return Bikini.DataSelector._cmp(x, operand) <= 0;
});
};
},
'$lt': function (operand) {
return function (value) {
return Bikini.DataSelector._anyIfArray(value, function (x) {
return Bikini.DataSelector._cmp(x, operand) < 0;
});
};
},
'$gt': function (operand) {
return function (value) {
return Bikini.DataSelector._anyIfArray(value, function (x) {
return Bikini.DataSelector._cmp(x, operand) > 0;
});
};
},
'$lte': function (operand) {
return function (value) {
return Bikini.DataSelector._anyIfArray(value, function (x) {
return Bikini.DataSelector._cmp(x, operand) <= 0;
});
};
},
'$gte': function (operand) {
return function (value) {
return Bikini.DataSelector._anyIfArray(value, function (x) {
return Bikini.DataSelector._cmp(x, operand) >= 0;
});
};
},
'$gt': function (operand) {
return function (value) {
return Bikini.DataSelector._anyIfArray(value, function (x) {
return Bikini.DataSelector._cmp(x, operand) > 0;
});
};
},
'$ne': function (operand) {
return function (value) {
return !Bikini.DataSelector._anyIfArrayPlus(value, function (x) {
return Bikini.DataSelector._equal(x, operand);
});
};
},
'$gte': function (operand) {
return function (value) {
return Bikini.DataSelector._anyIfArray(value, function (x) {
return Bikini.DataSelector._cmp(x, operand) >= 0;
});
};
},
'$nin': function (operand) {
if (!_.isArray(operand)) {
throw new Error('Argument to $nin must be array');
}
var inFunction = this.VALUE_OPERATORS.$in(operand);
return function (value) {
// Field doesn't exist, so it's not-in operand
if (value === undefined) {
return true;
}
return !inFunction(value);
};
},
'$ne': function (operand) {
return function (value) {
return !Bikini.DataSelector._anyIfArrayPlus(value, function (x) {
return Bikini.DataSelector._equal(x, operand);
});
};
},
'$exists': function (operand) {
return function (value) {
return operand === (value !== undefined);
};
},
'$mod': function (operand) {
var divisor = operand[0], remainder = operand[1];
return function (value) {
return Bikini.DataSelector._anyIfArray(value, function (x) {
return x % divisor === remainder;
});
};
},
'$nin': function (operand) {
if (!_.isArray(operand)) {
throw new Error('Argument to $nin must be array');
}
var inFunction = this.VALUE_OPERATORS.$in(operand);
return function (value) {
// Field doesn't exist, so it's not-in operand
if (value === undefined) {
return true;
}
return !inFunction(value);
};
},
'$size': function (operand) {
return function (value) {
return _.isArray(value) && operand === value.length;
};
},
'$exists': function (operand) {
return function (value) {
return operand === (value !== undefined);
};
},
'$mod': function (operand) {
var divisor = operand[0], remainder = operand[1];
return function (value) {
return Bikini.DataSelector._anyIfArray(value, function (x) {
return x % divisor === remainder;
});
};
},
'$type': function (operand) {
return function (value) {
// A nonexistent field is of no type.
if (_.isUndefined(value)) {
return false;
}
return Bikini.DataSelector._anyIfArray(value, function (x) {
return Bikini.Field.prototype.detectType(x) === operand;
});
};
},
'$size': function (operand) {
return function (value) {
return _.isArray(value) && operand === value.length;
};
},
'$regex': function (operand, options) {
'$type': function (operand) {
return function (value) {
// A nonexistent field is of no type.
if (_.isUndefined(value)) {
return false;
}
return Bikini.DataSelector._anyIfArray(value, function (x) {
return Bikini.Field.prototype.detectType(x) === operand;
});
};
},
if (_.isUndefined(options)) {
// Options passed in $options (even the empty string) always overrides
// options in the RegExp object itself.
'$regex': function (operand, options) {
// Be clear that we only support the JS-supported options, not extended
// ones (eg, Mongo supports x and s). Ideally we would implement x and s
// by transforming the regexp, but not today...
if (/[^gim]/.test(options)) {
throw new Error('Only the i, m, and g regexp options are supported');
}
if (_.isUndefined(options)) {
// Options passed in $options (even the empty string) always overrides
// options in the RegExp object itself.
var regexSource = _.isRegExp(operand) ? operand.source : operand;
operand = new RegExp(regexSource, options);
} else if (!_.isRegExp(operand)) {
operand = new RegExp(operand);
}
// Be clear that we only support the JS-supported options, not extended
// ones (eg, Mongo supports x and s). Ideally we would implement x and s
// by transforming the regexp, but not today...
if (/[^gim]/.test(options)) {
throw new Error('Only the i, m, and g regexp options are supported');
}
return function (value) {
if (_.isUndefined(value)) {
return false;
}
return Bikini.DataSelector._anyIfArray(value, function (x) {
return operand.test(x);
});
};
},
var regexSource = _.isRegExp(operand) ? operand.source : operand;
operand = new RegExp(regexSource, options);
} else if (!_.isRegExp(operand)) {
operand = new RegExp(operand);
}
'$options': function (operand) {
// evaluation happens at the $regex function above
return function (value) {
return true;
};
},
return function (value) {
if (_.isUndefined(value)) {
return false;
}
return Bikini.DataSelector._anyIfArray(value, function (x) {
return operand.test(x);
});
};
},
'$options': function (operand) {
// evaluation happens at the $regex function above
return function (value) {
return true;
};
},
'$elemMatch': function (operand) {
var matcher = Bikini.DataSelector.compileDocSelector(operand);
return function (value) {
if (!_.isArray(value)) {
return false;
}
return _.any(value, function (x) {
return matcher(x);
});
};
},
'$not': function (operand) {
var matcher = Bikini.DataSelector.compileDocSelector(operand);
return function (value) {
return !matcher(value);
};
'$elemMatch': function (operand) {
var matcher = Bikini.DataSelector.compileDocSelector(operand);
return function (value) {
if (!_.isArray(value)) {
return false;
}
return _.any(value, function (x) {
return matcher(x);
});
};
},
// Give a sort spec, which can be in any of these forms:
// {'key1': 1, 'key2': -1}
// [['key1', 'asc'], ['key2', 'desc']]
// ['key1', ['key2', 'desc']]
//
// (.. with the first form being dependent on the key enumeration
// behavior of your javascript VM, which usually does what you mean in
// this case if the key names don't look like integers ..)
//
// return a function that takes two objects, and returns -1 if the
// first object comes first in order, 1 if the second object comes
// first, or 0 if neither object comes before the other.
'$not': function (operand) {
var matcher = Bikini.DataSelector.compileDocSelector(operand);
return function (value) {
return !matcher(value);
};
}
},
compileSort: function (spec) {
var sortSpecParts = [];
// Give a sort spec, which can be in any of these forms:
// {'key1': 1, 'key2': -1}
// [['key1', 'asc'], ['key2', 'desc']]
// ['key1', ['key2', 'desc']]
//
// (.. with the first form being dependent on the key enumeration
// behavior of your javascript VM, which usually does what you mean in
// this case if the key names don't look like integers ..)
//
// return a function that takes two objects, and returns -1 if the
// first object comes first in order, 1 if the second object comes
// first, or 0 if neither object comes before the other.
if (_.isArray(spec)) {
for (var i = 0; i < spec.length; i++) {
if (typeof spec[i] === 'string') {
sortSpecParts.push({
lookup: this._makeLookupFunction(spec[i]),
ascending: true
});
} else {
sortSpecParts.push({
lookup: this._makeLookupFunction(spec[i][0]),
ascending: spec[i][1] !== 'desc'
});
}
}
} else if (typeof spec === 'object') {
for (var key in spec) {
sortSpecParts.push({
lookup: this._makeLookupFunction(key),
ascending: spec[key] >= 0
});
}
compileSort: function (spec) {
var sortSpecParts = [];
if (_.isArray(spec)) {
for (var i = 0; i < spec.length; i++) {
if (typeof spec[i] === 'string') {
sortSpecParts.push({
lookup: this._makeLookupFunction(spec[i]),
ascending: true
});
} else {
throw new Error('Bad sort specification: ', JSON.stringify(spec));
sortSpecParts.push({
lookup: this._makeLookupFunction(spec[i][0]),
ascending: spec[i][1] !== 'desc'
});
}
}
} else if (typeof spec === 'object') {
for (var key in spec) {
sortSpecParts.push({
lookup: this._makeLookupFunction(key),
ascending: spec[key] >= 0
});
}
} else {
throw new Error('Bad sort specification: ', JSON.stringify(spec));
}
if (sortSpecParts.length === 0) {
return function () {
return 0;
};
if (sortSpecParts.length === 0) {
return function () {
return 0;
};
}
// reduceValue takes in all the possible values for the sort key along various
// branches, and returns the min or max value (according to the bool
// findMin). Each value can itself be an array, and we look at its values
// too. (ie, we do a single level of flattening on branchValues, then find the
// min/max.)
var reduceValue = function (branchValues, findMin) {
var reduced;
var first = true;
// Iterate over all the values found in all the branches, and if a value is
// an array itself, iterate over the values in the array separately.
_.each(branchValues, function (branchValue) {
// Value not an array? Pretend it is.
if (!_.isArray(branchValue)) {
branchValue = [branchValue];
}
// Value is an empty array? Pretend it was missing, since that's where it
// should be sorted.
if (_.isArray(branchValue) && branchValue.length === 0) {
branchValue = [undefined];
}
_.each(branchValue, function (value) {
// We should get here at least once: lookup functions return non-empty
// arrays, so the outer loop runs at least once, and we prevented
// branchValue from being an empty array.
if (first) {
reduced = value;
first = false;
} else {
// Compare the value we found to the value we found so far, saving it
// if it's less (for an ascending sort) or more (for a descending
// sort).
var cmp = Bikini.DataSelector._cmp(reduced, value);
if ((findMin && cmp > 0) || (!findMin && cmp < 0)) {
reduced = value;
}
}
});
});
return reduced;
};
// reduceValue takes in all the possible values for the sort key along various
// branches, and returns the min or max value (according to the bool
// findMin). Each value can itself be an array, and we look at its values
// too. (ie, we do a single level of flattening on branchValues, then find the
// min/max.)
var reduceValue = function (branchValues, findMin) {
var reduced;
var first = true;
// Iterate over all the values found in all the branches, and if a value is
// an array itself, iterate over the values in the array separately.
_.each(branchValues, function (branchValue) {
// Value not an array? Pretend it is.
if (!_.isArray(branchValue)) {
branchValue = [branchValue];
}
// Value is an empty array? Pretend it was missing, since that's where it
// should be sorted.
if (_.isArray(branchValue) && branchValue.length === 0) {
branchValue = [undefined];
}
_.each(branchValue, function (value) {
// We should get here at least once: lookup functions return non-empty
// arrays, so the outer loop runs at least once, and we prevented
// branchValue from being an empty array.
if (first) {
reduced = value;
first = false;
} else {
// Compare the value we found to the value we found so far, saving it
// if it's less (for an ascending sort) or more (for a descending
// sort).
var cmp = Bikini.DataSelector._cmp(reduced, value);
if ((findMin && cmp > 0) || (!findMin && cmp < 0)) {
reduced = value;
}
}
});
});
return reduced;
};
return function (a, b) {
a = a.attributes ? a.attributes : a;
b = b.attributes ? b.attributes : b;
for (var i = 0; i < sortSpecParts.length; ++i) {
var specPart = sortSpecParts[i];
var aValue = reduceValue(specPart.lookup(a), specPart.ascending);
var bValue = reduceValue(specPart.lookup(b), specPart.ascending);
var compare = Bikini.DataSelector._cmp(aValue, bValue);
if (compare !== 0) {
return specPart.ascending ? compare : -compare;
}
}
return 0;
};
}
return function (a, b) {
a = a.attributes ? a.attributes : a;
b = b.attributes ? b.attributes : b;
for (var i = 0; i < sortSpecParts.length; ++i) {
var specPart = sortSpecParts[i];
var aValue = reduceValue(specPart.lookup(a), specPart.ascending);
var bValue = reduceValue(specPart.lookup(b), specPart.ascending);
var compare = Bikini.DataSelector._cmp(aValue, bValue);
if (compare !== 0) {
return specPart.ascending ? compare : -compare;
}
}
return 0;
};
}
});

@@ -19,15 +19,15 @@ // Copyright (c) 2013 M-Way Solutions GmbH

Bikini.Entity = function (options) {
var fields = this.fields;
this.fields = {};
this._mergeFields(fields);
options = options || {};
if (options.fields) {
this._mergeFields(options.fields);
}
this.typeMapping = options.typeMapping || this.typeMapping;
var collection = options.collection;
var model = options.model || (collection ? collection.prototype.model : null);
this.idAttribute = options.idAttribute || this.idAttribute || (model ? model.prototype.idAttribute : '');
this._updateFields(this.typeMapping);
this.initialize.apply(this, arguments);
var fields = this.fields;
this.fields = {};
this._mergeFields(fields);
options = options || {};
if (options.fields) {
this._mergeFields(options.fields);
}
this.typeMapping = options.typeMapping || this.typeMapping;
var collection = options.collection;
var model = options.model || (collection ? collection.prototype.model : null);
this.idAttribute = options.idAttribute || this.idAttribute || (model ? model.prototype.idAttribute : '');
this._updateFields(this.typeMapping);
this.initialize.apply(this, arguments);
};

@@ -43,23 +43,23 @@

Bikini.Entity.from = function (entity, options) {
// is not an instance of Bikini.Entity
if (!Bikini.Entity.prototype.isPrototypeOf(entity)) {
// if this is a prototype of an entity, create an instance
if (_.isFunction(entity) &&
Bikini.Entity.prototype.isPrototypeOf(entity.prototype)) {
var Entity = entity;
entity = new Entity(options);
} else {
if (typeof entity === 'string') {
entity = {
name: entity
};
}
// if this is just a config create a new Entity
var E = Bikini.Entity.extend(entity);
entity = new E(options);
}
} else if (options && options.typeMapping) {
entity._updateFields(options.typeMapping);
// is not an instance of Bikini.Entity
if (!Bikini.Entity.prototype.isPrototypeOf(entity)) {
// if this is a prototype of an entity, create an instance
if (_.isFunction(entity) &&
Bikini.Entity.prototype.isPrototypeOf(entity.prototype)) {
var Entity = entity;
entity = new Entity(options);
} else {
if (typeof entity === 'string') {
entity = {
name: entity
};
}
// if this is just a config create a new Entity
var E = Bikini.Entity.extend(entity);
entity = new E(options);
}
return entity;
} else if (options && options.typeMapping) {
entity._updateFields(options.typeMapping);
}
return entity;
};

@@ -73,231 +73,231 @@

/**
* The type of this object.
*
* @type String
*/
_type: 'Bikini.Entity',
/**
* The type of this object.
*
* @type String
*/
_type: 'Bikini.Entity',
/**
* Entity name, used for tables or collections
*
* @type String
*/
name: '',
/**
* Entity name, used for tables or collections
*
* @type String
*/
name: '',
/**
* idAttribute, should be the same as in the corresponding model
*
* @type String
*/
idAttribute: '',
/**
* idAttribute, should be the same as in the corresponding model
*
* @type String
*/
idAttribute: '',
/**
*
*
* @type Object
*/
fields: {},
/**
*
*
* @type Object
*/
fields: {},
/**
* initialize function will be called after creating an entity
*/
initialize: function () {
},
/**
* initialize function will be called after creating an entity
*/
initialize: function () {
},
/**
* get the field list of this entity
*
* @returns {Object}
*/
getFields: function () {
return this.fields;
},
/**
* get the field list of this entity
*
* @returns {Object}
*/
getFields: function () {
return this.fields;
},
/**
* get a specified field from this entity
*
* @param fieldKey
* @returns Bikini.Field instance
*/
getField: function (fieldKey) {
return this.fields[fieldKey];
},
/**
* get a specified field from this entity
*
* @param fieldKey
* @returns Bikini.Field instance
*/
getField: function (fieldKey) {
return this.fields[fieldKey];
},
/**
* get the translated name of a field
*
* @param fieldKey
* @returns String
*/
getFieldName: function (fieldKey) {
var field = this.getField(fieldKey);
return field && field.name ? field.name : fieldKey;
},
/**
* get the translated name of a field
*
* @param fieldKey
* @returns String
*/
getFieldName: function (fieldKey) {
var field = this.getField(fieldKey);
return field && field.name ? field.name : fieldKey;
},
/**
* get the primary key of this entity
*
* @returns String
*/
getKey: function () {
return this.idAttribute || Bikini.Model.idAttribute;
},
/**
* get the primary key of this entity
*
* @returns String
*/
getKey: function () {
return this.idAttribute || Bikini.Model.idAttribute;
},
/**
* get a list of keys for this entity
*
* @returns {Array}
*/
getKeys: function () {
return this.splitKey(this.getKey());
},
/**
* get a list of keys for this entity
*
* @returns {Array}
*/
getKeys: function () {
return this.splitKey(this.getKey());
},
/**
* Splits a comma separated list of keys to a key array
*
* @returns {Array} array of keys
*/
splitKey: function (key) {
var keys = [];
if (_.isString(key)) {
_.each(key.split(','), function (key) {
var k = key.trim();
if (k) {
keys.push(k);
}
});
/**
* Splits a comma separated list of keys to a key array
*
* @returns {Array} array of keys
*/
splitKey: function (key) {
var keys = [];
if (_.isString(key)) {
_.each(key.split(','), function (key) {
var k = key.trim();
if (k) {
keys.push(k);
}
return keys;
},
});
}
return keys;
},
/**
* merge a new list of fields into the exiting fields
*
* @param newFields
* @private
*/
_mergeFields: function (newFields) {
if (!_.isObject(this.fields)) {
this.fields = {};
/**
* merge a new list of fields into the exiting fields
*
* @param newFields
* @private
*/
_mergeFields: function (newFields) {
if (!_.isObject(this.fields)) {
this.fields = {};
}
var that = this;
if (_.isObject(newFields)) {
_.each(newFields, function (value, key) {
if (!that.fields[key]) {
that.fields[key] = new Bikini.Field(value);
} else {
that.fields[key].merge(value);
}
var that = this;
if (_.isObject(newFields)) {
_.each(newFields, function (value, key) {
if (!that.fields[key]) {
that.fields[key] = new Bikini.Field(value);
} else {
that.fields[key].merge(value);
}
});
}
},
});
}
},
/**
* check and update missing properties of fields
*
* @param typeMapping
* @private
*/
_updateFields: function (typeMapping) {
var that = this;
_.each(this.fields, function (value, key) {
// remove unused properties
if (value.persistent === NO) {
delete that.fields[key];
} else {
// add missing names
if (!value.name) {
value.name = key;
}
// apply default type conversions
if (typeMapping && typeMapping[value.type]) {
value.type = typeMapping[value.type];
}
}
});
},
/**
* transform the given data to attributes
* considering the field specifications
*
* @param data
* @param id
* @param fields
* @returns {*}
*/
toAttributes: function (data, id, fields) {
fields = fields || this.fields;
if (data && !_.isEmpty(fields)) {
// map field names
var value, attributes = {};
_.each(fields, function (field, key) {
value = _.isFunction(data.get) ? data.get(field.name) : data[field.name];
attributes[key] = value;
});
return attributes;
/**
* check and update missing properties of fields
*
* @param typeMapping
* @private
*/
_updateFields: function (typeMapping) {
var that = this;
_.each(this.fields, function (value, key) {
// remove unused properties
if (value.persistent === NO) {
delete that.fields[key];
} else {
// add missing names
if (!value.name) {
value.name = key;
}
return data;
},
/**
* transform the given attributes to the destination data format
* considering the field specifications
*
* @param attrs
* @param fields
* @returns {*}
*/
fromAttributes: function (attrs, fields) {
fields = fields || this.fields;
if (attrs && !_.isEmpty(fields)) {
var data = {};
_.each(fields, function (field, key) {
var value = _.isFunction(attrs.get) ? attrs.get(key) : attrs[key];
value = field.transform(value);
if (!_.isUndefined(value)) {
data[field.name] = value;
}
});
return data;
// apply default type conversions
if (typeMapping && typeMapping[value.type]) {
value.type = typeMapping[value.type];
}
return attrs;
},
}
});
},
/**
* set the id of the given model or attributes
*
* @param attrs
* @param id
* @returns {*}
*/
setId: function (attrs, id) {
if (attrs && id) {
var key = this.getKey() || attrs.idAttribute;
if (key) {
if (_.isFunction(attrs.set)) {
attrs.set(key, id);
} else {
attrs[key] = id;
}
}
/**
* transform the given data to attributes
* considering the field specifications
*
* @param data
* @param id
* @param fields
* @returns {*}
*/
toAttributes: function (data, id, fields) {
fields = fields || this.fields;
if (data && !_.isEmpty(fields)) {
// map field names
var value, attributes = {};
_.each(fields, function (field, key) {
value = _.isFunction(data.get) ? data.get(field.name) : data[field.name];
attributes[key] = value;
});
return attributes;
}
return data;
},
/**
* transform the given attributes to the destination data format
* considering the field specifications
*
* @param attrs
* @param fields
* @returns {*}
*/
fromAttributes: function (attrs, fields) {
fields = fields || this.fields;
if (attrs && !_.isEmpty(fields)) {
var data = {};
_.each(fields, function (field, key) {
var value = _.isFunction(attrs.get) ? attrs.get(key) : attrs[key];
value = field.transform(value);
if (!_.isUndefined(value)) {
data[field.name] = value;
}
return attrs;
},
});
return data;
}
return attrs;
},
/**
* get the id of the given model or attributes
*
* @param attrs
* @returns {*|Object|key|*}
*/
getId: function (attrs) {
if (attrs) {
var key = this.getKey() || attrs.idAttribute;
if (key) {
return _.isFunction(attrs.get) ? attrs.get(key) : attrs[key];
}
/**
* set the id of the given model or attributes
*
* @param attrs
* @param id
* @returns {*}
*/
setId: function (attrs, id) {
if (attrs && id) {
var key = this.getKey() || attrs.idAttribute;
if (key) {
if (_.isFunction(attrs.set)) {
attrs.set(key, id);
} else {
attrs[key] = id;
}
}
}
return attrs;
},
/**
* get the id of the given model or attributes
*
* @param attrs
* @returns {*|Object|key|*}
*/
getId: function (attrs) {
if (attrs) {
var key = this.getKey() || attrs.idAttribute;
if (key) {
return _.isFunction(attrs.get) ? attrs.get(key) : attrs[key];
}
}
}
});

@@ -19,4 +19,4 @@ // Copyright (c) 2013 M-Way Solutions GmbH

Bikini.Field = function (options) {
this.merge(options);
this.initialize.apply(this, arguments);
this.merge(options);
this.initialize.apply(this, arguments);
};

@@ -30,348 +30,348 @@

/**
* The type of this object.
*
* @type String
*/
_type: 'Bikini.Field',
/**
* The type of this object.
*
* @type String
*/
_type: 'Bikini.Field',
name: null,
name: null,
type: null,
type: null,
index: null,
index: null,
defaultValue: undefined,
defaultValue: undefined,
length: null,
length: null,
required: NO,
required: NO,
persistent: YES,
persistent: YES,
initialize: function () {
},
initialize: function () {
},
/**
* merge field properties into this instance
*
* @param obj
*/
merge: function (obj) {
obj = _.isString(obj) ? { type: obj } : (obj || {});
/**
* merge field properties into this instance
*
* @param obj
*/
merge: function (obj) {
obj = _.isString(obj) ? {type: obj} : (obj || {});
this.name = !_.isUndefined(obj.name) ? obj.name : this.name;
this.type = !_.isUndefined(obj.type) ? obj.type : this.type;
this.index = !_.isUndefined(obj.index) ? obj.index : this.index;
this.defaultValue = !_.isUndefined(obj.defaultValue) ? obj.defaultValue : this.defaultValue;
this.length = !_.isUndefined(obj.length) ? obj.length : this.length;
this.required = !_.isUndefined(obj.required) ? obj.required : this.required;
this.persistent = !_.isUndefined(obj.persistent) ? obj.persistent : this.persistent;
},
this.name = !_.isUndefined(obj.name) ? obj.name : this.name;
this.type = !_.isUndefined(obj.type) ? obj.type : this.type;
this.index = !_.isUndefined(obj.index) ? obj.index : this.index;
this.defaultValue = !_.isUndefined(obj.defaultValue) ? obj.defaultValue : this.defaultValue;
this.length = !_.isUndefined(obj.length) ? obj.length : this.length;
this.required = !_.isUndefined(obj.required) ? obj.required : this.required;
this.persistent = !_.isUndefined(obj.persistent) ? obj.persistent : this.persistent;
},
/**
* converts the give value into the required data type
*
* @param value
* @param type
* @returns {*}
*/
transform: function (value, type) {
type = type || this.type;
try {
if (_.isUndefined(value)) {
return this.defaultValue;
}
if (type === Bikini.DATA.TYPE.STRING || type === Bikini.DATA.TYPE.TEXT) {
if (_.isObject(value)) {
return JSON.stringify(value);
} else {
return _.isNull(value) ? 'null' : value.toString();
}
} else if (type === Bikini.DATA.TYPE.INTEGER) {
return parseInt(value);
} else if (type === Bikini.DATA.TYPE.BOOLEAN) {
return value === true || value === 'true'; // true, 1, "1" or "true"
} else if (type === Bikini.DATA.TYPE.FLOAT) {
return parseFloat(value);
} else if (type === Bikini.DATA.TYPE.OBJECT || type === Bikini.DATA.TYPE.ARRAY) {
if (!_.isObject(value)) {
return _.isString(value) ? JSON.parse(value) : null;
}
} else if (type === Bikini.DATA.TYPE.DATE) {
if (!Bikini.Date.isPrototypeOf(value)) {
var date = value ? Bikini.Date.create(value) : null;
return date && date.isValid() ? date : null;
}
} else if (type === Bikini.DATA.TYPE.OBJECTID) {
if (!Bikini.ObjectID.prototype.isPrototypeOf(value)) {
return _.isString(value) ? new Bikini.ObjectID(value) : null;
}
}
return value;
} catch (e) {
console.error('Failed converting value! ' + e.message);
/**
* converts the give value into the required data type
*
* @param value
* @param type
* @returns {*}
*/
transform: function (value, type) {
type = type || this.type;
try {
if (_.isUndefined(value)) {
return this.defaultValue;
}
if (type === Bikini.DATA.TYPE.STRING || type === Bikini.DATA.TYPE.TEXT) {
if (_.isObject(value)) {
return JSON.stringify(value);
} else {
return _.isNull(value) ? 'null' : value.toString();
}
},
/**
* check to values to be equal for the type of this field
*
* @param a
* @param b
* @returns {*}
*/
equals: function (a, b) {
var v1 = this.transform(a);
var v2 = this.transform(b);
return this._equals(v1, v2, _.isArray(v1));
},
/**
* check if this field holds binary data
*
* @param obj
* @returns {boolean|*}
*/
isBinary: function (obj) {
return (typeof Uint8Array !== 'undefined' && obj instanceof Uint8Array) || (obj && obj.$Uint8ArrayPolyfill);
},
/**
* detect the type of a given value
*
* @param v
* @returns {*}
*/
detectType: function (v) {
if (_.isNumber(v)) {
return Bikini.DATA.TYPE.FLOAT;
} else if (type === Bikini.DATA.TYPE.INTEGER) {
return parseInt(value);
} else if (type === Bikini.DATA.TYPE.BOOLEAN) {
return value === true || value === 'true'; // true, 1, "1" or "true"
} else if (type === Bikini.DATA.TYPE.FLOAT) {
return parseFloat(value);
} else if (type === Bikini.DATA.TYPE.OBJECT || type === Bikini.DATA.TYPE.ARRAY) {
if (!_.isObject(value)) {
return _.isString(value) ? JSON.parse(value) : null;
}
if (_.isString(v)) {
return Bikini.DATA.TYPE.STRING;
} else if (type === Bikini.DATA.TYPE.DATE) {
if (!Bikini.Date.isPrototypeOf(value)) {
var date = value ? Bikini.Date.create(value) : null;
return date && date.isValid() ? date : null;
}
if (_.isBoolean(v)) {
return Bikini.DATA.TYPE.BOOLEAN;
} else if (type === Bikini.DATA.TYPE.OBJECTID) {
if (!Bikini.ObjectID.prototype.isPrototypeOf(value)) {
return _.isString(value) ? new Bikini.ObjectID(value) : null;
}
if (_.isArray(v)) {
return Bikini.DATA.TYPE.ARRAY;
}
if (_.isNull(v)) {
return Bikini.DATA.TYPE.NULL;
}
if (_.isDate(v) || Bikini.Date.isPrototypeOf(v)) {
return Bikini.DATA.TYPE.DATE;
}
if (Bikini.ObjectID.prototype.isPrototypeOf(v)) {
return Bikini.DATA.TYPE.OBJECTID;
}
if (this.isBinary(v)) {
return Bikini.DATA.TYPE.BINARY;
}
return Bikini.DATA.TYPE.OBJECT;
},
}
return value;
} catch (e) {
console.error('Failed converting value! ' + e.message);
}
},
/**
* returns the sort order for the given type, used by sorting different type
*
* @param type
* @returns {number}
*/
typeOrder: function (type) {
switch (type) {
case Bikini.DATA.TYPE.NULL :
return 0;
case Bikini.DATA.TYPE.FLOAT :
return 1;
case Bikini.DATA.TYPE.STRING :
return 2;
case Bikini.DATA.TYPE.OBJECT :
return 3;
case Bikini.DATA.TYPE.ARRAY :
return 4;
case Bikini.DATA.TYPE.BINARY :
return 5;
case Bikini.DATA.TYPE.DATE :
return 6;
}
return -1;
},
/**
* check to values to be equal for the type of this field
*
* @param a
* @param b
* @returns {*}
*/
equals: function (a, b) {
var v1 = this.transform(a);
var v2 = this.transform(b);
return this._equals(v1, v2, _.isArray(v1));
},
_equals: function (a, b, keyOrderSensitive) {
var that = this;
var i;
if (a === b) {
return true;
/**
* check if this field holds binary data
*
* @param obj
* @returns {boolean|*}
*/
isBinary: function (obj) {
return (typeof Uint8Array !== 'undefined' && obj instanceof Uint8Array) || (obj && obj.$Uint8ArrayPolyfill);
},
/**
* detect the type of a given value
*
* @param v
* @returns {*}
*/
detectType: function (v) {
if (_.isNumber(v)) {
return Bikini.DATA.TYPE.FLOAT;
}
if (_.isString(v)) {
return Bikini.DATA.TYPE.STRING;
}
if (_.isBoolean(v)) {
return Bikini.DATA.TYPE.BOOLEAN;
}
if (_.isArray(v)) {
return Bikini.DATA.TYPE.ARRAY;
}
if (_.isNull(v)) {
return Bikini.DATA.TYPE.NULL;
}
if (_.isDate(v) || Bikini.Date.isPrototypeOf(v)) {
return Bikini.DATA.TYPE.DATE;
}
if (Bikini.ObjectID.prototype.isPrototypeOf(v)) {
return Bikini.DATA.TYPE.OBJECTID;
}
if (this.isBinary(v)) {
return Bikini.DATA.TYPE.BINARY;
}
return Bikini.DATA.TYPE.OBJECT;
},
/**
* returns the sort order for the given type, used by sorting different type
*
* @param type
* @returns {number}
*/
typeOrder: function (type) {
switch (type) {
case Bikini.DATA.TYPE.NULL :
return 0;
case Bikini.DATA.TYPE.FLOAT :
return 1;
case Bikini.DATA.TYPE.STRING :
return 2;
case Bikini.DATA.TYPE.OBJECT :
return 3;
case Bikini.DATA.TYPE.ARRAY :
return 4;
case Bikini.DATA.TYPE.BINARY :
return 5;
case Bikini.DATA.TYPE.DATE :
return 6;
}
return -1;
},
_equals: function (a, b, keyOrderSensitive) {
var that = this;
var i;
if (a === b) {
return true;
}
if (!a || !b) { // if either one is false, they'd have to be === to be equal
return false;
}
if (!(_.isObject(a) && _.isObject(b))) {
return false;
}
if (a instanceof Date && b instanceof Date) {
return a.valueOf() === b.valueOf();
}
if (this.isBinary(a) && this.isBinary(b)) {
if (a.length !== b.length) {
return false;
}
for (i = 0; i < a.length; i++) {
if (a[i] !== b[i]) {
return false;
}
if (!a || !b) { // if either one is false, they'd have to be === to be equal
return false;
}
return true;
}
if (_.isFunction(a.equals)) {
return a.equals(b);
}
if (_.isArray(a)) {
if (!_.isArray(b)) {
return false;
}
if (a.length !== b.length) {
return false;
}
for (i = 0; i < a.length; i++) {
if (!that.equals(a[i], b[i], keyOrderSensitive)) {
return false;
}
if (!(_.isObject(a) && _.isObject(b))) {
return false;
}
return true;
}
// fall back to structural equality of objects
var ret;
if (keyOrderSensitive) {
var bKeys = [];
_.each(b, function (val, x) {
bKeys.push(x);
});
i = 0;
ret = _.all(a, function (val, x) {
if (i >= bKeys.length) {
return false;
}
if (a instanceof Date && b instanceof Date) {
return a.valueOf() === b.valueOf();
if (x !== bKeys[i]) {
return false;
}
if (this.isBinary(a) && this.isBinary(b)) {
if (a.length !== b.length) {
return false;
}
for (i = 0; i < a.length; i++) {
if (a[i] !== b[i]) {
return false;
}
}
return true;
if (!that.equals(val, b[bKeys[i]], keyOrderSensitive)) {
return false;
}
if (_.isFunction(a.equals)) {
return a.equals(b);
i++;
return true;
});
return ret && i === bKeys.length;
} else {
i = 0;
ret = _.all(a, function (val, key) {
if (!_.has(b, key)) {
return false;
}
if (_.isArray(a)) {
if (!_.isArray(b)) {
return false;
}
if (a.length !== b.length) {
return false;
}
for (i = 0; i < a.length; i++) {
if (!that.equals(a[i], b[i], keyOrderSensitive)) {
return false;
}
}
return true;
if (!that.equals(val, b[key], keyOrderSensitive)) {
return false;
}
// fall back to structural equality of objects
var ret;
if (keyOrderSensitive) {
var bKeys = [];
_.each(b, function (val, x) {
bKeys.push(x);
});
i = 0;
ret = _.all(a, function (val, x) {
if (i >= bKeys.length) {
return false;
}
if (x !== bKeys[i]) {
return false;
}
if (!that.equals(val, b[bKeys[i]], keyOrderSensitive)) {
return false;
}
i++;
return true;
});
return ret && i === bKeys.length;
} else {
i = 0;
ret = _.all(a, function (val, key) {
if (!_.has(b, key)) {
return false;
}
if (!that.equals(val, b[key], keyOrderSensitive)) {
return false;
}
i++;
return true;
});
return ret && _.size(b) === i;
}
},
i++;
return true;
});
return ret && _.size(b) === i;
}
},
/**
* compare two values of unknown type according to BSON ordering
* semantics. (as an extension, consider 'undefined' to be less than
* any other value.) return negative if a is less, positive if b is
* less, or 0 if equal
*
* @param a
* @param b
* @returns {*}
* @private
*/
_cmp: function (a, b) {
if (a === undefined) {
return b === undefined ? 0 : -1;
/**
* compare two values of unknown type according to BSON ordering
* semantics. (as an extension, consider 'undefined' to be less than
* any other value.) return negative if a is less, positive if b is
* less, or 0 if equal
*
* @param a
* @param b
* @returns {*}
* @private
*/
_cmp: function (a, b) {
if (a === undefined) {
return b === undefined ? 0 : -1;
}
if (b === undefined) {
return 1;
}
var i = 0;
var ta = this.detectType(a);
var tb = this.detectType(b);
var oa = this.typeOrder(ta);
var ob = this.typeOrder(tb);
if (oa !== ob) {
return oa < ob ? -1 : 1;
}
if (ta !== tb) {
throw new Error('Missing type coercion logic in _cmp');
}
if (ta === 7) { // ObjectID
// Convert to string.
ta = tb = 2;
a = a.toHexString();
b = b.toHexString();
}
if (ta === Bikini.DATA.TYPE.DATE) {
// Convert to millis.
ta = tb = 1;
a = a.getTime();
b = b.getTime();
}
if (ta === Bikini.DATA.TYPE.FLOAT) {
return a - b;
}
if (tb === Bikini.DATA.TYPE.STRING) {
return a < b ? -1 : (a === b ? 0 : 1);
}
if (ta === Bikini.DATA.TYPE.OBJECT) {
// this could be much more efficient in the expected case ...
var toArray = function (obj) {
var ret = [];
for (var key in obj) {
ret.push(key);
ret.push(obj[key]);
}
if (b === undefined) {
return 1;
return ret;
};
return this._cmp(toArray(a), toArray(b));
}
if (ta === Bikini.DATA.TYPE.ARRAY) { // Array
for (i = 0; ; i++) {
if (i === a.length) {
return (i === b.length) ? 0 : -1;
}
var i = 0;
var ta = this.detectType(a);
var tb = this.detectType(b);
var oa = this.typeOrder(ta);
var ob = this.typeOrder(tb);
if (oa !== ob) {
return oa < ob ? -1 : 1;
if (i === b.length) {
return 1;
}
if (ta !== tb) {
throw new Error('Missing type coercion logic in _cmp');
var s = this._cmp(a[i], b[i]);
if (s !== 0) {
return s;
}
if (ta === 7) { // ObjectID
// Convert to string.
ta = tb = 2;
a = a.toHexString();
b = b.toHexString();
}
}
if (ta === Bikini.DATA.TYPE.BINARY) {
if (a.length !== b.length) {
return a.length - b.length;
}
for (i = 0; i < a.length; i++) {
if (a[i] < b[i]) {
return -1;
}
if (ta === Bikini.DATA.TYPE.DATE) {
// Convert to millis.
ta = tb = 1;
a = a.getTime();
b = b.getTime();
if (a[i] > b[i]) {
return 1;
}
if (ta === Bikini.DATA.TYPE.FLOAT) {
return a - b;
}
if (tb === Bikini.DATA.TYPE.STRING) {
return a < b ? -1 : (a === b ? 0 : 1);
}
if (ta === Bikini.DATA.TYPE.OBJECT) {
// this could be much more efficient in the expected case ...
var toArray = function (obj) {
var ret = [];
for (var key in obj) {
ret.push(key);
ret.push(obj[key]);
}
return ret;
};
return this._cmp(toArray(a), toArray(b));
}
if (ta === Bikini.DATA.TYPE.ARRAY) { // Array
for (i = 0; ; i++) {
if (i === a.length) {
return (i === b.length) ? 0 : -1;
}
if (i === b.length) {
return 1;
}
var s = this._cmp(a[i], b[i]);
if (s !== 0) {
return s;
}
}
}
if (ta === Bikini.DATA.TYPE.BINARY) {
if (a.length !== b.length) {
return a.length - b.length;
}
for (i = 0; i < a.length; i++) {
if (a[i] < b[i]) {
return -1;
}
if (a[i] > b[i]) {
return 1;
}
}
return 0;
}
if (ta === Bikini.DATA.TYPE.BOOLEAN) {
if (a) {
return b ? 0 : 1;
}
return b ? -1 : 0;
}
if (ta === Bikini.DATA.TYPE.NULL) {
return 0;
}
}
return 0;
}
if (ta === Bikini.DATA.TYPE.BOOLEAN) {
if (a) {
return b ? 0 : 1;
}
return b ? -1 : 0;
}
if (ta === Bikini.DATA.TYPE.NULL) {
return 0;
}
// if( ta === Bikini.DATA.TYPE.REGEXP ) {

@@ -384,4 +384,4 @@ // throw Error("Sorting not supported on regular expression");

// } // XXX
throw new Error('Unknown type to sort');
}
throw new Error('Unknown type to sort');
}
});

@@ -12,11 +12,11 @@ // Copyright (c) 2013 M-Way Solutions GmbH

Bikini.Model = Backbone.Model.extend({
constructor: function( attributes, options ) {
if(this.url && typeof this.url === 'string') {
if(this.url.charAt(this.url.length - 1) !== '/') {
this.url += '/';
}
}
this.init(attributes, options);
Backbone.Model.apply(this, arguments);
constructor: function (attributes, options) {
if (this.url && typeof this.url === 'string') {
if (this.url.charAt(this.url.length - 1) !== '/') {
this.url += '/';
}
}
this.init(attributes, options);
Backbone.Model.apply(this, arguments);
}
});

@@ -29,95 +29,95 @@

_type: 'Bikini.Model',
_type: 'Bikini.Model',
isModel: YES,
isModel: YES,
entity: null,
entity: null,
defaults: {},
defaults: {},
changedSinceSync: {},
changedSinceSync: {},
logon: Bikini.Security.logon,
logon: Bikini.Security.logon,
init: function( attributes, options ) {
options = options || {};
init: function (attributes, options) {
options = options || {};
this.collection = options.collection || this.collection;
this.idAttribute = options.idAttribute || this.idAttribute;
this.store = this.store || (this.collection ? this.collection.store : null) || options.store;
if( this.store && _.isFunction(this.store.initModel) ) {
this.store.initModel(this, options);
}
this.entity = this.entity || (this.collection ? this.collection.entity : null) || options.entity;
if( this.entity ) {
this.entity = Bikini.Entity.from(this.entity, { model: this.constructor, typeMapping: options.typeMapping });
this.idAttribute = this.entity.idAttribute || this.idAttribute;
}
this.credentials = this.credentials || (this.collection ? this.collection.credentials : null) || options.credentials;
this.on('change', this.onChange, this);
this.on('sync', this.onSync, this);
},
this.collection = options.collection || this.collection;
this.idAttribute = options.idAttribute || this.idAttribute;
this.store = this.store || (this.collection ? this.collection.store : null) || options.store;
if (this.store && _.isFunction(this.store.initModel)) {
this.store.initModel(this, options);
}
this.entity = this.entity || (this.collection ? this.collection.entity : null) || options.entity;
if (this.entity) {
this.entity = Bikini.Entity.from(this.entity, {model: this.constructor, typeMapping: options.typeMapping});
this.idAttribute = this.entity.idAttribute || this.idAttribute;
}
this.credentials = this.credentials || (this.collection ? this.collection.credentials : null) || options.credentials;
this.on('change', this.onChange, this);
this.on('sync', this.onSync, this);
},
sync: function( method, model, options ) {
options = options || {};
options.credentials = options.credentials || this.credentials;
var store = (options.store ? options.store : null) || this.store;
var that = this;
var args = arguments;
sync: function (method, model, options) {
options = options || {};
options.credentials = options.credentials || this.credentials;
var store = (options.store ? options.store : null) || this.store;
var that = this;
var args = arguments;
return this.logon(options, function( result ) {
if( store && _.isFunction(store.sync) ) {
return store.sync.apply(that, args);
} else {
return Backbone.sync.apply(that, args);
}
});
},
return this.logon(options, function (result) {
if (store && _.isFunction(store.sync)) {
return store.sync.apply(that, args);
} else {
return Backbone.sync.apply(that, args);
}
});
},
onChange: function( model, options ) {
// For each `set` attribute, update or delete the current value.
var attrs = model.changedAttributes();
if( _.isObject(attrs) ) {
for( var key in attrs ) {
this.changedSinceSync[key] = attrs[key];
}
}
},
onChange: function (model, options) {
// For each `set` attribute, update or delete the current value.
var attrs = model.changedAttributes();
if (_.isObject(attrs)) {
for (var key in attrs) {
this.changedSinceSync[key] = attrs[key];
}
}
},
onSync: function( model, options ) {
this.changedSinceSync = {};
},
onSync: function (model, options) {
this.changedSinceSync = {};
},
getUrlRoot: function() {
if( this.urlRoot ) {
return _.isFunction(this.urlRoot) ? this.urlRoot() : this.urlRoot;
} else if( this.collection ) {
return this.collection.getUrlRoot();
} else if( this.url ) {
var url = _.isFunction(this.url) ? this.url() : this.url;
if( url && this.id && url.indexOf(this.id) > 0 ) {
return url.substr(0, url.indexOf(this.id));
}
return url;
}
},
getUrlRoot: function () {
if (this.urlRoot) {
return _.isFunction(this.urlRoot) ? this.urlRoot() : this.urlRoot;
} else if (this.collection) {
return this.collection.getUrlRoot();
} else if (this.url) {
var url = _.isFunction(this.url) ? this.url() : this.url;
if (url && this.id && url.indexOf(this.id) > 0) {
return url.substr(0, url.indexOf(this.id));
}
return url;
}
},
toJSON: function( options ) {
options = options || {};
var entity = options.entity || this.entity;
if( Bikini.isEntity(entity) ) {
return entity.fromAttributes(options.attrs || this.attributes);
}
return options.attrs || _.clone(this.attributes);
},
toJSON: function (options) {
options = options || {};
var entity = options.entity || this.entity;
if (Bikini.isEntity(entity)) {
return entity.fromAttributes(options.attrs || this.attributes);
}
return options.attrs || _.clone(this.attributes);
},
parse: function( resp, options ) {
options = options || {};
var entity = options.entity || this.entity;
if( Bikini.isEntity(entity) ) {
return entity.toAttributes(resp);
}
return resp;
parse: function (resp, options) {
options = options || {};
var entity = options.entity || this.entity;
if (Bikini.isEntity(entity)) {
return entity.toAttributes(resp);
}
return resp;
}
});

@@ -13,28 +13,28 @@ // Copyright (c) 2013 M-Way Solutions GmbH

logon: function (options, callback) {
var credentials = options ? options.credentials : null;
if (credentials) {
switch (credentials.type) {
case 'basic':
return this.logonBasicAuth(options, callback);
}
}
return this.handleCallback(callback);
},
logon: function (options, callback) {
var credentials = options ? options.credentials : null;
if (credentials) {
switch (credentials.type) {
case 'basic':
return this.logonBasicAuth(options, callback);
}
}
return this.handleCallback(callback);
},
logonBasicAuth: function (options, callback) {
var credentials = options.credentials;
options.beforeSend = function (xhr) {
Bikini.Security.setBasicAuth(xhr, credentials);
};
return this.handleCallback(callback);
},
logonBasicAuth: function (options, callback) {
var credentials = options.credentials;
options.beforeSend = function (xhr) {
Bikini.Security.setBasicAuth(xhr, credentials);
};
return this.handleCallback(callback);
},
setBasicAuth: function( xhr, credentials ) {
if( credentials && credentials.username && xhr && Bikini.Base64 ) {
var basicAuth = Bikini.Base64.encode(encodeURIComponent(credentials.username + ':' + (credentials.password || '')));
xhr.setRequestHeader('Authorization', 'Basic ' + basicAuth);
}
setBasicAuth: function (xhr, credentials) {
if (credentials && credentials.username && xhr && Bikini.Base64) {
var basicAuth = Bikini.Base64.encode(encodeURIComponent(credentials.username + ':' + (credentials.password || '')));
xhr.setRequestHeader('Authorization', 'Basic ' + basicAuth);
}
}
});

@@ -13,283 +13,283 @@ // Copyright (c) 2013 M-Way Solutions GmbH

_type: 'Bikini.SqlSelector',
_type: 'Bikini.SqlSelector',
_selector: null,
_query: null,
_entity: null,
_selector: null,
_query: null,
_entity: null,
create: function (docSelector, entity) {
var selector = this.extend({
_entity: entity,
_selector: null,
_query: null
});
selector.init(docSelector);
create: function (docSelector, entity) {
var selector = this.extend({
_entity: entity,
_selector: null,
_query: null
});
selector.init(docSelector);
return selector;
},
return selector;
},
init: function (docSelector) {
this._selector = this.compileSelector(docSelector);
this._query = this.buildSqlQuery(docSelector);
},
init: function (docSelector) {
this._selector = this.compileSelector(docSelector);
this._query = this.buildSqlQuery(docSelector);
},
buildStatement: function (obj) {
return this._query;
},
buildStatement: function (obj) {
return this._query;
},
buildSqlQuery: function (selector, connector) {
// you can pass a literal function instead of a selector
if (selector instanceof Function) {
return '';
}
buildSqlQuery: function (selector, connector) {
// you can pass a literal function instead of a selector
if (selector instanceof Function) {
return '';
}
// shorthand -- sql
if (_.isString(selector)) {
return selector;
}
// shorthand -- sql
if (_.isString(selector)) {
return selector;
}
// protect against dangerous selectors. falsey and {_id: falsey} are both
// likely programmer error, and not what you want, particularly for
// destructive operations.
if (!selector || (('_id' in selector) && !selector._id)) {
return '1=2';
// protect against dangerous selectors. falsey and {_id: falsey} are both
// likely programmer error, and not what you want, particularly for
// destructive operations.
if (!selector || (('_id' in selector) && !selector._id)) {
return '1=2';
}
// Top level can't be an array or true or binary.
if (_.isBoolean(selector) || _.isArray(selector) || Bikini.DataField.isBinary(selector)) {
throw new Error('Invalid selector: ' + selector);
}
return this.buildSqlWhere(selector)();
},
// The main compilation function for a given selector.
buildSqlWhere: function (docSelector) {
var where = '';
var that = this;
var perKeySelectors = [];
_.each(docSelector, function (subSelector, key) {
if (key.substr(0, 1) === '$') {
// Outer operators are either logical operators (they recurse back into
// this function), or $where.
perKeySelectors.push(that.buildLogicalOperator(key, subSelector));
} else {
var valueLookup = that.buildLookup(key);
var valueSelector = that.buildValueSelector(subSelector);
if (_.isFunction(valueSelector)) {
perKeySelectors.push(function () {
return valueSelector(valueLookup);
});
}
}
});
// Top level can't be an array or true or binary.
if (_.isBoolean(selector) || _.isArray(selector) || Bikini.DataField.isBinary(selector)) {
throw new Error('Invalid selector: ' + selector);
return function () {
var sql = '';
_.each(perKeySelectors, function (f) {
if (_.isFunction(f)) {
sql += f.call(that);
}
});
return sql;
};
},
return this.buildSqlWhere(selector)();
},
buildValueSelector: function (valueSelector) {
var that = this;
if (valueSelector === null) { // undefined or null
return function (key) {
return key + ' IS NULL';
};
}
// The main compilation function for a given selector.
buildSqlWhere: function (docSelector) {
var where = '';
var that = this;
var perKeySelectors = [];
_.each(docSelector, function (subSelector, key) {
if (key.substr(0, 1) === '$') {
// Outer operators are either logical operators (they recurse back into
// this function), or $where.
perKeySelectors.push(that.buildLogicalOperator(key, subSelector));
} else {
var valueLookup = that.buildLookup(key);
var valueSelector = that.buildValueSelector(subSelector);
if (_.isFunction(valueSelector)) {
perKeySelectors.push(function () {
return valueSelector(valueLookup);
});
}
}
});
// Selector is a non-null primitive (and not an array or RegExp either).
if (!_.isObject(valueSelector)) {
return function (key) {
return key + ' = ' + that.buildValue(valueSelector);
};
}
return function () {
var sql = '';
_.each(perKeySelectors, function (f) {
if (_.isFunction(f)) {
sql += f.call(that);
}
});
return sql;
if (_.isRegExp(valueSelector)) {
var regEx = valueSelector.toString();
var match = regEx.match(/\/[\^]?([^^.*$'+()]*)[\$]?\//);
if (match && match.length > 1) {
var prefix = regEx.indexOf('/^') < 0 ? '%' : '';
var suffix = regEx.indexOf('$/') < 0 ? '%' : '';
return function (key) {
return key + ' LIKE "' + prefix + match[1] + suffix + '"';
};
},
}
return null;
}
buildValueSelector: function (valueSelector) {
var that = this;
if (valueSelector === null) { // undefined or null
return function (key) {
return key + ' IS NULL';
};
}
// Arrays match either identical arrays or arrays that contain it as a value.
if (_.isArray(valueSelector)) {
return null;
}
// Selector is a non-null primitive (and not an array or RegExp either).
if (!_.isObject(valueSelector)) {
return function (key) {
return key + ' = ' + that.buildValue(valueSelector);
};
// It's an object, but not an array or regexp.
if (this.hasOperators(valueSelector)) {
var operatorFunctions = [];
_.each(valueSelector, function (operand, operator) {
if (!_.has(that.VALUE_OPERATORS, operator)) {
throw new Error('Unrecognized operator: ' + operator);
}
operatorFunctions.push(that.VALUE_OPERATORS[operator](operand, that));
});
return function (key) {
return that.LOGICAL_OPERATORS.$and(operatorFunctions, key);
};
}
if (_.isRegExp(valueSelector)) {
var regEx = valueSelector.toString();
var match = regEx.match(/\/[\^]?([^^.*$'+()]*)[\$]?\//);
if (match && match.length > 1) {
var prefix = regEx.indexOf('/^') < 0 ? '%' : '';
var suffix = regEx.indexOf('$/') < 0 ? '%' : '';
return function (key) {
return key + ' LIKE "' + prefix + match[1] + suffix + '"';
};
}
return null;
}
// It's a literal; compare value (or element of value array) directly to the
// selector.
return function (key) {
return key + ' = ' + that.buildValue(valueSelector);
};
},
// Arrays match either identical arrays or arrays that contain it as a value.
if (_.isArray(valueSelector)) {
return null;
}
buildLookup: function (key) {
var field = this._entity ? this._entity.getField(key) : null;
key = field && field.name ? field.name : key;
return '"' + key + '"';
},
// It's an object, but not an array or regexp.
if (this.hasOperators(valueSelector)) {
var operatorFunctions = [];
_.each(valueSelector, function (operand, operator) {
if (!_.has(that.VALUE_OPERATORS, operator)) {
throw new Error('Unrecognized operator: ' + operator);
}
operatorFunctions.push(that.VALUE_OPERATORS[operator](operand, that));
});
return function (key) {
return that.LOGICAL_OPERATORS.$and(operatorFunctions, key);
};
buildValue: function (value) {
if (_.isString(value)) {
return '"' + value.replace(/"/g, '""') + '"';
}
return value;
},
buildLogicalOperator: function (operator, subSelector) {
if (!_.has(this.LOGICAL_OPERATORS, operator)) {
throw new Error('Unrecognized logical operator: ' + operator);
} else {
if (!_.isArray(subSelector) || _.isEmpty(subSelector)) {
throw new Error('$and/$or/$nor must be nonempty array');
}
var subSelectorFunction = _.map(subSelector, this.buildSqlWhere, this);
var that = this;
return function (key) {
return that.LOGICAL_OPERATORS[operator](subSelectorFunction, key);
};
}
},
LOGICAL_OPERATORS: {
'$and': function (subSelectorFunction, key) {
var sql = '';
var count = 0;
_.each(subSelectorFunction, function (f) {
var s = f !== null ? f(key) : '';
if (s) {
count++;
sql += sql ? ' AND ' + s : s;
}
});
return count > 1 ? '( ' + sql + ' )' : sql;
},
'$or': function (subSelectorFunction, key) {
var sql = '';
var miss = false;
_.each(subSelectorFunction, function (f) {
var s = f !== null ? f(key) : '';
miss |= !s;
sql += sql && s ? ' OR ' + s : s;
});
return miss ? '' : '( ' + sql + ' )';
},
'$nor': function (subSelectorFunction, key) {
var sql = '';
var miss = false;
_.each(subSelectorFunction, function (f) {
var s = f !== null ? f(key) : '';
miss |= !s;
sql += sql && s ? ' OR ' + s : s;
});
return miss ? '' : 'NOT ( ' + sql + ' )';
}
},
// It's a literal; compare value (or element of value array) directly to the
// selector.
return function (key) {
return key + ' = ' + that.buildValue(valueSelector);
};
VALUE_OPERATORS: {
'$in': function (operand) {
return null;
},
buildLookup: function (key) {
var field = this._entity ? this._entity.getField(key) : null;
key = field && field.name ? field.name : key;
return '"' + key + '"';
'$all': function (operand) {
return null;
},
buildValue: function (value) {
if (_.isString(value)) {
return '"' + value.replace(/"/g, '""') + '"';
}
return value;
'$lt': function (operand, that) {
return function (key) {
return key + ' < ' + that.buildValue(operand);
};
},
buildLogicalOperator: function (operator, subSelector) {
if (!_.has(this.LOGICAL_OPERATORS, operator)) {
throw new Error('Unrecognized logical operator: ' + operator);
} else {
if (!_.isArray(subSelector) || _.isEmpty(subSelector)) {
throw new Error('$and/$or/$nor must be nonempty array');
}
var subSelectorFunction = _.map(subSelector, this.buildSqlWhere, this);
var that = this;
return function (key) {
return that.LOGICAL_OPERATORS[operator](subSelectorFunction, key);
};
}
'$lte': function (operand, that) {
return function (key) {
return key + ' <= ' + that.buildValue(operand);
};
},
LOGICAL_OPERATORS: {
'$and': function (subSelectorFunction, key) {
var sql = '';
var count = 0;
_.each(subSelectorFunction, function (f) {
var s = f !== null ? f(key) : '';
if (s) {
count++;
sql += sql ? ' AND ' + s : s;
}
});
return count > 1 ? '( ' + sql + ' )' : sql;
},
'$or': function (subSelectorFunction, key) {
var sql = '';
var miss = false;
_.each(subSelectorFunction, function (f) {
var s = f !== null ? f(key) : '';
miss |= !s;
sql += sql && s ? ' OR ' + s : s;
});
return miss ? '' : '( ' + sql + ' )';
},
'$nor': function (subSelectorFunction, key) {
var sql = '';
var miss = false;
_.each(subSelectorFunction, function (f) {
var s = f !== null ? f(key) : '';
miss |= !s;
sql += sql && s ? ' OR ' + s : s;
});
return miss ? '' : 'NOT ( ' + sql + ' )';
}
'$gt': function (operand, that) {
return function (key) {
return key + ' > ' + that.buildValue(operand);
};
},
VALUE_OPERATORS: {
'$gte': function (operand, that) {
return function (key) {
return key + '' > '' + that.buildValue(operand);
};
},
'$in': function (operand) {
return null;
},
'$ne': function (operand, that) {
return function (key) {
return key + ' <> ' + that.buildValue(operand);
};
},
'$all': function (operand) {
return null;
},
'$nin': function (operand) {
return null;
},
'$lt': function (operand, that) {
return function (key) {
return key + ' < ' + that.buildValue(operand);
};
},
'$exists': function (operand, that) {
return function (key) {
return key + ' IS NOT NULL';
};
},
'$lte': function (operand, that) {
return function (key) {
return key + ' <= ' + that.buildValue(operand);
};
},
'$mod': function (operand) {
return null;
},
'$gt': function (operand, that) {
return function (key) {
return key + ' > ' + that.buildValue(operand);
};
},
'$size': function (operand) {
return null;
},
'$gte': function (operand, that) {
return function (key) {
return key + '' > '' + that.buildValue(operand);
};
},
'$type': function (operand) {
return null;
},
'$ne': function (operand, that) {
return function (key) {
return key + ' <> ' + that.buildValue(operand);
};
},
'$regex': function (operand, options) {
return null;
},
'$options': function (operand) {
return null;
},
'$nin': function (operand) {
return null;
},
'$elemMatch': function (operand) {
return null;
},
'$exists': function (operand, that) {
return function (key) {
return key + ' IS NOT NULL';
};
},
'$mod': function (operand) {
return null;
},
'$size': function (operand) {
return null;
},
'$type': function (operand) {
return null;
},
'$regex': function (operand, options) {
return null;
},
'$options': function (operand) {
return null;
},
'$elemMatch': function (operand) {
return null;
},
'$not': function (operand, that) {
var matcher = that.buildSqlWhere(operand);
return function (key) {
return 'NOT (' + matcher(key) + ')';
};
}
'$not': function (operand, that) {
var matcher = that.buildSqlWhere(operand);
return function (key) {
return 'NOT (' + matcher(key) + ')';
};
}
});
}
});

@@ -25,5 +25,5 @@ // Copyright (c) 2013 M-Way Solutions GmbH

* store: new Bikini.BikiniStore( {
* useLocalStore: YES, // (default) store the data for offline use
* useSocketNotify: YES, // (default) register at the server for live updates
* useOfflineChanges: YES // (default) allow changes to the offline data
* useLocalStore: true, // (default) store the data for offline use
* useSocketNotify: true, // (default) register at the server for live updates
* useOfflineChanges: true // (default) allow changes to the offline data
* })

@@ -33,569 +33,608 @@ * });

*/
Bikini.BikiniStore = Bikini.Store.extend({
_type: 'Bikini.BikiniStore',
_type: 'Bikini.BikiniStore',
_selector: null,
_selector: null,
endpoints: {},
endpoints: {},
options: null,
options: null,
localStore: Bikini.WebSqlStore,
localStore: Bikini.WebSqlStore,
useLocalStore: true,
useSocketNotify: true,
useOfflineChanges: true,
isConnected: false,
typeMapping: {
'binary': 'text',
'date': 'string'
},
useLocalStore: YES,
initialize: function (options) {
//debugger;
console.log('Bikini.BikiniStore.initialize');
Bikini.Store.prototype.initialize.apply(this, arguments);
this.options = this.options || {};
useSocketNotify: YES,
this.options.useLocalStore = this.useLocalStore;
this.options.useSocketNotify = this.useSocketNotify;
this.options.useOfflineChanges = this.useOfflineChanges;
this.options.query = options.query || false;
this.options.socketPath = this.socketPath;
this.options.localStore = this.localStore;
this.options.typeMapping = this.typeMapping;
if (this.options.useSocketNotify && typeof io !== 'object') {
console.log('Socket.IO not present !!');
this.options.useSocketNotify = false;
}
_.extend(this.options, options || {});
},
useOfflineChanges: YES,
//initModel: function (model) {
// console.log('Bikini.BikiniStore.initModel');
//},
isConnected: NO,
initCollection: function (collection) {
console.log('Bikini.BikiniStore.initCollection');
var url = collection.getUrlRoot();
if (url.charAt(url.length - 1) !== '/') {
url += '/';
}
var entity = this.getEntity(collection.entity);
if (url && entity) {
var name = entity.name;
var hash = this._locationBasedHashCode(url);
var credentials = entity.credentials || collection.credentials;
var user = credentials && credentials.username ? credentials.username : '';
var channel = name + user + hash;
collection.channel = channel;
// get or create endpoint for this url
var that = this;
var endpoint = this.endpoints[hash];
if (!endpoint) {
var href = this.getLocation(url);
endpoint = {};
endpoint.baseUrl = url;
endpoint.readUrl = collection.getUrl();
endpoint.host = href.protocol + '//' + href.host;
endpoint.path = href.pathname;
endpoint.entity = entity;
endpoint.channel = channel;
endpoint.credentials = credentials;
endpoint.socketPath = this.options.socketPath;
endpoint.localStore = this.createLocalStore(endpoint);
endpoint.messages = this.createMsgCollection(endpoint);
endpoint.socket = this.createSocket(endpoint);
endpoint.info = this.fetchServerInfo(endpoint);
that.endpoints[hash] = endpoint;
}
collection.endpoint = endpoint;
collection.listenTo(this, endpoint.channel, this.onMessage, collection);
}
},
typeMapping: {
'binary': 'text',
'date': 'string'
},
getEndpoint: function (url) {
console.log('Bikini.BikiniStore.getEndpoint');
if (url) {
var hash = this._locationBasedHashCode(url);
return this.endpoints[hash];
}
},
initialize: function( options ) {
Bikini.Store.prototype.initialize.apply(this, arguments);
this.options = this.options || {};
this.options.useLocalStore = this.useLocalStore;
this.options.useSocketNotify = this.useSocketNotify;
this.options.useOfflineChanges = this.useOfflineChanges;
this.options.socketPath = this.socketPath;
this.options.localStore = this.localStore;
this.options.typeMapping = this.typeMapping;
if( this.options.useSocketNotify && typeof io !== 'object' ) {
console.log('Socket.IO not present !!');
this.options.useSocketNotify = NO;
createLocalStore: function (endpoint, idAttribute) {
console.log('Bikini.BikiniStore.createLocalStore');
if (this.options.useLocalStore && endpoint) {
var entities = {};
entities[endpoint.entity.name] = {
name: endpoint.channel,
idAttribute: idAttribute
};
return this.options.localStore.create({
entities: entities
});
}
},
/**
* @description Here we save the changes in a Message local websql
* @param endpoint {string}
* @returns {*}
*/
createMsgCollection: function( endpoint ) {
if( this.options.useOfflineChanges && endpoint ) {
var entity = 'msg-' + endpoint.channel;
var entities = {};
entities[entity] = {
name: entity,
idAttribute: 'id'
};
var messages = Bikini.Collection.design({
url: endpoint.url,
entity: entity,
store: this.options.localStore.create({
entities: entities
})
});
var that = this;
messages.fetch({
success: function() {
that.sendMessages(endpoint);
}
_.extend(this.options, options || {});
},
});
return messages;
}
},
initModel: function( model ) {
},
initCollection: function( collection ) {
var url = collection.getUrlRoot();
if(url.charAt(url.length - 1) !== '/') {
url += '/';
createSocket: function (endpoint, name) {
console.log('Bikini.BikiniStore.createSocket');
//debugger;
if (this.options.useSocketNotify && endpoint && endpoint.socketPath) {
var that = this;
var url = endpoint.host;
var path = endpoint.path;
var href = this.getLocation(url);
if (href.port === '') {
if (href.protocol === 'https:') {
url += ':443';
} else if (href.protocol === 'http:') {
url += ':80';
}
var entity = this.getEntity(collection.entity);
if( url && entity ) {
var name = entity.name;
var hash = this._locationBasedHashCode(url);
var credentials = entity.credentials || collection.credentials;
var user = credentials && credentials.username ? credentials.username : '';
var channel = name + user + hash;
collection.channel = channel;
// get or create endpoint for this url
var that = this;
var endpoint = this.endpoints[hash];
if( !endpoint ) {
var href = this.getLocation(url);
endpoint = {};
endpoint.baseUrl = url;
endpoint.readUrl = collection.getUrl();
endpoint.host = href.protocol + '//' + href.host;
endpoint.path = href.pathname;
endpoint.entity = entity;
endpoint.channel = channel;
endpoint.credentials = credentials;
endpoint.socketPath = this.options.socketPath;
endpoint.localStore = this.createLocalStore(endpoint);
endpoint.messages = this.createMsgCollection(endpoint);
endpoint.socket = this.createSocket(endpoint);
endpoint.info = this.fetchServerInfo(endpoint);
that.endpoints[hash] = endpoint;
}
collection.endpoint = endpoint;
collection.listenTo(this, endpoint.channel, this.onMessage, collection);
}
},
}
getEndpoint: function( url ) {
if( url ) {
var hash = this._locationBasedHashCode(url);
return this.endpoints[hash];
}
},
//path = endpoint.socketPath || (path + (path.charAt(path.length - 1) === '/' ? '' : '/' ) + 'live');
path = endpoint.socketPath;
// remove leading /
var resource = (path && path.indexOf('/') === 0) ? path.substr(1) : path;
var connectVo = {
resource: resource
};
createLocalStore: function( endpoint, idAttribute ) {
if( this.options.useLocalStore && endpoint ) {
var entities = {};
entities[endpoint.entity.name] = {
name: endpoint.channel,
idAttribute: idAttribute
};
return this.options.localStore.create({
entities: entities
});
}
},
if (this.options.socketQuery) {
connectVo.query = this.options.socketQuery;
}
endpoint.socket = io.connect(url, connectVo);
endpoint.socket.on('connect', function () {
that._bindChannel(endpoint, name);
that.onConnect(endpoint);
});
endpoint.socket.on('disconnect', function () {
console.log('socket.io: disconnect');
that.onDisconnect(endpoint);
});
return endpoint.socket;
}
},
createMsgCollection: function( endpoint ) {
if( this.options.useOfflineChanges && endpoint ) {
var messages = Bikini.Collection.design({
url: endpoint.url,
entity: 'msg-' + endpoint.channel,
store: this.options.localStore.create()
});
var that = this;
messages.fetch({
success: function() {
that.sendMessages(endpoint);
}
});
return messages;
_bindChannel: function (endpoint, name) {
console.log('Bikini.BikiniStore._bindChannel');
var that = this;
if (endpoint && endpoint.socket) {
var channel = endpoint.channel;
var socket = endpoint.socket;
var time = this.getLastMessageTime(channel);
name = name || endpoint.entity.name;
socket.on(channel, function (msg) {
if (msg) {
that.trigger(channel, msg);
if (that.options.useLocalStore) {
that.setLastMessageTime(channel, msg.time);
}
}
},
});
socket.emit('bind', {
entity: name,
channel: channel,
time: time
});
}
},
getLastMessageTime: function (channel) {
if(this.lastMesgTime !== undefined) {
return this.lastMesgTime;
}
console.log('Bikini.BikiniStore.getLastMessageTime');
this.lastMesgTime = localStorage.getItem('__' + channel + 'lastMesgTime') || 0;
return this.lastMesgTime;
},
setLastMessageTime: function (channel, time) {
if (time && time > this.getLastMessageTime()) {
console.log('Bikini.BikiniStore.setLastMessageTime');
localStorage.setItem('__' + channel + 'lastMesgTime', time);
this.lastMesgTime = time;
}
},
_hashCode: function (str) {
console.log('Bikini.BikiniStore._hashCode');
var hash = 0, char;
if (str.length === 0) {
return hash;
}
for (var i = 0, l = str.length; i < l; i++) {
char = str.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
hash |= 0; // Convert to 32bit integer
}
return hash;
},
createSocket: function( endpoint, name ) {
if( this.options.useSocketNotify && endpoint && endpoint.socketPath) {
var that = this;
var url = endpoint.host;
var path = endpoint.path;
var href = this.getLocation(url);
if(href.port === '') {
if(href.protocol === 'https:') {
url += ':443';
} else if(href.protocol === 'http:') {
url += ':80';
}
}
_locationBasedHashCode: function (str) {
console.log('Bikini.BikiniStore._locationBasedHashCode');
return this._hashCode(this._getLocationUrl(str));
},
//path = endpoint.socketPath || (path + (path.charAt(path.length - 1) === '/' ? '' : '/' ) + 'live');
path = endpoint.socketPath;
// remove leading /
var resource = (path && path.indexOf('/') === 0) ? path.substr(1) : path;
_getLocationUrl: function (str) {
console.log('Bikini.BikiniStore._getLocationUrl');
return this.getLocation(str).toString();
},
endpoint.socket = io.connect(url, { resource: resource });
endpoint.socket.on('connect', function() {
that._bindChannel(endpoint, name);
that.onConnect(endpoint);
});
endpoint.socket.on('disconnect', function() {
console.log('socket.io: disconnect');
that.onDisconnect(endpoint);
});
return endpoint.socket;
}
},
_getLocation: function (url) {
console.log('Bikini.BikiniStore._getLocation');
var location = document.createElement('a');
location.href = url || this.url;
// IE doesn't populate all link properties when setting .href with a relative URL,
// however .href will return an absolute URL which then can be used on itself
// to populate these additional fields.
if (location.host === '') {
location.href = location.href;
}
return location;
},
_bindChannel: function(endpoint, name ) {
var that = this;
if (endpoint && endpoint.socket) {
var channel = endpoint.channel;
var socket = endpoint.socket;
var time = this.getLastMessageTime(channel);
name = name || endpoint.entity.name;
socket.on(channel, function( msg ) {
if( msg ) {
that.trigger(channel, msg);
if (that.options.useLocalStore) {
that.setLastMessageTime(channel, msg.time);
}
}
});
socket.emit('bind', {
entity: name,
channel: channel,
time: time
});
}
},
onConnect: function (endpoint) {
console.log('Bikini.BikiniStore.onConnect');
this.isConnected = true;
this.fetchChanges(endpoint);
this.sendMessages(endpoint);
},
getLastMessageTime: function( channel ) {
return localStorage.getItem('__' + channel + 'last_msg_time') || 0;
},
onDisconnect: function (endpoint) {
console.log('Bikini.BikiniStore.onDisconnect');
this.isConnected = false;
if (endpoint.socket && endpoint.socket.socket) {
endpoint.socket.socket.onDisconnect();
}
},
setLastMessageTime: function( channel, time ) {
if( time ) {
localStorage.setItem('__' + channel + 'last_msg_time', time);
}
},
onMessage: function (msg) {
console.log('Bikini.BikiniStore.onMessage');
if (!msg) {
return;
}
var localStore = this.endpoint ? this.endpoint.localStore : null;
var attrs = null;
var method = null;
var id = null;
var options = {
store: localStore,
entity: this.entity,
merge: true,
fromMessage: true,
parse: true
};
if (msg.id && msg.method) {
attrs = msg.data || {};
method = msg.method;
id = msg.id;
} else if (msg.attributes) {
attrs = msg.attributes.data;
method = msg.attributes.method;
id = msg.attributes.id;
}
_hashCode: function( str ) {
var hash = 0, char;
if( str.length === 0 ) {
return hash;
switch (method) {
case 'patch':
case 'update':
case 'create':
options.patch = method === 'patch';
var model = id ? this.get(id) : null;
if (model) {
model.save(attrs, options);
} else {
this.create(attrs, options);
}
for( var i = 0, l = str.length; i < l; i++ ) {
char = str.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
hash |= 0; // Convert to 32bit integer
break;
case 'delete':
if (id) {
if (id === 'all') {
while ((model = this.first())) {
if (localStore) {
localStore.sync.apply(this, [
'delete',
model,
{store: localStore, fromMessage: true}
]);
}
this.remove(model);
}
this.store.setLastMessageTime(this.endpoint.channel, '');
} else {
var msgModel = this.get(id);
if (msgModel) {
msgModel.destroy(options);
}
}
}
return hash;
},
break;
_locationBasedHashCode: function( str ){
return this._hashCode( this._getLocationUrl(str) );
},
default:
break;
}
_getLocationUrl: function(str){
return this.getLocation(str).toString();
},
},
_getLocation: function( url ) {
var location = document.createElement('a');
location.href = url || this.url;
// IE doesn't populate all link properties when setting .href with a relative URL,
// however .href will return an absolute URL which then can be used on itself
// to populate these additional fields.
if( location.host === '' ) {
location.href = location.href;
}
return location;
},
sync: function (method, model, options) {
//debugger;
console.log('Bikini.BikiniStore.sync');
var that = options.store || this.store;
if (options.fromMessage) {
return that.handleCallback(options.success);
}
var endpoint = that.getEndpoint(this.getUrlRoot());
var promise = null;
if (that && endpoint) {
var channel = this.channel;
onConnect: function( endpoint ) {
this.isConnected = YES;
this.fetchChanges(endpoint );
this.sendMessages(endpoint );
},
if (Bikini.isModel(model) && !model.id) {
model.set(model.idAttribute, new Bikini.ObjectID().toHexString());
}
onDisconnect: function(endpoint) {
this.isConnected = NO;
if (endpoint.socket && endpoint.socket.socket) {
endpoint.socket.socket.onDisconnect();
}
},
var time = that.getLastMessageTime(channel);
// only send read messages if no other store can do this
// or for initial load
if (method !== 'read' || !endpoint.localStore || !time) {
// do backbone rest
promise = that.addMessage(method, model, // we don't need to call callbacks if an other store handle this
endpoint.localStore ? {} : options, endpoint);
} else if (method === 'read') {
promise = that.fetchChanges(endpoint);
}
if (endpoint.localStore) {
options.store = endpoint.localStore;
endpoint.localStore.sync.apply(this, arguments);
}
return promise;
}
},
onMessage: function( msg ) {
if(!msg) {
addMessage: function (method, model, options, endpoint) {
var that = this;
if (method && model) {
var changes = model.changedSinceSync;
var data = null;
var storeMsg = true;
switch (method) {
case 'update':
case 'create':
data = options.attrs || model.toJSON();
break;
case 'patch':
if (_.isEmpty(changes)) {
return;
}
var localStore = this.endpoint ? this.endpoint.localStore : null;
var attrs = null;
var method = null;
var id = null;
var options = {
store: localStore,
entity: this.entity,
merge: YES,
fromMessage: YES,
parse: YES
};
if(msg.id && msg.method){
attrs = msg.data || {};
method = msg.method;
id = msg.id;
} else if(msg.attributes) {
attrs = msg.attributes.data;
method = msg.attributes.method;
id = msg.attributes.id;
}
}
data = model.toJSON({attrs: changes});
break;
switch(method) {
case 'patch':
case 'update':
case 'create':
options.patch = method === 'patch';
var model = id ? this.get(id) : null;
if( model ) {
model.save(attrs, options);
} else {
this.create(attrs, options);
}
break;
case 'delete':
if( id ) {
if( id === 'all' ) {
while( (model = this.first()) ) {
if( localStore ) {
localStore.sync.apply(this, [
'delete',
model,
{ store: localStore, fromMessage: YES }
]);
}
this.remove(model);
}
this.store.setLastMessageTime(this.endpoint.channel, '');
} else {
var msgModel = this.get(id);
if( msgModel ) {
msgModel.destroy(options);
}
}
}
break;
case 'delete':
break;
default:
break;
}
default:
storeMsg = false;
break;
}
var msg = {
_id: model.id,
id: model.id,
method: method,
data: data
};
var emit = function (endpoint, msg) {
return that.emitMessage(endpoint, msg, options, model);
};
if (storeMsg) {
return this.storeMessage(endpoint, msg, emit);
} else {
return emit(endpoint, msg);
}
}
},
},
sync: function( method, model, options ) {
var that = options.store || this.store;
if( options.fromMessage ) {
return that.handleCallback(options.success);
emitMessage: function (endpoint, msg, options, model) {
var channel = endpoint.channel;
var that = this;
var url = Bikini.isModel(model) || msg.method !== 'read' ? endpoint.baseUrl : endpoint.readUrl;
if (msg.id && msg.method !== 'create') {
url += (url.charAt(url.length - 1) === '/' ? '' : '/' ) + msg.id;
}
return model.sync.apply(model, [msg.method, model, {
url: url,
error: function (xhr, status) {
if (!xhr.responseText && that.options.useOfflineChanges) {
// this seams to be only a connection problem, so we keep the message an call success
that.onDisconnect(endpoint);
that.handleCallback(options.success, msg.data);
} else {
that.removeMessage(endpoint, msg, function (endpoint, msg) {
// Todo: revert changed data
that.handleCallback(options.error, status);
});
}
var endpoint = that.getEndpoint(this.getUrlRoot());
var promise = null;
if( that && endpoint ) {
var channel = this.channel;
if( Bikini.isModel(model) && !model.id ) {
model.set(model.idAttribute, new Bikini.ObjectID().toHexString());
},
success: function (data) {
if (!that.isConnected) {
that.onConnect(endpoint);
}
that.removeMessage(endpoint, msg, function (endpoint, msg) {
if (options.success) {
var resp = data;
that.handleCallback(options.success, resp);
} else {
// that.setLastMessageTime(channel, msg.time);
if (msg.method === 'read') {
var array = _.isArray(data) ? data : [data];
for (var i = 0; i < array.length; i++) {
data = array[i];
if (data) {
that.trigger(channel, {
id: data[endpoint.entity.idAttribute] || data._id,
method: 'update',
data: data
});
}
}
} else {
that.trigger(channel, msg);
}
}
});
},
store: {}
}]);
},
var time = that.getLastMessageTime(channel);
// only send read messages if no other store can do this
// or for initial load
if( method !== 'read' || !endpoint.localStore || !time ) {
// do backbone rest
promise = that.addMessage(method, model, // we don't need to call callbacks if an other store handle this
endpoint.localStore ? {} : options, endpoint);
} else if( method === 'read' ) {
promise =that.fetchChanges(endpoint);
fetchChanges: function (endpoint) {
var that = this;
var channel = endpoint ? endpoint.channel : '';
var time = that.getLastMessageTime(channel);
if (endpoint && endpoint.baseUrl && channel && time) {
var changes = new Bikini.Collection({});
return changes.fetch({
url: endpoint.baseUrl + 'changes/' + time,
success: function (a, b, response) {
changes.each(function (msg) {
if (msg.get('time') && msg.get('method')) {
if (that.options.useLocalStore) {
that.setLastMessageTime(channel, msg.get('time'));
}
that.trigger(channel, msg);
}
if( endpoint.localStore ) {
options.store = endpoint.localStore;
endpoint.localStore.sync.apply(this, arguments);
}
return promise;
}
},
});
return response.xhr;
},
credentials: endpoint.credentials
});
}
},
addMessage: function( method, model, options, endpoint ) {
var that = this;
if( method && model ) {
var changes = model.changedSinceSync;
var data = null;
var storeMsg = YES;
switch( method ) {
case 'update':
case 'create':
data = options.attrs || model.toJSON();
break;
case 'patch':
if( _.isEmpty(changes) ) {
return;
}
data = model.toJSON({ attrs: changes });
break;
case 'delete':
break;
default:
storeMsg = NO;
break;
fetchServerInfo: function (endpoint) {
var that = this;
if (endpoint && endpoint.baseUrl) {
var info = new Bikini.Model();
var time = that.getLastMessageTime(endpoint.channel);
var url = endpoint.baseUrl;
if (url.charAt((url.length - 1)) !== '/') {
url += '/';
}
return info.fetch({
url: url + 'info',
success: function (a, b, response) {
//@todo why we set a server time here ?
if (!time && info.get('time')) {
that.setLastMessageTime(endpoint.channel, info.get('time'));
}
if (!endpoint.socketPath && info.get('socketPath')) {
endpoint.socketPath = info.get('socketPath');
var name = info.get('entity') || endpoint.entity.name;
if (that.options.useSocketNotify) {
endpoint.socket = that.createSocket(endpoint, name);
}
var msg = {
_id: model.id,
id: model.id,
method: method,
data: data
};
var emit = function( endpoint, msg ) {
return that.emitMessage(endpoint, msg, options, model);
};
if( storeMsg ) {
return this.storeMessage(endpoint, msg, emit);
} else {
return emit(endpoint, msg);
}
}
},
}
return response.xhr;
},
credentials: endpoint.credentials
});
}
},
emitMessage: function( endpoint, msg, options, model ) {
var channel = endpoint.channel;
var that = this;
var url = Bikini.isModel(model) || msg.method !== 'read' ? endpoint.baseUrl : endpoint.readUrl;
if( msg.id && msg.method !== 'create' ) {
url += (url.charAt(url.length - 1) === '/' ? '' : '/' ) + msg.id;
sendMessages: function (endpoint) {
if (endpoint && endpoint.messages) {
var that = this;
endpoint.messages.each(function (message) {
var msg;
try {
msg = JSON.parse(message.get('msg'));
} catch (e) {
}
return model.sync.apply(model, [msg.method, model, {
url: url,
error: function( xhr, status ) {
if( !xhr.responseText && that.options.useOfflineChanges ) {
// this seams to be only a connection problem, so we keep the message an call success
that.onDisconnect(endpoint);
that.handleCallback(options.success, msg.data);
} else {
that.removeMessage(endpoint, msg, function( endpoint, msg ) {
// Todo: revert changed data
that.handleCallback(options.error, status);
});
}
},
success: function( data ) {
if (!that.isConnected) {
that.onConnect(endpoint);
}
that.removeMessage(endpoint, msg, function( endpoint, msg ) {
if( options.success ) {
var resp = data;
that.handleCallback(options.success, resp);
} else {
// that.setLastMessageTime(channel, msg.time);
if( msg.method === 'read' ) {
var array = _.isArray(data) ? data : [ data ];
for( var i = 0; i < array.length; i++ ) {
data = array[i];
if( data ) {
that.trigger(channel, {
id: data._id,
method: 'update',
data: data
});
}
}
} else {
that.trigger(channel, msg);
}
}
});
},
store: {}
}]);
},
fetchChanges: function( endpoint ) {
var that = this;
var channel = endpoint ? endpoint.channel : '';
var time = that.getLastMessageTime(channel);
if( endpoint && endpoint.baseUrl && channel && time ) {
var changes = new Bikini.Collection({});
return changes.fetch({
url: endpoint.baseUrl + 'changes/' + time,
success: function(a, b, response) {
changes.each(function( msg ) {
if( msg.get('time') && msg.get('method') ) {
if (that.options.useLocalStore) {
that.setLastMessageTime(channel, msg.get('time'));
}
that.trigger(channel, msg);
}
});
return response.xhr;
},
credentials: endpoint.credentials
});
var channel = message.get('channel');
if (msg && channel) {
var model = that.createModel({collection: endpoint.messages}, msg.data);
that.emitMessage(endpoint, msg, {}, model);
} else {
message.destroy();
}
},
});
}
},
fetchServerInfo: function( endpoint ) {
var that = this;
if( endpoint && endpoint.baseUrl ) {
var info = new Bikini.Model();
var time = that.getLastMessageTime(endpoint.channel);
var url = endpoint.baseUrl;
if (url.charAt((url.length - 1)) !== '/') {
url += '/';
}
return info.fetch({
url: url + 'info',
success: function(a, b, response) {
if( !time && info.get('time') ) {
that.setLastMessageTime(endpoint.channel, info.get('time'));
}
if( !endpoint.socketPath && info.get('socketPath') ) {
endpoint.socketPath = info.get('socketPath');
var name = info.get('entity') || endpoint.entity.name;
if( that.options.useSocketNotify ) {
that.createSocket(endpoint, name);
}
}
return response.xhr;
},
credentials: endpoint.credentials
});
}
},
mergeMessages: function (data, id) {
return data;
},
sendMessages: function( endpoint ) {
if( endpoint && endpoint.messages ) {
var that = this;
endpoint.messages.each(function( message ) {
var msg;
try {
msg = JSON.parse(message.get('msg'));
} catch( e ) {
}
var channel = message.get('channel');
if( msg && channel ) {
var model = that.createModel({ collection: endpoint.messages }, msg.data);
that.emitMessage(endpoint, msg, {}, model);
} else {
message.destroy();
}
});
}
},
storeMessage: function (endpoint, msg, callback) {
if (endpoint && endpoint.messages && msg) {
var channel = endpoint.channel;
var message = endpoint.messages.get(msg._id);
if (message) {
var oldMsg = JSON.parse(message.get('msg'));
message.save({
msg: JSON.stringify(_.extend(oldMsg, msg))
});
} else {
endpoint.messages.create({
_id: msg._id,
id: msg.id,
msg: JSON.stringify(msg),
channel: channel
});
}
}
return callback(endpoint, msg);
},
mergeMessages: function( data, id ) {
return data;
},
removeMessage: function (endpoint, msg, callback) {
if (endpoint && endpoint.messages) {
var message = endpoint.messages.get(msg._id);
if (message) {
message.destroy();
}
}
return callback(endpoint, msg);
},
storeMessage: function( endpoint, msg, callback ) {
if( endpoint && endpoint.messages && msg ) {
var channel = endpoint.channel;
var message = endpoint.messages.get(msg._id);
if( message ) {
var oldMsg = JSON.parse(message.get('msg'));
message.save({
msg: JSON.stringify(_.extend(oldMsg, msg))
});
} else {
endpoint.messages.create({
_id: msg._id,
id: msg.id,
msg: JSON.stringify(msg),
channel: channel
});
}
clear: function (collection) {
if (collection) {
var endpoint = this.getEndpoint(collection.getUrlRoot());
if (endpoint) {
if (endpoint.messages) {
endpoint.messages.destroy();
}
return callback(endpoint, msg);
},
collection.reset();
this.setLastMessageTime(endpoint.channel, '');
}
}
},
removeMessage: function( endpoint, msg, callback ) {
if( endpoint && endpoint.messages ) {
var message = endpoint.messages.get(msg._id);
if( message ) {
message.destroy();
}
}
return callback(endpoint, msg);
},
/*
url = "http://example.com:3000/pathname/?search=test#hash";
clear: function( collection ) {
if( collection ) {
var endpoint = this.getEndpoint(collection.getUrlRoot());
if( endpoint ) {
if( endpoint.messages ) {
endpoint.messages.destroy();
}
collection.reset();
this.setLastMessageTime(endpoint.channel, '');
}
}
},
/*
url = "http://example.com:3000/pathname/?search=test#hash";
location.protocol; // => "http:"
location.host; // => "example.com:3000"
location.hostname; // => "example.com"
location.port; // => "3000"
location.pathname; // => "/pathname/"
location.hash; // => "#hash"
location.search; // => "?search=test"
*/
getLocation: function( url ) {
var location = document.createElement('a');
location.href = url || this.url;
// IE doesn't populate all link properties when setting .href with a relative URL,
// however .href will return an absolute URL which then can be used on itself
// to populate these additional fields.
if( location.host === '' ) {
location.href = location.href;
}
return location;
location.protocol; // => "http:"
location.host; // => "example.com:3000"
location.hostname; // => "example.com"
location.port; // => "3000"
location.pathname; // => "/pathname/"
location.hash; // => "#hash"
location.search; // => "?search=test"
*/
getLocation: function (url) {
var location = document.createElement('a');
location.href = url || this.url;
// IE doesn't populate all link properties when setting .href with a relative URL,
// however .href will return an absolute URL which then can be used on itself
// to populate these additional fields.
if (location.host === '') {
location.href = location.href;
}
return location;
}
});

@@ -26,158 +26,158 @@ // Copyright (c) 2013 M-Way Solutions GmbH

_type: 'Bikini.LocalStorageStore',
_type: 'Bikini.LocalStorageStore',
ids: {},
ids: {},
sync: function( method, model, options ) {
options = options || {};
var that = options.store || this.store;
var entity = that.getEntity(model.entity || options.entity || this.entity);
var attrs;
if( that && entity && model ) {
var id = model.id || (method === 'create' ? new Bikini.ObjectID().toHexString() : null);
attrs = options.attrs || model.toJSON(options);
switch( method ) {
case 'patch':
case 'update':
case 'create':
if (method !== 'create') {
attrs = _.extend(that._getItem(entity, id) || {}, attrs);
}
if( model.id !== id && model.idAttribute ) {
attrs[model.idAttribute] = id;
}
that._setItem(entity, id, attrs);
break;
case 'delete' :
that._removeItem(entity, id);
break;
case 'read' :
if( id ) {
attrs = that._getItem(entity, id);
} else {
attrs = [];
var ids = that._getItemIds(entity);
for( id in ids ) {
var itemData = that._getItem(entity, id);
if( itemData ) {
attrs.push(itemData);
}
}
}
break;
default:
return;
sync: function (method, model, options) {
options = options || {};
var that = options.store || this.store;
var entity = that.getEntity(model.entity || options.entity || this.entity);
var attrs;
if (that && entity && model) {
var id = model.id || (method === 'create' ? new Bikini.ObjectID().toHexString() : null);
attrs = options.attrs || model.toJSON(options);
switch (method) {
case 'patch':
case 'update':
case 'create':
if (method !== 'create') {
attrs = _.extend(that._getItem(entity, id) || {}, attrs);
}
if (model.id !== id && model.idAttribute) {
attrs[model.idAttribute] = id;
}
that._setItem(entity, id, attrs);
break;
case 'delete' :
that._removeItem(entity, id);
break;
case 'read' :
if (id) {
attrs = that._getItem(entity, id);
} else {
attrs = [];
var ids = that._getItemIds(entity);
for (id in ids) {
var itemData = that._getItem(entity, id);
if (itemData) {
attrs.push(itemData);
}
}
}
if( attrs ) {
that.handleSuccess(options, attrs);
} else {
that.handleError(options, Bikini.Store.CONST.ERROR_NO_ENTITY);
}
},
}
break;
default:
return;
}
}
if (attrs) {
that.handleSuccess(options, attrs);
} else {
that.handleError(options, Bikini.Store.CONST.ERROR_NO_ENTITY);
}
},
drop: function( options ) {
var entity = this.getEntity(options);
if( entity && entity.name ) {
var keys = this._findAllKeys(entity);
for (var i=0; i<keys.length; i++) {
localStorage.removeItem(keys[i]);
}
localStorage.removeItem('__ids__' + entity.name);
this.handleSuccess(options);
} else {
this.handleError(options, Bikini.Store.CONST.ERROR_NO_ENTITY);
}
},
drop: function (options) {
var entity = this.getEntity(options);
if (entity && entity.name) {
var keys = this._findAllKeys(entity);
for (var i = 0; i < keys.length; i++) {
localStorage.removeItem(keys[i]);
}
localStorage.removeItem('__ids__' + entity.name);
this.handleSuccess(options);
} else {
this.handleError(options, Bikini.Store.CONST.ERROR_NO_ENTITY);
}
},
_getKey: function( entity, id ) {
return '_' + entity.name + '_' + id;
},
_getKey: function (entity, id) {
return '_' + entity.name + '_' + id;
},
_getItem: function( entity, id ) {
var attrs;
if( entity && id ) {
try {
attrs = JSON.parse(localStorage.getItem(this._getKey(entity, id)));
if( attrs ) {
entity.setId(attrs, id); // fix id
} else {
this._delItemId(id);
}
} catch( e ) {
console.error(Bikini.Store.CONST.ERROR_LOAD_DATA + e.message);
}
_getItem: function (entity, id) {
var attrs;
if (entity && id) {
try {
attrs = JSON.parse(localStorage.getItem(this._getKey(entity, id)));
if (attrs) {
entity.setId(attrs, id); // fix id
} else {
this._delItemId(id);
}
return attrs;
},
} catch (e) {
console.error(Bikini.Store.CONST.ERROR_LOAD_DATA + e.message);
}
}
return attrs;
},
_setItem: function( entity, id, attrs ) {
if( entity && id && attrs ) {
try {
localStorage.setItem(this._getKey(entity, id), JSON.stringify(attrs));
this._addItemId(entity, id);
} catch( e ) {
console.error(Bikini.Store.CONST.ERROR_SAVE_DATA + e.message);
}
}
},
_setItem: function (entity, id, attrs) {
if (entity && id && attrs) {
try {
localStorage.setItem(this._getKey(entity, id), JSON.stringify(attrs));
this._addItemId(entity, id);
} catch (e) {
console.error(Bikini.Store.CONST.ERROR_SAVE_DATA + e.message);
}
}
},
_removeItem: function( entity, id ) {
if( entity && id ) {
localStorage.removeItem(this._getKey(entity, id));
this._delItemId(entity, id);
}
},
_removeItem: function (entity, id) {
if (entity && id) {
localStorage.removeItem(this._getKey(entity, id));
this._delItemId(entity, id);
}
},
_addItemId: function( entity, id ) {
var ids = this._getItemIds(entity);
if( !(id in ids) ) {
ids[id] = '';
this._saveItemIds(entity, ids);
}
},
_addItemId: function (entity, id) {
var ids = this._getItemIds(entity);
if (!(id in ids)) {
ids[id] = '';
this._saveItemIds(entity, ids);
}
},
_delItemId: function( entity, id ) {
var ids = this._getItemIds(entity);
if( id in ids ) {
delete ids[id];
this._saveItemIds(entity, ids);
}
},
_delItemId: function (entity, id) {
var ids = this._getItemIds(entity);
if (id in ids) {
delete ids[id];
this._saveItemIds(entity, ids);
}
},
_findAllKeys: function (entity) {
var keys = [];
var prefixItem = this._getKey(entity, '');
if( prefixItem ) {
var key, len = localStorage.length;
for (var i=0; i < len; i++) {
key = localStorage.key(i);
if (key && key === prefixItem) {
keys.push(key);
}
}
_findAllKeys: function (entity) {
var keys = [];
var prefixItem = this._getKey(entity, '');
if (prefixItem) {
var key, len = localStorage.length;
for (var i = 0; i < len; i++) {
key = localStorage.key(i);
if (key && key === prefixItem) {
keys.push(key);
}
return keys;
},
}
}
return keys;
},
_getItemIds: function( entity ) {
try {
var key = '__ids__' + entity.name;
if( !this.ids[entity.name] ) {
this.ids[entity.name] = JSON.parse(localStorage.getItem(key)) || {};
}
return this.ids[entity.name];
} catch( e ) {
console.error(Bikini.Store.CONST.ERROR_LOAD_IDS + e.message);
}
},
_getItemIds: function (entity) {
try {
var key = '__ids__' + entity.name;
if (!this.ids[entity.name]) {
this.ids[entity.name] = JSON.parse(localStorage.getItem(key)) || {};
}
return this.ids[entity.name];
} catch (e) {
console.error(Bikini.Store.CONST.ERROR_LOAD_IDS + e.message);
}
},
_saveItemIds: function( entity, ids ) {
try {
var key = '__ids__' + entity.name;
localStorage.setItem(key, JSON.stringify(ids));
} catch( e ) {
console.error(Bikini.Store.CONST.ERROR_SAVE_IDS + e.message);
}
_saveItemIds: function (entity, ids) {
try {
var key = '__ids__' + entity.name;
localStorage.setItem(key, JSON.stringify(ids));
} catch (e) {
console.error(Bikini.Store.CONST.ERROR_SAVE_IDS + e.message);
}
});
}
});

@@ -12,4 +12,4 @@ // Copyright (c) 2013 M-Way Solutions GmbH

*/
Bikini.Store = function() {
this.initialize.apply(this, arguments);
Bikini.Store = function () {
this.initialize.apply(this, arguments);
};

@@ -24,234 +24,234 @@

_type: 'Bikini.Store',
_type: 'Bikini.Store',
entities: null,
entities: null,
options: null,
options: null,
name: '',
name: '',
typeMapping: (function() {
var map = {};
map [Bikini.DATA.TYPE.OBJECTID] = Bikini.DATA.TYPE.STRING;
map [Bikini.DATA.TYPE.DATE] = Bikini.DATA.TYPE.STRING;
map [Bikini.DATA.TYPE.BINARY] = Bikini.DATA.TYPE.TEXT;
return map;
})(),
typeMapping: (function () {
var map = {};
map [Bikini.DATA.TYPE.OBJECTID] = Bikini.DATA.TYPE.STRING;
map [Bikini.DATA.TYPE.DATE] = Bikini.DATA.TYPE.STRING;
map [Bikini.DATA.TYPE.BINARY] = Bikini.DATA.TYPE.TEXT;
return map;
})(),
initialize: function( options ) {
options = options || {};
this.options = this.options || {};
this.options.name = this.name;
this.options.typeMapping = this.typeMapping;
this.options.entities = this.entities;
_.extend(this.options, options || {});
initialize: function (options) {
options = options || {};
this.options = this.options || {};
this.options.name = this.name;
this.options.typeMapping = this.typeMapping;
this.options.entities = this.entities;
_.extend(this.options, options || {});
this._setEntities(options.entities || {});
},
this._setEntities(options.entities || {});
},
_setEntities: function( entities ) {
this.entities = {};
for( var name in entities ) {
var entity = Bikini.Entity.from(entities[name], {
store: this,
typeMapping: this.options.typeMapping
});
entity.name = entity.name || name;
_setEntities: function (entities) {
this.entities = {};
for (var name in entities) {
var entity = Bikini.Entity.from(entities[name], {
store: this,
typeMapping: this.options.typeMapping
});
entity.name = entity.name || name;
// connect collection and model to this store
var collection = entity.collection || Bikini.Collection.extend({ model: Bikini.Model.extend({}) });
var model = collection.prototype.model;
// set new entity and name
collection.prototype.entity = model.prototype.entity = name;
collection.prototype.store = model.prototype.store = this;
entity.idAttribute = entity.idAttribute || model.prototype.idAttribute;
this.entities[name] = entity;
}
},
// connect collection and model to this store
var collection = entity.collection || Bikini.Collection.extend({model: Bikini.Model.extend({})});
var model = collection.prototype.model;
// set new entity and name
collection.prototype.entity = model.prototype.entity = name;
collection.prototype.store = model.prototype.store = this;
entity.idAttribute = entity.idAttribute || model.prototype.idAttribute;
this.entities[name] = entity;
}
},
getEntity: function( obj ) {
if( obj ) {
var entity = obj.entity || obj;
var name = _.isString(entity) ? entity : entity.name;
if( name ) {
return this.entities[name] || (entity && entity.name ? entity : { name: name });
}
}
},
getEntity: function (obj) {
if (obj) {
var entity = obj.entity || obj;
var name = _.isString(entity) ? entity : entity.name;
if (name) {
return this.entities[name] || (entity && entity.name ? entity : {name: name});
}
}
},
getCollection: function( entity ) {
if( _.isString(entity) ) {
entity = this.entities[entity];
}
if( entity && entity.collection ) {
if( Bikini.Collection.prototype.isPrototypeOf(entity.collection) ) {
return entity.collection;
} else {
return new entity.collection();
}
}
},
getCollection: function (entity) {
if (_.isString(entity)) {
entity = this.entities[entity];
}
if (entity && entity.collection) {
if (Bikini.Collection.prototype.isPrototypeOf(entity.collection)) {
return entity.collection;
} else {
return new entity.collection();
}
}
},
createModel: function( entity, attrs ) {
if( _.isString(entity) ) {
entity = this.entities[entity];
}
if( entity && entity.collection ) {
var Model = entity.collection.model || entity.collection.prototype.model;
if( Model ) {
return new Model(attrs);
}
}
},
createModel: function (entity, attrs) {
if (_.isString(entity)) {
entity = this.entities[entity];
}
if (entity && entity.collection) {
var Model = entity.collection.model || entity.collection.prototype.model;
if (Model) {
return new Model(attrs);
}
}
},
getArray: function( data ) {
if( _.isArray(data) ) {
return data;
} else if( Bikini.isCollection(data) ) {
return data.models;
}
return _.isObject(data) ? [ data ] : [];
},
getArray: function (data) {
if (_.isArray(data)) {
return data;
} else if (Bikini.isCollection(data)) {
return data.models;
}
return _.isObject(data) ? [data] : [];
},
getDataArray: function( data ) {
var array = [];
if( _.isArray(data) || Backbone.Collection.prototype.isPrototypeOf(data) ) {
_.each(data, function( d ) {
var attrs = this.getAttributes(d);
if( attrs ) {
array.push(attrs);
}
});
} else {
var attrs = this.getAttributes(data);
if( attrs ) {
array.push(this.getAttributes(attrs));
}
getDataArray: function (data) {
var array = [];
if (_.isArray(data) || Backbone.Collection.prototype.isPrototypeOf(data)) {
_.each(data, function (d) {
var attrs = this.getAttributes(d);
if (attrs) {
array.push(attrs);
}
return array;
},
});
} else {
var attrs = this.getAttributes(data);
if (attrs) {
array.push(this.getAttributes(attrs));
}
}
return array;
},
getAttributes: function( model ) {
if( Backbone.Model.prototype.isPrototypeOf(model) ) {
return model.attributes;
}
return _.isObject(model) ? model : null;
},
getAttributes: function (model) {
if (Backbone.Model.prototype.isPrototypeOf(model)) {
return model.attributes;
}
return _.isObject(model) ? model : null;
},
initModel: function( model ) {
},
initModel: function (model) {
},
initCollection: function( collection ) {
},
initCollection: function (collection) {
},
initEntity: function( entity ) {
},
initEntity: function (entity) {
},
sync: function( method, model, options ) {
},
sync: function (method, model, options) {
},
/**
*
* @param collection usally a collection, but can also be a model
* @param options
*/
fetch: function( collection, options ) {
if( collection && !collection.models && !collection.attributes && !options ) {
options = collection;
}
if( (!collection || (!collection.models && !collection.attributes)) && options && options.entity ) {
collection = this.getCollection(options.entity);
}
if( collection && collection.fetch ) {
var opts = _.extend({}, options || {}, { store: this });
return collection.fetch(opts);
}
},
/**
*
* @param collection usally a collection, but can also be a model
* @param options
*/
fetch: function (collection, options) {
if (collection && !collection.models && !collection.attributes && !options) {
options = collection;
}
if ((!collection || (!collection.models && !collection.attributes)) && options && options.entity) {
collection = this.getCollection(options.entity);
}
if (collection && collection.fetch) {
var opts = _.extend({}, options || {}, {store: this});
return collection.fetch(opts);
}
},
create: function( collection, model, options ) {
if( collection && !collection.models && !options ) {
model = collection;
options = model;
}
if( (!collection || !collection.models) && options && options.entity ) {
collection = this.getCollection(options.entity);
}
if( collection && collection.create ) {
var opts = _.extend({}, options || {}, { store: this });
collection.create(model, opts);
}
},
create: function (collection, model, options) {
if (collection && !collection.models && !options) {
model = collection;
options = model;
}
if ((!collection || !collection.models) && options && options.entity) {
collection = this.getCollection(options.entity);
}
if (collection && collection.create) {
var opts = _.extend({}, options || {}, {store: this});
collection.create(model, opts);
}
},
save: function( model, attr, options ) {
if( model && !model.attributes && !options ) {
attr = model;
options = attr;
}
if( (!model || !model.attributes) && options && options.entity ) {
model = this.createModel(options.entity);
}
if( model && model.save ) {
var opts = _.extend({}, options || {}, { store: this });
model.save(attr, opts);
}
},
save: function (model, attr, options) {
if (model && !model.attributes && !options) {
attr = model;
options = attr;
}
if ((!model || !model.attributes) && options && options.entity) {
model = this.createModel(options.entity);
}
if (model && model.save) {
var opts = _.extend({}, options || {}, {store: this});
model.save(attr, opts);
}
},
destroy: function( model, options ) {
if( model && model.destroy ) {
var opts = _.extend({}, options || {}, { store: this });
model.destroy(opts);
}
},
destroy: function (model, options) {
if (model && model.destroy) {
var opts = _.extend({}, options || {}, {store: this});
model.destroy(opts);
}
},
_checkEntity: function( obj, entity ) {
if( !Bikini.isEntity(entity) ) {
var error = Bikini.Store.CONST.ERROR_NO_ENTITY;
console.error(error);
this.handleCallback(obj.error, error);
this.handleCallback(obj.finish, error);
return false;
}
return true;
},
_checkEntity: function (obj, entity) {
if (!Bikini.isEntity(entity)) {
var error = Bikini.Store.CONST.ERROR_NO_ENTITY;
console.error(error);
this.handleCallback(obj.error, error);
this.handleCallback(obj.finish, error);
return false;
}
return true;
},
_checkData: function( obj, data ) {
if( (!_.isArray(data) || data.length === 0) && !_.isObject(data) ) {
var error = Bikini.Store.CONST.ERROR_NO_DATA;
console.error(error);
this.handleCallback(obj.error, error);
this.handleCallback(obj.finish, error);
return false;
}
return true;
},
_checkData: function (obj, data) {
if ((!_.isArray(data) || data.length === 0) && !_.isObject(data)) {
var error = Bikini.Store.CONST.ERROR_NO_DATA;
console.error(error);
this.handleCallback(obj.error, error);
this.handleCallback(obj.finish, error);
return false;
}
return true;
},
handleSuccess: function( obj ) {
var args = Array.prototype.slice.call(arguments, 1);
if( obj.success ) {
this.handleCallback.apply(this, [ obj.success ].concat(args));
}
if( obj.finish ) {
this.handleCallback.apply(this, [ obj.finish ].concat(args));
}
},
handleSuccess: function (obj) {
var args = Array.prototype.slice.call(arguments, 1);
if (obj.success) {
this.handleCallback.apply(this, [obj.success].concat(args));
}
if (obj.finish) {
this.handleCallback.apply(this, [obj.finish].concat(args));
}
},
handleError: function( obj ) {
var args = Array.prototype.slice.call(arguments, 1);
if( obj.error ) {
this.handleCallback.apply(this, [ obj.error ].concat(args));
}
if( obj.finish ) {
this.handleCallback.apply(this, [ obj.finish ].concat(args));
}
},
CONST: {
ERROR_NO_ENTITY: 'No valid entity specified. ',
ERROR_NO_DATA: 'No data passed. ',
ERROR_LOAD_DATA: 'Error while loading data from store. ',
ERROR_SAVE_DATA: 'Error while saving data to the store. ',
ERROR_LOAD_IDS: 'Error while loading ids from store. ',
ERROR_SAVE_IDS: 'Error while saving ids to the store. '
handleError: function (obj) {
var args = Array.prototype.slice.call(arguments, 1);
if (obj.error) {
this.handleCallback.apply(this, [obj.error].concat(args));
}
if (obj.finish) {
this.handleCallback.apply(this, [obj.finish].concat(args));
}
},
CONST: {
ERROR_NO_ENTITY: 'No valid entity specified. ',
ERROR_NO_DATA: 'No data passed. ',
ERROR_LOAD_DATA: 'Error while loading data from store. ',
ERROR_SAVE_DATA: 'Error while saving data to the store. ',
ERROR_LOAD_IDS: 'Error while loading ids from store. ',
ERROR_SAVE_IDS: 'Error while saving ids to the store. '
}
});

@@ -19,6 +19,6 @@ // Copyright (c) 2013 M-Way Solutions GmbH

* var MyCollection = Bikini.Collection.extend({
* model: MyModel,
* entity: 'MyTableName',
* store: new Bikini.WebSqlStorageStore()
* });
* model: MyModel,
* entity: 'MyTableName',
* store: new Bikini.WebSqlStorageStore()
* });
*

@@ -29,10 +29,10 @@ * // If you want to use specific columns you can specify the fields

* var MyModel = Bikini.Model.extend({
* idAttribute: 'id',
* fields: {
* id: { type: Bikini.DATA.TYPE.STRING, required: YES, index: YES },
* sureName: { name: 'USERNAME', type: Bikini.DATA.TYPE.STRING },
* firstName: { type: Bikini.DATA.TYPE.STRING, length: 200 },
* age: { type: Bikini.DATA.TYPE.INTEGER }
* }
* });
* idAttribute: 'id',
* fields: {
* id: { type: Bikini.DATA.TYPE.STRING, required: YES, index: YES },
* sureName: { name: 'USERNAME', type: Bikini.DATA.TYPE.STRING },
* firstName: { type: Bikini.DATA.TYPE.STRING, length: 200 },
* age: { type: Bikini.DATA.TYPE.INTEGER }
* }
* });
*

@@ -43,580 +43,584 @@ *

_type: 'Bikini.WebSqlStore',
_type: 'Bikini.WebSqlStore',
_selector: null,
_selector: null,
options: null,
options: null,
name: 'bikini',
name: 'bikini',
size: 1024 * 1024, // 1 MB
size: 1024 * 1024, // 1 MB
version: '1.0',
version: '1.0',
db: null,
db: null,
dataField: { name: 'data', type: 'text', required: true },
dataField: {name: 'data', type: 'text', required: true},
idField: { name: 'id', type: 'string', required: true },
idField: {name: 'id', type: 'string', required: true},
typeMapping: (function() {
var map = {};
map [Bikini.DATA.TYPE.OBJECTID] = Bikini.DATA.TYPE.STRING;
map [Bikini.DATA.TYPE.DATE] = Bikini.DATA.TYPE.STRING;
map [Bikini.DATA.TYPE.OBJECT] = Bikini.DATA.TYPE.TEXT;
map [Bikini.DATA.TYPE.ARRAY] = Bikini.DATA.TYPE.TEXT;
map [Bikini.DATA.TYPE.BINARY] = Bikini.DATA.TYPE.TEXT;
return map;
})(),
typeMapping: (function () {
var map = {};
map [Bikini.DATA.TYPE.OBJECTID] = Bikini.DATA.TYPE.STRING;
map [Bikini.DATA.TYPE.DATE] = Bikini.DATA.TYPE.STRING;
map [Bikini.DATA.TYPE.OBJECT] = Bikini.DATA.TYPE.TEXT;
map [Bikini.DATA.TYPE.ARRAY] = Bikini.DATA.TYPE.TEXT;
map [Bikini.DATA.TYPE.BINARY] = Bikini.DATA.TYPE.TEXT;
return map;
})(),
sqlTypeMapping: (function() {
var map = {};
map [Bikini.DATA.TYPE.STRING] = 'varchar(255)';
map [Bikini.DATA.TYPE.TEXT] = 'text';
map [Bikini.DATA.TYPE.OBJECT] = 'text';
map [Bikini.DATA.TYPE.ARRAY] = 'text';
map [Bikini.DATA.TYPE.FLOAT] = 'float';
map [Bikini.DATA.TYPE.INTEGER] = 'integer';
map [Bikini.DATA.TYPE.DATE] = 'varchar(255)';
map [Bikini.DATA.TYPE.BOOLEAN] = 'boolean';
return map;
})(),
sqlTypeMapping: (function () {
var map = {};
map [Bikini.DATA.TYPE.STRING] = 'varchar(255)';
map [Bikini.DATA.TYPE.TEXT] = 'text';
map [Bikini.DATA.TYPE.OBJECT] = 'text';
map [Bikini.DATA.TYPE.ARRAY] = 'text';
map [Bikini.DATA.TYPE.FLOAT] = 'float';
map [Bikini.DATA.TYPE.INTEGER] = 'integer';
map [Bikini.DATA.TYPE.DATE] = 'varchar(255)';
map [Bikini.DATA.TYPE.BOOLEAN] = 'boolean';
return map;
})(),
initialize: function( options ) {
Bikini.Store.prototype.initialize.apply(this, arguments);
this.options = this.options || {};
this.options.name = this.name;
this.options.size = this.size;
this.options.version = this.version;
this.options.typeMapping = this.typeMapping;
this.options.sqlTypeMapping = this.sqlTypeMapping;
_.extend(this.options, options || {});
initialize: function (options) {
Bikini.Store.prototype.initialize.apply(this, arguments);
this.options = this.options || {};
this.options.name = this.name;
this.options.size = this.size;
this.options.version = this.version;
this.options.typeMapping = this.typeMapping;
this.options.sqlTypeMapping = this.sqlTypeMapping;
_.extend(this.options, options || {});
this._openDb({
error: function( msg ) {
console.error(msg);
}
this._openDb({
error: function (msg) {
console.error(msg);
}
});
},
sync: function (method, model, options) {
var that = options.store || this.store;
var models = Bikini.isCollection(model) ? model.models : [model];
var q = new $.Deferred();
var _oldSuccess = options.success;
if (options.success) {
options.success = function (response) {
q.resolve(response);
_oldSuccess(response);
};
}
//debugger;
options.entity = options.entity || this.entity;
switch (method) {
case 'create':
that._checkTable(options, function () {
that._insertOrReplace(models, options);
});
},
break;
sync: function( method, model, options ) {
var that = options.store || this.store;
var models = Bikini.isCollection(model) ? model.models : [ model ];
var q = new $.Deferred();
var _oldSuccess = options.success;
if(options.success) {
options.success = function(response) {
q.resolve(response);
_oldSuccess(response);
};
}
options.entity = options.entity || this.entity;
switch( method ) {
case 'create':
that._checkTable(options, function() {
that._insertOrReplace(models, options);
});
break;
case 'update':
case 'patch':
that._checkTable(options, function () {
that._insertOrReplace(models, options);
});
break;
case 'update':
case 'patch':
that._checkTable(options, function() {
that._insertOrReplace(models, options);
});
break;
case 'delete':
that._delete(models, options);
break;
case 'delete':
that._delete(models, options);
break;
case 'read':
var self = this;
that._checkTable(options, function () {
that._select(self, options);
});
break;
case 'read':
that._select(this, options);
break;
default:
break;
}
return q.promise();
},
default:
break;
}
return q.promise();
},
select: function (options) {
this._select(null, options);
},
select: function( options ) {
this._select(null, options);
},
drop: function (options) {
this._dropTable(options);
},
drop: function( options ) {
this._dropTable(options);
},
createTable: function (options) {
this._createTable(options);
},
createTable: function( options ) {
this._createTable(options);
},
execute: function (options) {
this._executeSql(options);
},
execute: function( options ) {
this._executeSql(options);
},
/**
* @private
*/
_openDb: function( options ) {
var error, dbError;
/* openDatabase(db_name, version, description, estimated_size, callback) */
if( !this.db ) {
try {
if( !window.openDatabase ) {
error = 'Your browser does not support WebSQL databases.';
} else {
this.db = window.openDatabase(this.options.name, '', '', this.options.size);
if( this.entities ) {
for( var key in this.entities ) {
this._createTable({ entity: this.entities[key] });
}
}
}
} catch( e ) {
dbError = e;
}
}
if( this.db ) {
if( this.options.version && this.db.version !== this.options.version ) {
this._updateDb(options);
} else {
this.handleSuccess(options, this.db);
}
} else if( dbError === 2 || dbError === '2') {
// Version number mismatch.
this._updateDb(options);
/**
* @private
*/
_openDb: function (options) {
var error, dbError;
/* openDatabase(db_name, version, description, estimated_size, callback) */
if (!this.db) {
try {
if (!window.openDatabase) {
error = 'Your browser does not support WebSQL databases.';
} else {
if( !error && dbError ) {
error = dbError;
this.db = window.openDatabase(this.options.name, '', '', this.options.size);
if (this.entities) {
for (var key in this.entities) {
this._createTable({entity: this.entities[key]});
}
this.handleSuccess(options, error);
}
}
},
} catch (e) {
dbError = e;
}
}
if (this.db) {
if (this.options.version && this.db.version !== this.options.version) {
this._updateDb(options);
} else {
this.handleSuccess(options, this.db);
}
} else if (dbError === 2 || dbError === '2') {
// Version number mismatch.
this._updateDb(options);
} else {
if (!error && dbError) {
error = dbError;
}
this.handleSuccess(options, error);
}
},
_updateDb: function( options ) {
var error;
var lastSql;
var that = this;
try {
var db = window.openDatabase(this.options.name, '', '', this.options.size);
try {
var arSql = this._sqlUpdateDatabase(db.version, this.options.version);
db.changeVersion(db.version, this.options.version, function( tx ) {
_.each(arSql, function( sql ) {
console.log('sql statement: ' + sql);
lastSql = sql;
tx.executeSql(sql);
});
}, function( msg ) {
that.handleError(options, msg, lastSql);
}, function() {
that.handleSuccess(options);
});
} catch( e ) {
error = e.message;
console.error('webSql change version failed, DB-Version: ' + db.version);
}
} catch( e ) {
error = e.message;
}
if( error ) {
this.handleError(options, error);
}
},
_updateDb: function (options) {
var error;
var lastSql;
var that = this;
try {
var db = window.openDatabase(this.options.name, '', '', this.options.size);
try {
var arSql = this._sqlUpdateDatabase(db.version, this.options.version);
db.changeVersion(db.version, this.options.version, function (tx) {
_.each(arSql, function (sql) {
console.log('sql statement: ' + sql);
lastSql = sql;
tx.executeSql(sql);
});
}, function (msg) {
that.handleError(options, msg, lastSql);
}, function () {
that.handleSuccess(options);
});
} catch (e) {
error = e.message;
console.error('webSql change version failed, DB-Version: ' + db.version);
}
} catch (e) {
error = e.message;
}
if (error) {
this.handleError(options, error);
}
},
_sqlUpdateDatabase: function( oldVersion, newVersion ) {
// create sql array, simply drop and create the database
var sql = [];
if( this.entities ) {
for( var name in this.entities ) {
var entity = this.entities[name];
sql.push(this._sqlDropTable(entity.name));
sql.push(this._sqlCreateTable(entity));
}
}
return sql;
},
_sqlUpdateDatabase: function (oldVersion, newVersion) {
// create sql array, simply drop and create the database
var sql = [];
if (this.entities) {
for (var name in this.entities) {
var entity = this.entities[name];
sql.push(this._sqlDropTable(entity.name));
sql.push(this._sqlCreateTable(entity));
}
}
return sql;
},
_sqlDropTable: function( name ) {
return 'DROP TABLE IF EXISTS \'' + name + '\'';
},
_sqlDropTable: function (name) {
return 'DROP TABLE IF EXISTS \'' + name + '\'';
},
_isAutoincrementKey: function( entity, key ) {
if( entity && key ) {
var column = this.getField(entity, key);
return column && column.type === Bikini.DATA.TYPE.INTEGER;
}
},
_isAutoincrementKey: function (entity, key) {
if (entity && key) {
var column = this.getField(entity, key);
return column && column.type === Bikini.DATA.TYPE.INTEGER;
}
},
_sqlPrimaryKey: function( entity, keys ) {
if( keys && keys.length === 1 ) {
if( this._isAutoincrementKey(entity, keys[0]) ) {
return keys[0] + ' INTEGER PRIMARY KEY ASC AUTOINCREMENT UNIQUE';
} else {
return keys[0] + ' PRIMARY KEY ASC UNIQUE';
}
}
return '';
},
_sqlPrimaryKey: function (entity, keys) {
if (keys && keys.length === 1) {
if (this._isAutoincrementKey(entity, keys[0])) {
return keys[0] + ' INTEGER PRIMARY KEY ASC AUTOINCREMENT UNIQUE';
} else {
return keys[0] + ' PRIMARY KEY ASC UNIQUE';
}
}
return '';
},
_sqlConstraint: function( entity, keys ) {
if( keys && keys.length > 1 ) {
return 'PRIMARY KEY (' + keys.join(',') + ') ON CONFLICT REPLACE';
}
return '';
},
_sqlConstraint: function (entity, keys) {
if (keys && keys.length > 1) {
return 'PRIMARY KEY (' + keys.join(',') + ') ON CONFLICT REPLACE';
}
return '';
},
_sqlCreateTable: function( entity ) {
var that = this;
var keys = entity.getKeys();
var primaryKey = keys.length === 1 ? this._sqlPrimaryKey(entity, keys) : '';
var constraint = keys.length > 1 ? this._sqlConstraint(entity, keys) : (entity.constraint || '');
_sqlCreateTable: function (entity) {
var that = this;
var keys = entity.getKeys();
var primaryKey = keys.length === 1 ? this._sqlPrimaryKey(entity, keys) : '';
var constraint = keys.length > 1 ? this._sqlConstraint(entity, keys) : (entity.constraint || '');
var columns = '';
var fields = this.getFields(entity);
_.each(fields, function( field ) {
// skip ID, it is defined manually above
if( !primaryKey || field.name !== keys[0] ) {
// only add valid types
var attr = that._dbAttribute(field);
if( attr ) {
columns += (columns ? ', ' : '') + attr;
}
}
});
if( !columns ) {
columns = this._dbAttribute(this.dataField);
var columns = '';
var fields = this.getFields(entity);
_.each(fields, function (field) {
// skip ID, it is defined manually above
if (!primaryKey || field.name !== keys[0]) {
// only add valid types
var attr = that._dbAttribute(field);
if (attr) {
columns += (columns ? ', ' : '') + attr;
}
var sql = 'CREATE TABLE IF NOT EXISTS \'' + entity.name + '\' (';
sql += primaryKey ? primaryKey + ', ' : '';
sql += columns;
sql += constraint ? ', ' + constraint : '';
sql += ');';
return sql;
},
}
});
if (!columns) {
columns = this._dbAttribute(this.dataField);
}
var sql = 'CREATE TABLE IF NOT EXISTS \'' + entity.name + '\' (';
sql += primaryKey ? primaryKey + ', ' : '';
sql += columns;
sql += constraint ? ', ' + constraint : '';
sql += ');';
return sql;
},
_sqlDelete: function(options, entity ) {
var sql = 'DELETE FROM \'' + entity.name + '\'';
var where = this._sqlWhere(options, entity) || this._sqlWhereFromData(options, entity);
if( where ) {
sql += ' WHERE ' + where;
}
sql += options.and ? ' AND ' + options.and : '';
return sql;
},
_sqlDelete: function (options, entity) {
var sql = 'DELETE FROM \'' + entity.name + '\'';
var where = this._sqlWhere(options, entity) || this._sqlWhereFromData(options, entity);
if (where) {
sql += ' WHERE ' + where;
}
sql += options.and ? ' AND ' + options.and : '';
return sql;
},
_sqlWhere: function( options, entity ) {
this._selector = null;
var sql = '';
if( _.isString(options.where) ) {
sql = options.where;
} else if( _.isObject(options.where) ) {
this._selector = Bikini.SqlSelector.create(options.where, entity);
sql = this._selector.buildStatement();
}
return sql;
},
_sqlWhere: function (options, entity) {
this._selector = null;
var sql = '';
if (_.isString(options.where)) {
sql = options.where;
} else if (_.isObject(options.where)) {
this._selector = Bikini.SqlSelector.create(options.where, entity);
sql = this._selector.buildStatement();
}
return sql;
},
_sqlWhereFromData: function(options, entity ) {
var that = this;
var ids = [];
if( options && options.models && entity && entity.idAttribute ) {
var id, key = entity.idAttribute;
var field = this.getField(entity, key);
_.each(options.models, function( model ) {
id = model.id;
if( !_.isUndefined(id) ) {
ids.push(that._sqlValue(id, field));
}
});
if( ids.length > 0 ) {
return key + ' IN (' + ids.join(',') + ')';
}
_sqlWhereFromData: function (options, entity) {
var that = this;
var ids = [];
if (options && options.models && entity && entity.idAttribute) {
var id, key = entity.idAttribute;
var field = this.getField(entity, key);
_.each(options.models, function (model) {
id = model.id;
if (!_.isUndefined(id)) {
ids.push(that._sqlValue(id, field));
}
return '';
},
});
if (ids.length > 0) {
return key + ' IN (' + ids.join(',') + ')';
}
}
return '';
},
_sqlSelect: function( options, entity ) {
_sqlSelect: function (options, entity) {
var sql = 'SELECT ';
if( options.fields ) {
if( options.fields.length > 1 ) {
sql += options.fields.join(', ');
} else if( options.fields.length === 1 ) {
sql += options.fields[0];
}
} else {
sql += '*';
}
sql += ' FROM \'' + entity.name + '\'';
if( options.join ) {
sql += ' JOIN ' + options.join;
}
var sql = 'SELECT ';
if (options.fields) {
if (options.fields.length > 1) {
sql += options.fields.join(', ');
} else if (options.fields.length === 1) {
sql += options.fields[0];
}
} else {
sql += '*';
}
sql += ' FROM \'' + entity.name + '\'';
if (options.join) {
sql += ' JOIN ' + options.join;
}
if( options.leftJoin ) {
sql += ' LEFT JOIN ' + options.leftJoin;
}
if (options.leftJoin) {
sql += ' LEFT JOIN ' + options.leftJoin;
}
var where = this._sqlWhere(options, entity) || this._sqlWhereFromData(options, entity);
if( where ) {
sql += ' WHERE ' + where;
}
var where = this._sqlWhere(options, entity) || this._sqlWhereFromData(options, entity);
if (where) {
sql += ' WHERE ' + where;
}
if( options.order ) {
sql += ' ORDER BY ' + options.order;
}
if (options.order) {
sql += ' ORDER BY ' + options.order;
}
if( options.limit ) {
sql += ' LIMIT ' + options.limit;
}
if (options.limit) {
sql += ' LIMIT ' + options.limit;
}
if( options.offset ) {
sql += ' OFFSET ' + options.offset;
}
if (options.offset) {
sql += ' OFFSET ' + options.offset;
}
return sql;
},
return sql;
},
_sqlValue: function( value, field ) {
var type = field && field.type ? field.type : Bikini.Field.prototype.detectType(value);
if( type === Bikini.DATA.TYPE.INTEGER || type === Bikini.DATA.TYPE.FLOAT ) {
return value;
} else if( type === Bikini.DATA.TYPE.BOOLEAN ) {
return value ? '1' : '0';
} else if( type === Bikini.DATA.TYPE.NULL ) {
return 'NULL';
}
value = Bikini.Field.prototype.transform(value, Bikini.DATA.TYPE.STRING);
value = value.replace(/"/g, '""');
return '"' + value + '"';
},
_sqlValue: function (value, field) {
var type = field && field.type ? field.type : Bikini.Field.prototype.detectType(value);
if (type === Bikini.DATA.TYPE.INTEGER || type === Bikini.DATA.TYPE.FLOAT) {
return value;
} else if (type === Bikini.DATA.TYPE.BOOLEAN) {
return value ? '1' : '0';
} else if (type === Bikini.DATA.TYPE.NULL) {
return 'NULL';
}
value = Bikini.Field.prototype.transform(value, Bikini.DATA.TYPE.STRING);
value = value.replace(/"/g, '""');
return '"' + value + '"';
},
_dbAttribute: function( field ) {
if( field && field.name ) {
var type = this.options.sqlTypeMapping[field.type];
var isReqStr = field.required ? ' NOT NULL' : '';
if( type ) {
return field.name + ' ' + type.toUpperCase() + isReqStr;
}
}
},
_dbAttribute: function (field) {
if (field && field.name) {
var type = this.options.sqlTypeMapping[field.type];
var isReqStr = field.required ? ' NOT NULL' : '';
if (type) {
return field.name + ' ' + type.toUpperCase() + isReqStr;
}
}
},
_dropTable: function( options ) {
_dropTable: function (options) {
var entity = this.getEntity(options);
entity.db = null;
var entity = this.getEntity(options);
entity.db = null;
if( this._checkDb(options) && entity ) {
var sql = this._sqlDropTable(entity.name);
// reset flag
this._executeTransaction(options, [sql]);
}
},
if (this._checkDb(options) && entity) {
var sql = this._sqlDropTable(entity.name);
// reset flag
this._executeTransaction(options, [sql]);
}
},
_createTable: function( options ) {
_createTable: function (options) {
var entity = this.getEntity(options);
entity.db = this.db;
var entity = this.getEntity(options);
entity.db = this.db;
if( this._checkDb(options) && this._checkEntity(options, entity) ) {
var sql = this._sqlCreateTable(entity);
// reset flag
this._executeTransaction(options, [sql]);
}
},
if (this._checkDb(options) && this._checkEntity(options, entity)) {
var sql = this._sqlCreateTable(entity);
// reset flag
this._executeTransaction(options, [sql]);
}
},
_checkTable: function( options, callback ) {
var entity = this.getEntity(options);
var that = this;
if( entity && !entity.db ) {
this._createTable({
success: function() {
callback();
},
error: function( error ) {
this.handleError(options, error);
},
entity: entity
});
} else {
callback();
}
},
_checkTable: function (options, callback) {
var entity = this.getEntity(options);
var that = this;
if (entity && !entity.db) {
this._createTable({
success: function () {
callback();
},
error: function (error) {
this.handleError(options, error);
},
entity: entity
});
} else {
callback();
}
},
_insertOrReplace: function( models, options ) {
_insertOrReplace: function (models, options) {
var entity = this.getEntity(options);
var entity = this.getEntity(options);
if( this._checkDb(options) && this._checkEntity(options, entity) && this._checkData(options, models) ) {
if (this._checkDb(options) && this._checkEntity(options, entity) && this._checkData(options, models)) {
var isAutoInc = this._isAutoincrementKey(entity, entity.getKey());
var statements = [];
var sqlTemplate = 'INSERT OR REPLACE INTO \'' + entity.name + '\' (';
for( var i = 0; i < models.length; i++ ) {
var model = models[i];
var statement = ''; // the actual sql insert string with values
if( !isAutoInc && !model.id && model.idAttribute ) {
model.set(model.idAttribute, new Bikini.ObjectID().toHexString());
}
var value = options.attrs || model.toJSON();
var args, keys;
if( !_.isEmpty(entity.fields) ) {
args = _.values(value);
keys = _.keys(value);
} else {
args = [ model.id, JSON.stringify(value) ];
keys = [ 'id', 'data'];
}
if( args.length > 0 ) {
var values = new Array(args.length).join('?,') + '?';
var columns = '\'' + keys.join('\',\'') + '\'';
statement += sqlTemplate + columns + ') VALUES (' + values + ');';
statements.push({ statement: statement, arguments: args });
}
}
this._executeTransaction(options, statements);
var isAutoInc = this._isAutoincrementKey(entity, entity.getKey());
var statements = [];
var sqlTemplate = 'INSERT OR REPLACE INTO \'' + entity.name + '\' (';
for (var i = 0; i < models.length; i++) {
var model = models[i];
var statement = ''; // the actual sql insert string with values
if (!isAutoInc && !model.id && model.idAttribute) {
model.set(model.idAttribute, new Bikini.ObjectID().toHexString());
}
},
var value = options.attrs || model.toJSON();
var args, keys;
if (!_.isEmpty(entity.fields)) {
args = _.values(value);
keys = _.keys(value);
} else {
args = [model.id, JSON.stringify(value)];
keys = ['id', 'data'];
}
if (args.length > 0) {
var values = new Array(args.length).join('?,') + '?';
var columns = '\'' + keys.join('\',\'') + '\'';
statement += sqlTemplate + columns + ') VALUES (' + values + ');';
statements.push({statement: statement, arguments: args});
}
}
this._executeTransaction(options, statements);
}
},
_select: function( result, options ) {
_select: function (result, options) {
var entity = this.getEntity(options);
var entity = this.getEntity(options);
if( this._checkDb(options) && this._checkEntity(options, entity) ) {
var lastStatement;
var isCollection = Bikini.isCollection(result);
if( isCollection ) {
result = [];
if (this._checkDb(options) && this._checkEntity(options, entity)) {
var lastStatement;
var isCollection = Bikini.isCollection(result);
if (isCollection) {
result = [];
} else {
options.models = [result];
}
var stm = this._sqlSelect(options, entity);
var that = this;
this.db.readTransaction(function (t) {
var statement = stm.statement || stm;
var args = stm.arguments;
lastStatement = statement;
console.log('sql statement: ' + statement);
if (args) {
console.log(' arguments: ' + JSON.stringify(args));
}
t.executeSql(statement, args, function (tx, res) {
var len = res.rows.length;//, i;
for (var i = 0; i < len; i++) {
var item = res.rows.item(i);
var attrs;
if (!_.isEmpty(entity.fields) || !that._hasDefaultFields(item)) {
attrs = item;
} else {
options.models = [ result ];
try {
attrs = JSON.parse(item.data);
} catch (e) {
}
}
var stm = this._sqlSelect(options, entity);
var that = this;
this.db.readTransaction(function( t ) {
var statement = stm.statement || stm;
var args = stm.arguments;
lastStatement = statement;
console.log('sql statement: ' + statement);
if( args ) {
console.log(' arguments: ' + JSON.stringify(args));
}
t.executeSql(statement, args, function( tx, res ) {
var len = res.rows.length;//, i;
for( var i = 0; i < len; i++ ) {
var item = res.rows.item(i);
var attrs;
if( !_.isEmpty(entity.fields) || !that._hasDefaultFields(item) ) {
attrs = item;
} else {
try {
attrs = JSON.parse(item.data);
} catch( e ) {
}
}
if( attrs && (!that._selector || that._selector.matches(attrs)) ) {
if( isCollection ) {
result.push(attrs);
} else {
result = attrs;
break;
}
}
}
}, function (t, e) {
// error
console.error('webSql error: ' + e.message);
});
}, function( sqlError ) { // errorCallback
console.error('WebSql Syntax Error: ' + sqlError.message);
that.handleError(options, sqlError.message, lastStatement);
}, function() { // voidCallback (success)
that.handleSuccess(options, result);
});
}
},
if (attrs && (!that._selector || that._selector.matches(attrs))) {
if (isCollection) {
result.push(attrs);
} else {
result = attrs;
break;
}
}
}
}, function (t, e) {
// error
console.error('webSql error: ' + e.message);
});
}, function (sqlError) { // errorCallback
console.error('WebSql Syntax Error: ' + sqlError.message);
that.handleError(options, sqlError.message, lastStatement);
}, function () { // voidCallback (success)
that.handleSuccess(options, result);
});
}
},
_delete: function( models, options ) {
var entity = this.getEntity(options);
if( this._checkDb(options) && this._checkEntity(options, entity) ) {
options.models = models;
var sql = this._sqlDelete(options, entity);
// reset flag
this._executeTransaction(options, [sql]);
}
},
_delete: function (models, options) {
var entity = this.getEntity(options);
if (this._checkDb(options) && this._checkEntity(options, entity)) {
options.models = models;
var sql = this._sqlDelete(options, entity);
// reset flag
this._executeTransaction(options, [sql]);
}
},
_executeSql: function( options ) {
if( options.sql ) {
this._executeTransaction(options, [options.sql]);
}
},
_executeSql: function (options) {
if (options.sql) {
this._executeTransaction(options, [options.sql]);
}
},
_executeTransaction: function( options, statements ) {
var error;
var lastStatement;
if( this._checkDb(options) ) {
var that = this;
try {
/* transaction has 3 parameters: the transaction callback, the error callback and the success callback */
this.db.transaction(function( t ) {
_.each(statements, function( stm ) {
var statement = stm.statement || stm;
var args = stm.arguments;
lastStatement = statement;
console.log('sql statement: ' + statement);
if( args ) {
console.log(' arguments: ' + JSON.stringify(args));
}
t.executeSql(statement, args);
});
}, function( sqlError ) { // errorCallback
console.error(sqlError.message);
that.handleError(options, sqlError.message, lastStatement);
}, function() {
that.handleSuccess(options);
});
} catch( e ) {
console.error(e.message);
_executeTransaction: function (options, statements) {
var error;
var lastStatement;
if (this._checkDb(options)) {
var that = this;
try {
/* transaction has 3 parameters: the transaction callback, the error callback and the success callback */
this.db.transaction(function (t) {
_.each(statements, function (stm) {
var statement = stm.statement || stm;
var args = stm.arguments;
lastStatement = statement;
console.log('sql statement: ' + statement);
if (args) {
console.log(' arguments: ' + JSON.stringify(args));
}
}
if( error ) {
this.handleCallback(options.error, error, lastStatement);
}
},
t.executeSql(statement, args);
});
}, function (sqlError) { // errorCallback
console.error(sqlError.message);
that.handleError(options, sqlError.message, lastStatement);
}, function () {
that.handleSuccess(options);
});
} catch (e) {
console.error(e.message);
}
}
if (error) {
this.handleCallback(options.error, error, lastStatement);
}
},
_hasDefaultFields: function( item ) {
return _.every(_.keys(item), function( key ) {
return key === this.idField.name || key === this.dataField.name;
}, this);
},
_hasDefaultFields: function (item) {
return _.every(_.keys(item), function (key) {
return key === this.idField.name || key === this.dataField.name;
}, this);
},
_checkDb: function( options ) {
// has to be initialized first
if( !this.db ) {
var error = 'db handler not initialized.';
console.error(error);
this.handleError(options, error);
return false;
}
return true;
},
_checkDb: function (options) {
// has to be initialized first
if (!this.db) {
var error = 'db handler not initialized.';
console.error(error);
this.handleError(options, error);
return false;
}
return true;
},
getFields: function( entity ) {
if( !_.isEmpty(entity.fields) ) {
return entity.fields;
} else {
var fields = {};
fields.data = this.dataField;
var idAttribute = entity.idAttribute || 'id';
fields[idAttribute] = this.idField;
return fields;
}
},
getField: function( entity, key ) {
return this.getFields(entity)[key];
getFields: function (entity) {
if (!_.isEmpty(entity.fields)) {
return entity.fields;
} else {
var fields = {};
fields.data = this.dataField;
var idAttribute = entity.idAttribute || 'id';
fields[idAttribute] = this.idField;
return fields;
}
},
getField: function (entity, key) {
return this.getFields(entity)[key];
}
});

@@ -14,175 +14,175 @@ // Copyright (c) 2013 M-Way Solutions GmbH

/**
* The type of this object.
*
* @type String
*/
type: 'Bikini.Base64',
/**
* The type of this object.
*
* @type String
*/
type: 'Bikini.Base64',
/**
* The key string for the base 64 decoding and encoding.
*
* @type String
*/
_keyStr: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=',
/**
* The key string for the base 64 decoding and encoding.
*
* @type String
*/
_keyStr: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=',
/**
* This method encodes a given binary input, using the base64 encoding.
*
* @param {String} input The binary to be encoded. (e.g. an requested image)
* @returns {String} The base64 encoded string.
*/
encodeBinary: function( input ) {
var output = '';
var bytebuffer;
var encodedCharIndexes = new Array(4);
var inx = 0;
var paddingBytes = 0;
/**
* This method encodes a given binary input, using the base64 encoding.
*
* @param {String} input The binary to be encoded. (e.g. an requested image)
* @returns {String} The base64 encoded string.
*/
encodeBinary: function (input) {
var output = '';
var bytebuffer;
var encodedCharIndexes = new Array(4);
var inx = 0;
var paddingBytes = 0;
while( inx < input.length ) {
// Fill byte buffer array
bytebuffer = new Array(3);
for( var jnx = 0; jnx < bytebuffer.length; jnx++ ) {
if( inx < input.length ) {
bytebuffer[jnx] = input.charCodeAt(inx++) & 0xff;
} // throw away high-order byte, as documented at: https://developer.mozilla.org/En/Using_XMLHttpRequest#Handling_binary_data
else {
bytebuffer[jnx] = 0;
}
}
while (inx < input.length) {
// Fill byte buffer array
bytebuffer = new Array(3);
for (var jnx = 0; jnx < bytebuffer.length; jnx++) {
if (inx < input.length) {
bytebuffer[jnx] = input.charCodeAt(inx++) & 0xff;
} // throw away high-order byte, as documented at: https://developer.mozilla.org/En/Using_XMLHttpRequest#Handling_binary_data
else {
bytebuffer[jnx] = 0;
}
}
// Get each encoded character, 6 bits at a time
// index 1: first 6 bits
encodedCharIndexes[0] = bytebuffer[0] >> 2;
// index 2: second 6 bits (2 least significant bits from input byte 1 + 4 most significant bits from byte 2)
encodedCharIndexes[1] = ((bytebuffer[0] & 0x3) << 4) | (bytebuffer[1] >> 4);
// index 3: third 6 bits (4 least significant bits from input byte 2 + 2 most significant bits from byte 3)
encodedCharIndexes[2] = ((bytebuffer[1] & 0x0f) << 2) | (bytebuffer[2] >> 6);
// index 3: forth 6 bits (6 least significant bits from input byte 3)
encodedCharIndexes[3] = bytebuffer[2] & 0x3f;
// Get each encoded character, 6 bits at a time
// index 1: first 6 bits
encodedCharIndexes[0] = bytebuffer[0] >> 2;
// index 2: second 6 bits (2 least significant bits from input byte 1 + 4 most significant bits from byte 2)
encodedCharIndexes[1] = ((bytebuffer[0] & 0x3) << 4) | (bytebuffer[1] >> 4);
// index 3: third 6 bits (4 least significant bits from input byte 2 + 2 most significant bits from byte 3)
encodedCharIndexes[2] = ((bytebuffer[1] & 0x0f) << 2) | (bytebuffer[2] >> 6);
// index 3: forth 6 bits (6 least significant bits from input byte 3)
encodedCharIndexes[3] = bytebuffer[2] & 0x3f;
// Determine whether padding happened, and adjust accordingly
paddingBytes = inx - (input.length - 1);
switch( paddingBytes ) {
case 2:
// Set last 2 characters to padding char
encodedCharIndexes[3] = 64;
encodedCharIndexes[2] = 64;
break;
case 1:
// Set last character to padding char
encodedCharIndexes[3] = 64;
break;
default:
break; // No padding - proceed
}
// Now we will grab each appropriate character out of our keystring
// based on our index array and append it to the output string
for( jnx = 0; jnx < encodedCharIndexes.length; jnx++ ) {
output += this._keyStr.charAt(encodedCharIndexes[jnx]);
}
}
return output;
},
// Determine whether padding happened, and adjust accordingly
paddingBytes = inx - (input.length - 1);
switch (paddingBytes) {
case 2:
// Set last 2 characters to padding char
encodedCharIndexes[3] = 64;
encodedCharIndexes[2] = 64;
break;
case 1:
// Set last character to padding char
encodedCharIndexes[3] = 64;
break;
default:
break; // No padding - proceed
}
// Now we will grab each appropriate character out of our keystring
// based on our index array and append it to the output string
for (jnx = 0; jnx < encodedCharIndexes.length; jnx++) {
output += this._keyStr.charAt(encodedCharIndexes[jnx]);
}
}
return output;
},
/**
* This method encodes a given input string, using the base64 encoding.
*
* @param {String} input The string to be encoded.
* @returns {String} The base64 encoded string.
*/
encode: function( input ) {
var output = '';
var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
var i = 0;
/**
* This method encodes a given input string, using the base64 encoding.
*
* @param {String} input The string to be encoded.
* @returns {String} The base64 encoded string.
*/
encode: function (input) {
var output = '';
var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
var i = 0;
input = Bikini.Cypher.utf8Encode(input);
input = Bikini.Cypher.utf8Encode(input);
while( i < input.length ) {
chr1 = input.charCodeAt(i++);
chr2 = input.charCodeAt(i++);
chr3 = input.charCodeAt(i++);
while (i < input.length) {
chr1 = input.charCodeAt(i++);
chr2 = input.charCodeAt(i++);
chr3 = input.charCodeAt(i++);
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if( isNaN(chr2) ) {
enc3 = enc4 = 64;
} else if( isNaN(chr3) ) {
enc4 = 64;
}
if (isNaN(chr2)) {
enc3 = enc4 = 64;
} else if (isNaN(chr3)) {
enc4 = 64;
}
output += this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) + this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);
}
output += this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) + this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);
}
return output;
},
return output;
},
binaryEncode: function( input ) {
var output = '';
var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
var i = 0;
binaryEncode: function (input) {
var output = '';
var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
var i = 0;
while( i < input.length ) {
chr1 = input.charCodeAt(i++);
chr2 = input.charCodeAt(i++);
chr3 = input.charCodeAt(i++);
while (i < input.length) {
chr1 = input.charCodeAt(i++);
chr2 = input.charCodeAt(i++);
chr3 = input.charCodeAt(i++);
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if( isNaN(chr2) ) {
enc3 = enc4 = 64;
} else if( isNaN(chr3) ) {
enc4 = 64;
}
if (isNaN(chr2)) {
enc3 = enc4 = 64;
} else if (isNaN(chr3)) {
enc4 = 64;
}
output += this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) + this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);
}
output += this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) + this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);
}
return output;
},
return output;
},
/**
* This method decodes a given input string, using the base64 decoding.
*
* @param {String} input The string to be decoded.
* @returns {String} The base64 decoded string.
*/
decode: function( input ) {
var output = '';
var chr1, chr2, chr3;
var enc1, enc2, enc3, enc4;
var i = 0;
/**
* This method decodes a given input string, using the base64 decoding.
*
* @param {String} input The string to be decoded.
* @returns {String} The base64 decoded string.
*/
decode: function (input) {
var output = '';
var chr1, chr2, chr3;
var enc1, enc2, enc3, enc4;
var i = 0;
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, '');
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, '');
while( i < input.length ) {
enc1 = this._keyStr.indexOf(input.charAt(i++));
enc2 = this._keyStr.indexOf(input.charAt(i++));
enc3 = this._keyStr.indexOf(input.charAt(i++));
enc4 = this._keyStr.indexOf(input.charAt(i++));
while (i < input.length) {
enc1 = this._keyStr.indexOf(input.charAt(i++));
enc2 = this._keyStr.indexOf(input.charAt(i++));
enc3 = this._keyStr.indexOf(input.charAt(i++));
enc4 = this._keyStr.indexOf(input.charAt(i++));
chr1 = (enc1 << 2) | (enc2 >> 4);
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
chr3 = ((enc3 & 3) << 6) | enc4;
chr1 = (enc1 << 2) | (enc2 >> 4);
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
chr3 = ((enc3 & 3) << 6) | enc4;
output = output + String.fromCharCode(chr1);
output = output + String.fromCharCode(chr1);
if( enc3 !== 64 ) {
output = output + String.fromCharCode(chr2);
}
if (enc3 !== 64) {
output = output + String.fromCharCode(chr2);
}
if( enc4 !== 64 ) {
output = output + String.fromCharCode(chr3);
}
}
if (enc4 !== 64) {
output = output + String.fromCharCode(chr3);
}
}
return Bikini.Cypher.utf8Decode(output);
}
return Bikini.Cypher.utf8Decode(output);
}
});
});

@@ -13,156 +13,156 @@ // Copyright (c) 2013 M-Way Solutions GmbH

/**
* The type of this object.
*
* @type String
*/
type: 'Bikini.Cypher',
/**
* The type of this object.
*
* @type String
*/
type: 'Bikini.Cypher',
/**
* The default decoder.
*
* @type Bikini.Base64
*/
defaultDecoder: Bikini.Base64,
/**
* The default decoder.
*
* @type Bikini.Base64
*/
defaultDecoder: Bikini.Base64,
/**
* The default encoder.
*
* @type Bikini.Base64
*/
/**
* The default encoder.
*
* @type Bikini.Base64
*/
defaultEncoder: Bikini.Base64,
defaultEncoder: Bikini.Base64,
/**
* The default hash algorithm.
*
* @type Bikini.SHA256
*/
/**
* The default hash algorithm.
*
* @type Bikini.SHA256
*/
defaultHasher: Bikini.SHA256,
defaultHasher: Bikini.SHA256,
/**
* This method is the one that initiates the decoding of a given string, based on either
* the default decoder or a custom decoder.
*
* @param {String} input The input string to be decoded.
* @param {Object} algorithm The algorithm object containing a decode method.
* @returns {String} The decoded string.
*/
decode: function( input, algorithm ) {
/**
* This method is the one that initiates the decoding of a given string, based on either
* the default decoder or a custom decoder.
*
* @param {String} input The input string to be decoded.
* @param {Object} algorithm The algorithm object containing a decode method.
* @returns {String} The decoded string.
*/
decode: function (input, algorithm) {
if( algorithm && algorithm.decode ) {
return algorithm.decode(input);
} else {
return this.defaultDecoder.decode(input);
}
if (algorithm && algorithm.decode) {
return algorithm.decode(input);
} else {
return this.defaultDecoder.decode(input);
}
},
},
/**
* This method is the one that initiates the encoding of a given string, based on either
* the default encoder or a custom encoder.
*
* @param {String} input The input string to be decoded.
* @param {Object} algorithm The algorithm object containing a encode method.
* @returns {String} The encoded string.
*/
encode: function( input, algorithm ) {
/**
* This method is the one that initiates the encoding of a given string, based on either
* the default encoder or a custom encoder.
*
* @param {String} input The input string to be decoded.
* @param {Object} algorithm The algorithm object containing a encode method.
* @returns {String} The encoded string.
*/
encode: function (input, algorithm) {
if( algorithm && algorithm.encode ) {
return algorithm.encode(input);
} else {
return this.defaultEncoder.encode(input);
}
if (algorithm && algorithm.encode) {
return algorithm.encode(input);
} else {
return this.defaultEncoder.encode(input);
}
},
},
/**
* This method is the one that initiates the hashing of a given string, based on either
* the default hashing algorithm or a custom hashing algorithm.
*
* @param {String} input The input string to be hashed.
* @param {Object} algorithm The algorithm object containing a hash method.
* @returns {String} The hashed string.
*/
hash: function( input, algorithm ) {
/**
* This method is the one that initiates the hashing of a given string, based on either
* the default hashing algorithm or a custom hashing algorithm.
*
* @param {String} input The input string to be hashed.
* @param {Object} algorithm The algorithm object containing a hash method.
* @returns {String} The hashed string.
*/
hash: function (input, algorithm) {
if( algorithm && algorithm.hash ) {
return algorithm.hash(input);
} else {
return this.defaultHasher.hash(input);
}
if (algorithm && algorithm.hash) {
return algorithm.hash(input);
} else {
return this.defaultHasher.hash(input);
}
},
},
/**
* Private method for UTF-8 encoding
*
* @private
* @param {String} string The string to be encoded.
* @returns {String} The utf8 encoded string.
*/
utf8Encode: function( string ) {
string = string.replace(/\r\n/g, '\n');
var utf8String = '';
/**
* Private method for UTF-8 encoding
*
* @private
* @param {String} string The string to be encoded.
* @returns {String} The utf8 encoded string.
*/
utf8Encode: function (string) {
string = string.replace(/\r\n/g, '\n');
var utf8String = '';
for( var n = 0; n < string.length; n++ ) {
for (var n = 0; n < string.length; n++) {
var c = string.charCodeAt(n);
var c = string.charCodeAt(n);
if( c < 128 ) {
utf8String += String.fromCharCode(c);
} else if( (c > 127) && (c < 2048) ) {
utf8String += String.fromCharCode((c >> 6) | 192);
utf8String += String.fromCharCode((c & 63) | 128);
} else {
utf8String += String.fromCharCode((c >> 12) | 224);
utf8String += String.fromCharCode(((c >> 6) & 63) | 128);
utf8String += String.fromCharCode((c & 63) | 128);
}
if (c < 128) {
utf8String += String.fromCharCode(c);
} else if ((c > 127) && (c < 2048)) {
utf8String += String.fromCharCode((c >> 6) | 192);
utf8String += String.fromCharCode((c & 63) | 128);
} else {
utf8String += String.fromCharCode((c >> 12) | 224);
utf8String += String.fromCharCode(((c >> 6) & 63) | 128);
utf8String += String.fromCharCode((c & 63) | 128);
}
}
}
return utf8String;
},
return utf8String;
},
/**
* Private method for UTF-8 decoding
*
* @private
* @param {String} string The string to be decoded.
* @returns {String} The utf8 decoded string.
*/
utf8Decode: function( utf8String ) {
var string = '';
var i;
var c;
var c1;
var c2;
var c3;
i = c = c1 = c2 = 0;
/**
* Private method for UTF-8 decoding
*
* @private
* @param {String} string The string to be decoded.
* @returns {String} The utf8 decoded string.
*/
utf8Decode: function (utf8String) {
var string = '';
var i;
var c;
var c1;
var c2;
var c3;
i = c = c1 = c2 = 0;
while( i < utf8String.length ) {
while (i < utf8String.length) {
c = utf8String.charCodeAt(i);
c = utf8String.charCodeAt(i);
if( c < 128 ) {
string += String.fromCharCode(c);
i++;
} else if( (c > 191) && (c < 224) ) {
c2 = utf8String.charCodeAt(i + 1);
string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
i += 2;
} else {
c2 = utf8String.charCodeAt(i + 1);
c3 = utf8String.charCodeAt(i + 2);
string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
i += 3;
}
if (c < 128) {
string += String.fromCharCode(c);
i++;
} else if ((c > 191) && (c < 224)) {
c2 = utf8String.charCodeAt(i + 1);
string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
i += 2;
} else {
c2 = utf8String.charCodeAt(i + 1);
c3 = utf8String.charCodeAt(i + 2);
string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
i += 3;
}
}
}
return string;
}
return string;
}
});
});

@@ -12,12 +12,12 @@ // Copyright (c) 2013 M-Way Solutions GmbH

/**
* This method is used to create a new instance of Bikini.Date based on the data
* library moment.js.
*
* @returns {Object}
*/
create: function() {
var m = moment.apply(this, arguments);
return _.extend(m, this);
}
/**
* This method is used to create a new instance of Bikini.Date based on the data
* library moment.js.
*
* @returns {Object}
*/
create: function () {
var m = moment.apply(this, arguments);
return _.extend(m, this);
}
};

@@ -19,11 +19,11 @@ // Copyright (c) 2013 M-Way Solutions GmbH

*/
Bikini.ObjectID = function( hexString ) {
Bikini.ObjectID.counter = Bikini.ObjectID.counter || parseInt(Math.random() * Math.pow(16, 6));
Bikini.ObjectID.machineId = Bikini.ObjectID.machineId || parseInt(Math.random() * Math.pow(16, 6));
Bikini.ObjectID.processId = Bikini.ObjectID.processId || parseInt(Math.random() * Math.pow(16, 4));
this._ObjectID(hexString);
Bikini.ObjectID = function (hexString) {
Bikini.ObjectID.counter = Bikini.ObjectID.counter || parseInt(Math.random() * Math.pow(16, 6));
Bikini.ObjectID.machineId = Bikini.ObjectID.machineId || parseInt(Math.random() * Math.pow(16, 6));
Bikini.ObjectID.processId = Bikini.ObjectID.processId || parseInt(Math.random() * Math.pow(16, 4));
this._ObjectID(hexString);
};
Bikini.ObjectID._looksLikeObjectID = function( str ) {
return str.length === 24 && str.match(/^[0-9a-f]*$/);
Bikini.ObjectID._looksLikeObjectID = function (str) {
return str.length === 24 && str.match(/^[0-9a-f]*$/);
};

@@ -33,130 +33,130 @@

_str: '',
_str: '',
_ObjectID: function( hexString ) {
//random-based impl of Mongo ObjectID
if( hexString ) {
hexString = hexString.toLowerCase();
if( !Bikini.ObjectID._looksLikeObjectID(hexString) ) {
throw new Error('Invalid hexadecimal string for creating an ObjectID');
}
// meant to work with _.isEqual(), which relies on structural equality
this._str = hexString;
} else {
_ObjectID: function (hexString) {
//random-based impl of Mongo ObjectID
if (hexString) {
hexString = hexString.toLowerCase();
if (!Bikini.ObjectID._looksLikeObjectID(hexString)) {
throw new Error('Invalid hexadecimal string for creating an ObjectID');
}
// meant to work with _.isEqual(), which relies on structural equality
this._str = hexString;
} else {
this._str =
this._hexString(8, new Date().getTime()/1000) + // a 4-byte value from the Unix timestamp
this._hexString(6, Bikini.ObjectID.machineId) + // a 3-byte machine identifier
this._hexString(4, Bikini.ObjectID.processId) + // a 2-byte process identifier
this._hexString(6, Bikini.ObjectID.counter++); // a 3-byte counter, starting with a random value.
}
return this._str;
},
this._str =
this._hexString(8, new Date().getTime() / 1000) + // a 4-byte value from the Unix timestamp
this._hexString(6, Bikini.ObjectID.machineId) + // a 3-byte machine identifier
this._hexString(4, Bikini.ObjectID.processId) + // a 2-byte process identifier
this._hexString(6, Bikini.ObjectID.counter++); // a 3-byte counter, starting with a random value.
}
return this._str;
},
_hexString: function(len, num) {
num = num || parseInt(Math.random() * Math.pow(16,len));
var str = num.toString(16);
while(str.length < len) {
str = '0'+str;
}
return str.substr(0, len);
},
_hexString: function (len, num) {
num = num || parseInt(Math.random() * Math.pow(16, len));
var str = num.toString(16);
while (str.length < len) {
str = '0' + str;
}
return str.substr(0, len);
},
toString: function() {
return 'ObjectID(\'' + this._str + '\')';
},
toString: function () {
return 'ObjectID(\'' + this._str + '\')';
},
equals: function( other ) {
return other instanceof this._ObjectID && this.valueOf() === other.valueOf();
},
equals: function (other) {
return other instanceof this._ObjectID && this.valueOf() === other.valueOf();
},
clone: function() {
return new Bikini.ObjectID(this._str);
},
clone: function () {
return new Bikini.ObjectID(this._str);
},
typeName: function() {
return 'oid';
},
typeName: function () {
return 'oid';
},
getTimestamp: function() {
return parseInt(this._str.substr(0, 8), 16)*1000;
},
getTimestamp: function () {
return parseInt(this._str.substr(0, 8), 16) * 1000;
},
getMachineId: function() {
return parseInt(this._str.substr(8, 6), 16);
},
getMachineId: function () {
return parseInt(this._str.substr(8, 6), 16);
},
getProcessId: function() {
return parseInt(this._str.substr(14, 4), 16);
},
getProcessId: function () {
return parseInt(this._str.substr(14, 4), 16);
},
getCounter: function() {
return parseInt(this._str.substr(18, 6), 16);
},
getCounter: function () {
return parseInt(this._str.substr(18, 6), 16);
},
valueOf: function() {
return this._str;
},
valueOf: function () {
return this._str;
},
toJSON: function() {
return this._str;
},
toJSON: function () {
return this._str;
},
toHexString: function() {
return this._str;
},
toHexString: function () {
return this._str;
},
// Is this selector just shorthand for lookup by _id?
_selectorIsId: function( selector ) {
return (typeof selector === 'string') ||
(typeof selector === 'number') ||
selector instanceof Bikini.ObjectId;
},
// Is this selector just shorthand for lookup by _id?
_selectorIsId: function (selector) {
return (typeof selector === 'string') ||
(typeof selector === 'number') ||
selector instanceof Bikini.ObjectId;
},
// Is the selector just lookup by _id (shorthand or not)?
_selectorIsIdPerhapsAsObject: function( selector ) {
return this._selectorIsId(selector) || (selector && typeof selector === 'object' && selector._id && this._selectorIsId(selector._id) && _.size(selector) === 1);
},
// Is the selector just lookup by _id (shorthand or not)?
_selectorIsIdPerhapsAsObject: function (selector) {
return this._selectorIsId(selector) || (selector && typeof selector === 'object' && selector._id && this._selectorIsId(selector._id) && _.size(selector) === 1);
},
// If this is a selector which explicitly constrains the match by ID to a finite
// number of documents, returns a list of their IDs. Otherwise returns
// null. Note that the selector may have other restrictions so it may not even
// match those document! We care about $in and $and since those are generated
// access-controlled update and remove.
_idsMatchedBySelector: function( selector ) {
// Is the selector just an ID?
if( this._selectorIsId(selector) ) {
return [selector];
}
if( !selector ) {
return null;
}
// If this is a selector which explicitly constrains the match by ID to a finite
// number of documents, returns a list of their IDs. Otherwise returns
// null. Note that the selector may have other restrictions so it may not even
// match those document! We care about $in and $and since those are generated
// access-controlled update and remove.
_idsMatchedBySelector: function (selector) {
// Is the selector just an ID?
if (this._selectorIsId(selector)) {
return [selector];
}
if (!selector) {
return null;
}
// Do we have an _id clause?
if( _.has(selector, '_id') ) {
// Is the _id clause just an ID?
if( this._selectorIsId(selector._id) ) {
return [selector._id];
}
// Is the _id clause {_id: {$in: ["x", "y", "z"]}}?
if( selector._id && selector._id.$in && _.isArray(selector._id.$in) && !_.isEmpty(selector._id.$in) && _.all(selector._id.$in, this._selectorIsId) ) {
return selector._id.$in;
}
return null;
}
// Do we have an _id clause?
if (_.has(selector, '_id')) {
// Is the _id clause just an ID?
if (this._selectorIsId(selector._id)) {
return [selector._id];
}
// Is the _id clause {_id: {$in: ["x", "y", "z"]}}?
if (selector._id && selector._id.$in && _.isArray(selector._id.$in) && !_.isEmpty(selector._id.$in) && _.all(selector._id.$in, this._selectorIsId)) {
return selector._id.$in;
}
return null;
}
// If this is a top-level $and, and any of the clauses constrain their
// documents, then the whole selector is constrained by any one clause's
// constraint. (Well, by their intersection, but that seems unlikely.)
if( selector.$and && _.isArray(selector.$and) ) {
for( var i = 0; i < selector.$and.length; ++i ) {
var subIds = this._idsMatchedBySelector(selector.$and[i]);
if( subIds ) {
return subIds;
}
}
// If this is a top-level $and, and any of the clauses constrain their
// documents, then the whole selector is constrained by any one clause's
// constraint. (Well, by their intersection, but that seems unlikely.)
if (selector.$and && _.isArray(selector.$and)) {
for (var i = 0; i < selector.$and.length; ++i) {
var subIds = this._idsMatchedBySelector(selector.$and[i]);
if (subIds) {
return subIds;
}
}
}
return null;
}
});
return null;
}
});

@@ -14,179 +14,179 @@ // Copyright (c) 2013 M-Way Solutions GmbH

/**
* The type of this object.
*
* @type String
*/
type: 'Bikini.SHA256',
/**
* The type of this object.
*
* @type String
*/
type: 'Bikini.SHA256',
/**
* Defines the bits per input character: 8 - ASCII, 16 - Unicode
*
* @type Number
*/
chrsz: 8,
/**
* Defines the bits per input character: 8 - ASCII, 16 - Unicode
*
* @type Number
*/
chrsz: 8,
/**
* Defines the hex output format: 0 - lowercase, 1 - uppercase
*
* @type Number
*/
hexcase: 0,
/**
* Defines the hex output format: 0 - lowercase, 1 - uppercase
*
* @type Number
*/
hexcase: 0,
/**
* This method is called from the 'outside world', controls the hashing and
* finally returns the hash value.
*
* @param {String} input The input string to be hashed.
* @returns {String} The sha256 hashed string.
*/
hash: function( input ) {
input = Bikini.Cypher.utf8Encode(input);
return this.binb2hex(this.coreSha256(this.str2binb(input), input.length * this.chrsz));
},
/**
* This method is called from the 'outside world', controls the hashing and
* finally returns the hash value.
*
* @param {String} input The input string to be hashed.
* @returns {String} The sha256 hashed string.
*/
hash: function (input) {
input = Bikini.Cypher.utf8Encode(input);
return this.binb2hex(this.coreSha256(this.str2binb(input), input.length * this.chrsz));
},
/**
* @private
*/
safeAdd: function( x, y ) {
var lsw = (x & 0xFFFF) + (y & 0xFFFF);
var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
return (msw << 16) | (lsw & 0xFFFF);
},
/**
* @private
*/
safeAdd: function (x, y) {
var lsw = (x & 0xFFFF) + (y & 0xFFFF);
var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
return (msw << 16) | (lsw & 0xFFFF);
},
/**
* @private
*/
S: function( X, n ) {
return ( X >>> n ) | (X << (32 - n));
},
/**
* @private
*/
S: function (X, n) {
return ( X >>> n ) | (X << (32 - n));
},
/**
* @private
*/
R: function( X, n ) {
return ( X >>> n );
},
/**
* @private
*/
R: function (X, n) {
return ( X >>> n );
},
/**
* @private
*/
Ch: function( x, y, z ) {
return ((x & y) ^ ((~x) & z));
},
/**
* @private
*/
Ch: function (x, y, z) {
return ((x & y) ^ ((~x) & z));
},
/**
* @private
*/
Maj: function( x, y, z ) {
return ((x & y) ^ (x & z) ^ (y & z));
},
/**
* @private
*/
Maj: function (x, y, z) {
return ((x & y) ^ (x & z) ^ (y & z));
},
/**
* @private
*/
Sigma0256: function( x ) {
return (this.S(x, 2) ^ this.S(x, 13) ^ this.S(x, 22));
},
/**
* @private
*/
Sigma0256: function (x) {
return (this.S(x, 2) ^ this.S(x, 13) ^ this.S(x, 22));
},
/**
* @private
*/
Sigma1256: function( x ) {
return (this.S(x, 6) ^ this.S(x, 11) ^ this.S(x, 25));
},
/**
* @private
*/
Sigma1256: function (x) {
return (this.S(x, 6) ^ this.S(x, 11) ^ this.S(x, 25));
},
/**
* @private
*/
Gamma0256: function( x ) {
return (this.S(x, 7) ^ this.S(x, 18) ^ this.R(x, 3));
},
/**
* @private
*/
Gamma0256: function (x) {
return (this.S(x, 7) ^ this.S(x, 18) ^ this.R(x, 3));
},
/**
* @private
*/
Gamma1256: function( x ) {
return (this.S(x, 17) ^ this.S(x, 19) ^ this.R(x, 10));
},
/**
* @private
*/
Gamma1256: function (x) {
return (this.S(x, 17) ^ this.S(x, 19) ^ this.R(x, 10));
},
/**
* @private
*/
coreSha256: function( m, l ) {
var K = new Array(0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, 0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5, 0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3, 0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174, 0xE49B69C1, 0xEFBE4786, 0xFC19DC6, 0x240CA1CC, 0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA, 0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7, 0xC6E00BF3, 0xD5A79147, 0x6CA6351, 0x14292967, 0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13, 0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85, 0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3, 0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070, 0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5, 0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3, 0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208, 0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2);
var HASH = new Array(0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19);
var W = new Array(64);
var a, b, c, d, e, f, g, h, i, j;
var T1, T2;
/**
* @private
*/
coreSha256: function (m, l) {
var K = new Array(0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, 0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5, 0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3, 0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174, 0xE49B69C1, 0xEFBE4786, 0xFC19DC6, 0x240CA1CC, 0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA, 0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7, 0xC6E00BF3, 0xD5A79147, 0x6CA6351, 0x14292967, 0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13, 0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85, 0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3, 0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070, 0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5, 0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3, 0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208, 0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2);
var HASH = new Array(0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19);
var W = new Array(64);
var a, b, c, d, e, f, g, h, i, j;
var T1, T2;
m[l >> 5] |= 0x80 << (24 - l % 32);
m[((l + 64 >> 9) << 4) + 15] = l;
m[l >> 5] |= 0x80 << (24 - l % 32);
m[((l + 64 >> 9) << 4) + 15] = l;
for( i = 0; i < m.length; i += 16 ) {
a = HASH[0];
b = HASH[1];
c = HASH[2];
d = HASH[3];
e = HASH[4];
f = HASH[5];
g = HASH[6];
h = HASH[7];
for (i = 0; i < m.length; i += 16) {
a = HASH[0];
b = HASH[1];
c = HASH[2];
d = HASH[3];
e = HASH[4];
f = HASH[5];
g = HASH[6];
h = HASH[7];
for( j = 0; j < 64; j++ ) {
if( j < 16 ) {
W[j] = m[j + i];
} else {
W[j] = this.safeAdd(this.safeAdd(this.safeAdd(this.Gamma1256(W[j - 2]), W[j - 7]), this.Gamma0256(W[j - 15])), W[j - 16]);
}
for (j = 0; j < 64; j++) {
if (j < 16) {
W[j] = m[j + i];
} else {
W[j] = this.safeAdd(this.safeAdd(this.safeAdd(this.Gamma1256(W[j - 2]), W[j - 7]), this.Gamma0256(W[j - 15])), W[j - 16]);
}
T1 = this.safeAdd(this.safeAdd(this.safeAdd(this.safeAdd(h, this.Sigma1256(e)), this.Ch(e, f, g)), K[j]), W[j]);
T2 = this.safeAdd(this.Sigma0256(a), this.Maj(a, b, c));
T1 = this.safeAdd(this.safeAdd(this.safeAdd(this.safeAdd(h, this.Sigma1256(e)), this.Ch(e, f, g)), K[j]), W[j]);
T2 = this.safeAdd(this.Sigma0256(a), this.Maj(a, b, c));
h = g;
g = f;
f = e;
e = this.safeAdd(d, T1);
d = c;
c = b;
b = a;
a = this.safeAdd(T1, T2);
}
h = g;
g = f;
f = e;
e = this.safeAdd(d, T1);
d = c;
c = b;
b = a;
a = this.safeAdd(T1, T2);
}
HASH[0] = this.safeAdd(a, HASH[0]);
HASH[1] = this.safeAdd(b, HASH[1]);
HASH[2] = this.safeAdd(c, HASH[2]);
HASH[3] = this.safeAdd(d, HASH[3]);
HASH[4] = this.safeAdd(e, HASH[4]);
HASH[5] = this.safeAdd(f, HASH[5]);
HASH[6] = this.safeAdd(g, HASH[6]);
HASH[7] = this.safeAdd(h, HASH[7]);
}
return HASH;
},
HASH[0] = this.safeAdd(a, HASH[0]);
HASH[1] = this.safeAdd(b, HASH[1]);
HASH[2] = this.safeAdd(c, HASH[2]);
HASH[3] = this.safeAdd(d, HASH[3]);
HASH[4] = this.safeAdd(e, HASH[4]);
HASH[5] = this.safeAdd(f, HASH[5]);
HASH[6] = this.safeAdd(g, HASH[6]);
HASH[7] = this.safeAdd(h, HASH[7]);
}
return HASH;
},
/**
* @private
*/
str2binb: function( str ) {
var bin = [];
var mask = (1 << this.chrsz) - 1;
for( var i = 0; i < str.length * this.chrsz; i += this.chrsz ) {
bin[i >> 5] |= (str.charCodeAt(i / this.chrsz) & mask) << (24 - i % 32);
}
return bin;
},
/**
* @private
*/
str2binb: function (str) {
var bin = [];
var mask = (1 << this.chrsz) - 1;
for (var i = 0; i < str.length * this.chrsz; i += this.chrsz) {
bin[i >> 5] |= (str.charCodeAt(i / this.chrsz) & mask) << (24 - i % 32);
}
return bin;
},
/**
* @private
*/
binb2hex: function( binarray ) {
var hexTab = this.hexcase ? '0123456789ABCDEF' : '0123456789abcdef';
var str = '';
for( var i = 0; i < binarray.length * 4; i++ ) {
str += hexTab.charAt((binarray[i >> 2] >> ((3 - i % 4) * 8 + 4)) & 0xF) + hexTab.charAt((binarray[i >> 2] >> ((3 - i % 4) * 8 )) & 0xF);
}
return str;
}
/**
* @private
*/
binb2hex: function (binarray) {
var hexTab = this.hexcase ? '0123456789ABCDEF' : '0123456789abcdef';
var str = '';
for (var i = 0; i < binarray.length * 4; i++) {
str += hexTab.charAt((binarray[i >> 2] >> ((3 - i % 4) * 8 + 4)) & 0xF) + hexTab.charAt((binarray[i >> 2] >> ((3 - i % 4) * 8 )) & 0xF);
}
return str;
}
});
});

@@ -14,33 +14,33 @@ // Copyright (c) 2013 M-Way Solutions GmbH

Bikini.UniqueId = Bikini.Object.design({
uuid: function(len, radix) {
// based on Robert Kieffer's randomUUID.js at http://www.broofa.com
var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');
var uuid = [];
//len = len ? len : 32;
radix = radix || chars.length;
var i;
uuid: function (len, radix) {
// based on Robert Kieffer's randomUUID.js at http://www.broofa.com
var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');
var uuid = [];
//len = len ? len : 32;
radix = radix || chars.length;
var i;
if (len) {
for (i = 0; i < len; i++) {
uuid[i] = chars[0 | Math.random() * radix];
}
} else {
// rfc4122, version 4 form
var r;
if (len) {
for (i = 0; i < len; i++) {
uuid[i] = chars[0 | Math.random() * radix];
}
} else {
// rfc4122, version 4 form
var r;
// rfc4122 requires these characters
uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';
uuid[14] = '4';
// rfc4122 requires these characters
uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';
uuid[14] = '4';
// Fill in random data. At i==19 set the high bits of clock sequence as
// per rfc4122, sec. 4.1.5
for (i = 0; i < 36; i++) {
if (!uuid[i]) {
r = 0 | Math.random() * 16;
uuid[i] = chars[(i === 19) ? (r & 0x3) | 0x8 : r];
}
}
// Fill in random data. At i==19 set the high bits of clock sequence as
// per rfc4122, sec. 4.1.5
for (i = 0; i < 36; i++) {
if (!uuid[i]) {
r = 0 | Math.random() * 16;
uuid[i] = chars[(i === 19) ? (r & 0x3) | 0x8 : r];
}
return uuid.join('');
}
}
});
return uuid.join('');
}
});

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display