/*
---
name: BehaviorAPI
description: HTML getters for Behavior's API model.
requires: [Core/Class, /Element.Data]
provides: [BehaviorAPI]
...
*/
(function(){
//see Docs/BehaviorAPI.md for documentation of public methods.
var reggy = /[^a-z0-9\-]/gi,
dots = /\./g;
window.BehaviorAPI = new Class({
element: null,
prefix: '',
defaults: {},
initialize: function(element, prefix){
this.element = element;
this.prefix = prefix.toLowerCase().replace(dots, '-').replace(reggy, '');
},
/******************
* PUBLIC METHODS
******************/
get: function(/* name[, name, name, etc] */){
if (arguments.length > 1) return this._getObj(Array.from(arguments));
return this._getValue(arguments[0]);
},
getAs: function(/*returnType, name, defaultValue OR {name: returnType, name: returnType, etc}*/){
if (typeOf(arguments[0]) == 'object') return this._getValuesAs.apply(this, arguments);
return this._getValueAs.apply(this, arguments);
},
require: function(/* name[, name, name, etc] */){
for (var i = 0; i < arguments.length; i++){
if (this._getValue(arguments[i]) == undefined) throw new Error('Could not retrieve ' + this.prefix + '-' + arguments[i] + ' option from element.');
}
return this;
},
requireAs: function(returnType, name /* OR {name: returnType, name: returnType, etc}*/){
var val;
if (typeOf(arguments[0]) == 'object'){
for (var objName in arguments[0]){
val = this._getValueAs(arguments[0][objName], objName);
if (val === undefined || val === null) throw new Error("Could not retrieve " + this.prefix + '-' + objName + " option from element.");
}
} else {
val = this._getValueAs(returnType, name);
if (val === undefined || val === null) throw new Error("Could not retrieve " + this.prefix + '-' + name + " option from element.");
}
return this;
},
setDefault: function(name, value /* OR {name: value, name: value, etc }*/){
if (typeOf(arguments[0]) == 'object'){
for (var objName in arguments[0]){
this.setDefault(objName, arguments[0][objName]);
}
return this;
}
name = name.camelCase();
switch (typeOf(value)){
case 'object': value = Object.clone(value); break;
case 'array': value = Array.clone(value); break;
case 'hash': value = new Hash(value); break;
}
this.defaults[name] = value;
var setValue = this._getValue(name);
var options = this._getOptions();
if (setValue == null){
options[name] = value;
} else if (typeOf(setValue) == 'object' && typeOf(value) == 'object') {
options[name] = Object.merge({}, value, setValue);
}
return this;
},
refreshAPI: function(){
delete this.options;
this.setDefault(this.defaults);
return;
},
/******************
* PRIVATE METHODS
******************/
//given an array of names, returns an object of key/value pairs for each name
_getObj: function(names){
var obj = {};
names.each(function(name){
var value = this._getValue(name);
if (value !== undefined) obj[name] = value;
}, this);
return obj;
},
//gets the data-behaviorname-options object and parses it as JSON
_getOptions: function(){
try {
if (!this.options){
var options = this.element.getData(this.prefix + '-options', '{}').trim();
if (options === "") return this.options = {};
if (options && options.substring(0,1) != '{') options = '{' + options + '}';
var isSecure = JSON.isSecure(options);
if (!isSecure) throw new Error('warning, options value for element is not parsable, check your JSON format for quotes, etc.');
this.options = isSecure ? JSON.decode(options, false) : {};
for (option in this.options) {
this.options[option.camelCase()] = this.options[option];
}
}
} catch (e){
throw new Error('Could not get options from element; check your syntax. ' + this.prefix + '-options: "' + this.element.getData(this.prefix + '-options', '{}') + '"');
}
return this.options;
},
//given a name (string) returns the value for it
_getValue: function(name){
name = name.camelCase();
var options = this._getOptions();
if (!options.hasOwnProperty(name)){
var inline = this.element.getData(this.prefix + '-' + name.hyphenate());
if (inline) options[name] = inline;
}
return options[name];
},
//given a Type and a name (string) returns the value for it coerced to that type if possible
//else returns the defaultValue or null
_getValueAs: function(returnType, name, defaultValue){
var value = this._getValue(name);
if (value == null || value == undefined) return defaultValue;
var coerced = this._coerceFromString(returnType, value);
if (coerced == null) throw new Error("Could not retrieve value '" + name + "' as the specified type. Its value is: " + value);
return coerced;
},
//given an object of name/Type pairs, returns those as an object of name/value (as specified Type) pairs
_getValuesAs: function(obj){
var returnObj = {};
for (var name in obj){
returnObj[name] = this._getValueAs(obj[name], name);
}
return returnObj;
},
//attempts to run a value through the JSON parser. If the result is not of that type returns null.
_coerceFromString: function(toType, value){
if (typeOf(value) == 'string' && toType != String){
if (JSON.isSecure(value)) value = JSON.decode(value, false);
}
if (instanceOf(value, toType)) return value;
return null;
}
});
})();